1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-22 22:59:43 +02:00
documize/domain/auth/endpoint.go
Harvey Kandola a982af6e79 permission screen UI overflow fix
1. handle long username overflow on space permissions screen
2. only show document history to editors
3. removed redundant document editing permission check
4. Ensure subdomain is detected when accepting space invitation
2017-08-31 18:01:07 +01:00

216 lines
5.9 KiB
Go

// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package auth
import (
"database/sql"
"errors"
"net/http"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
"github.com/documize/community/domain"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/section/provider"
"github.com/documize/community/domain/user"
"github.com/documize/community/model/auth"
"github.com/documize/community/model/org"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *domain.Store
}
// Login user based up HTTP Authorization header.
// An encrypted authentication token is issued with an expiry date.
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
method := "auth.Login"
ctx := domain.GetRequestContext(r)
// check for http header
authHeader := r.Header.Get("Authorization")
if len(authHeader) == 0 {
response.WriteBadRequestError(w, method, "Missing Authorization header")
return
}
// decode what we received
data := strings.Replace(authHeader, "Basic ", "", 1)
decodedBytes, err := secrets.DecodeBase64([]byte(data))
if err != nil {
response.WriteBadRequestError(w, method, "Unable to decode authentication token")
h.Runtime.Log.Error("decode auth header", err)
return
}
decoded := string(decodedBytes)
// check that we have domain:email:password (but allow for : in password field!)
credentials := strings.SplitN(decoded, ":", 3)
if len(credentials) != 3 {
response.WriteBadRequestError(w, method, "Bad authentication token, expecting domain:email:password")
h.Runtime.Log.Error("bad auth token", err)
return
}
dom := strings.TrimSpace(strings.ToLower(credentials[0]))
email := strings.TrimSpace(strings.ToLower(credentials[1]))
password := credentials[2]
dom = h.Store.Organization.CheckDomain(ctx, dom) // TODO optimize by removing this once js allows empty domains
h.Runtime.Log.Info("logon attempt " + email + " @ " + dom)
u, err := h.Store.User.GetByDomain(ctx, dom, email)
if err == sql.ErrNoRows {
response.WriteUnauthorizedError(w)
return
}
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(u.Reset) > 0 || len(u.Password) == 0 {
response.WriteUnauthorizedError(w)
return
}
// Password correct and active user
if email != strings.TrimSpace(strings.ToLower(u.Email)) || !secrets.MatchPassword(u.Password, password, u.Salt) {
response.WriteUnauthorizedError(w)
return
}
org, err := h.Store.Organization.GetOrganizationByDomain(dom)
if err != nil {
response.WriteUnauthorizedError(w)
h.Runtime.Log.Error("bad auth organization", err)
return
}
// Attach user accounts and work out permissions
user.AttachUserAccounts(ctx, *h.Store, org.RefID, &u)
if len(u.Accounts) == 0 {
response.WriteUnauthorizedError(w)
h.Runtime.Log.Error("bad auth accounts", err)
return
}
h.Runtime.Log.Info("login " + email + " @ " + dom)
authModel := auth.AuthenticationModel{}
authModel.Token = GenerateJWT(h.Runtime, u.RefID, org.RefID, dom)
authModel.User = u
response.WriteJSON(w, authModel)
}
// ValidateToken finds and validates authentication token.
func (h *Handler) ValidateToken(w http.ResponseWriter, r *http.Request) {
// TODO should this go after token validation?
if s := r.URL.Query().Get("section"); s != "" {
if err := provider.Callback(s, h.Runtime, h.Store, w, r); err != nil {
w.WriteHeader(http.StatusUnauthorized)
h.Runtime.Log.Error("section validation failure", err)
}
return
}
token := FindJWT(r)
rc, _, tokenErr := DecodeJWT(h.Runtime, token)
var org = org.Organization{}
var err = errors.New("")
// We always grab the org record regardless of token status.
// Why? If bad token we might be OK to alow anonymous access
// depending upon the domain in question.
if len(rc.OrgID) == 0 {
dom := organization.GetRequestSubdomain(r)
org, err = h.Store.Organization.GetOrganizationByDomain(dom)
} else {
org, err = h.Store.Organization.GetOrganization(rc, rc.OrgID)
}
rc.Subdomain = org.Domain
// Inability to find org record spells the end of this request.
if err != nil {
response.WriteUnauthorizedError(w)
return
}
// If we have bad auth token and the domain does not allow anon access
if !org.AllowAnonymousAccess && tokenErr != nil {
response.WriteUnauthorizedError(w)
return
}
dom := organization.GetSubdomainFromHost(r)
dom2 := organization.GetRequestSubdomain(r)
if org.Domain != dom && org.Domain != dom2 {
response.WriteUnauthorizedError(w)
return
}
// If we have bad auth token and the domain allows anon access
// then we generate guest rc.
if org.AllowAnonymousAccess {
// So you have a bad token
if len(token) > 1 {
if tokenErr != nil {
response.WriteUnauthorizedError(w)
return
}
} else {
// Just grant anon user guest access
rc.UserID = "0"
rc.OrgID = org.RefID
rc.Authenticated = false
rc.Guest = true
}
}
rc.AllowAnonymousAccess = org.AllowAnonymousAccess
rc.OrgName = org.Title
rc.Administrator = false
rc.Editor = false
rc.Global = false
rc.AppURL = r.Host
rc.Subdomain = organization.GetSubdomainFromHost(r)
rc.SSL = r.TLS != nil
// Fetch user permissions for this org
if !rc.Authenticated {
response.WriteUnauthorizedError(w)
return
}
u, err := user.GetSecuredUser(rc, *h.Store, org.RefID, rc.UserID)
if err != nil {
response.WriteUnauthorizedError(w)
h.Runtime.Log.Error("ValidateToken", err)
return
}
rc.Administrator = u.Admin
rc.Editor = u.Editor
rc.Global = u.Global
response.WriteJSON(w, u)
}