1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-18 20:59:43 +02:00

moving endpoints to new API (WIP)

This commit is contained in:
Harvey Kandola 2017-07-26 10:50:26 +01:00
parent 27640dffc4
commit 72b14def6d
36 changed files with 1371 additions and 472 deletions

View file

@ -718,7 +718,7 @@ func ResetUserPassword(w http.ResponseWriter, r *http.Request) {
log.IfErr(err) log.IfErr(err)
} }
// Get user object contain associated accounts but credentials are wiped. // GetSecuredUser wipes credentials.
func GetSecuredUser(p request.Persister, orgID, user string) (u entity.User, err error) { func GetSecuredUser(p request.Persister, orgID, user string) (u entity.User, err error) {
u, err = p.GetUser(user) u, err = p.GetUser(user)
AttachUserAccounts(p, orgID, &u) AttachUserAccounts(p, orgID, &u)
@ -726,6 +726,7 @@ func GetSecuredUser(p request.Persister, orgID, user string) (u entity.User, err
return return
} }
// AttachUserAccounts ...
func AttachUserAccounts(p request.Persister, orgID string, user *entity.User) { func AttachUserAccounts(p request.Persister, orgID string, user *entity.User) {
user.ProtectSecrets() user.ProtectSecrets()
a, err := p.GetUserAccounts(user.RefID) a, err := p.GetUserAccounts(user.RefID)

View file

@ -1,13 +0,0 @@
// 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 request provides HTTP request parsing functions.
package request

View file

@ -0,0 +1,12 @@
package account
import (
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store domain.Store
}

View file

@ -1,22 +1,40 @@
package account // 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 mysql
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"time" "time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql" "github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/account"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add inserts the given record into the datbase account table. // Add inserts the given record into the datbase account table.
func Add(s domain.StoreContext, account Account) (err error) { func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err error) {
account.Created = time.Now().UTC() account.Created = time.Now().UTC()
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -35,7 +53,7 @@ func Add(s domain.StoreContext, account Account) (err error) {
} }
// GetUserAccount returns the database account record corresponding to the given userID, using the client's current organizaion. // GetUserAccount returns the database account record corresponding to the given userID, using the client's current organizaion.
func GetUserAccount(s domain.StoreContext, userID string) (account Account, err error) { func (s Scope) GetUserAccount(ctx domain.RequestContext, userID string) (account account.Account, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE b.refid=a.orgid and a.orgid=? and a.userid=?") stmt, err := s.Runtime.Db.Preparex("SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE b.refid=a.orgid and a.orgid=? and a.userid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -44,7 +62,7 @@ func GetUserAccount(s domain.StoreContext, userID string) (account Account, err
return return
} }
err = stmt.Get(&account, s.Context.OrgID, userID) err = stmt.Get(&account, ctx.OrgID, userID)
if err != sql.ErrNoRows && err != nil { if err != sql.ErrNoRows && err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select for account by user %s", userID)) err = errors.Wrap(err, fmt.Sprintf("execute select for account by user %s", userID))
return return
@ -54,7 +72,7 @@ func GetUserAccount(s domain.StoreContext, userID string) (account Account, err
} }
// GetUserAccounts returns a slice of database account records, for all organizations that the userID is a member of, in organization title order. // GetUserAccounts returns a slice of database account records, for all organizations that the userID is a member of, in organization title order.
func GetUserAccounts(s domain.StoreContext, userID string) (t []Account, err error) { func (s Scope) GetUserAccounts(ctx domain.RequestContext, userID string) (t []account.Account, err error) {
err = s.Runtime.Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.userid=? AND a.orgid=b.refid AND a.active=1 ORDER BY b.title", userID) err = s.Runtime.Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.userid=? AND a.orgid=b.refid AND a.active=1 ORDER BY b.title", userID)
if err != sql.ErrNoRows && err != nil { if err != sql.ErrNoRows && err != nil {
@ -65,19 +83,19 @@ func GetUserAccounts(s domain.StoreContext, userID string) (t []Account, err err
} }
// GetAccountsByOrg returns a slice of database account records, for all users in the client's organization. // GetAccountsByOrg returns a slice of database account records, for all users in the client's organization.
func GetAccountsByOrg(s domain.StoreContext) (t []Account, err error) { func (s Scope) GetAccountsByOrg(ctx domain.RequestContext) (t []account.Account, err error) {
err = s.Runtime.Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.orgid=b.refid AND a.orgid=? AND a.active=1", s.Context.OrgID) err = s.Runtime.Db.Select(&t, "SELECT a.*, b.company, b.title, b.message, b.domain FROM account a, organization b WHERE a.orgid=b.refid AND a.orgid=? AND a.active=1", ctx.OrgID)
if err != sql.ErrNoRows && err != nil { if err != sql.ErrNoRows && err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select account for org %s", s.Context.OrgID)) err = errors.Wrap(err, fmt.Sprintf("execute select account for org %s", ctx.OrgID))
} }
return return
} }
// CountOrgAccounts returns the numnber of active user accounts for specified organization. // CountOrgAccounts returns the numnber of active user accounts for specified organization.
func CountOrgAccounts(s domain.StoreContext) (c int) { func (s Scope) CountOrgAccounts(ctx domain.RequestContext) (c int) {
row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? AND active=1", s.Context.OrgID) row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? AND active=1", ctx.OrgID)
err := row.Scan(&c) err := row.Scan(&c)
@ -94,10 +112,10 @@ func CountOrgAccounts(s domain.StoreContext) (c int) {
} }
// UpdateAccount updates the database record for the given account to the given values. // UpdateAccount updates the database record for the given account to the given values.
func UpdateAccount(s domain.StoreContext, account Account) (err error) { func (s Scope) UpdateAccount(ctx domain.RequestContext, account account.Account) (err error) {
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err := ctx.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -115,7 +133,7 @@ func UpdateAccount(s domain.StoreContext, account Account) (err error) {
} }
// HasOrgAccount returns if the given orgID has valid userID. // HasOrgAccount returns if the given orgID has valid userID.
func HasOrgAccount(s domain.StoreContext, orgID, userID string) bool { func (s Scope) HasOrgAccount(ctx domain.RequestContext, orgID, userID string) bool {
row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? and userid=?", orgID, userID) row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? and userid=?", orgID, userID)
var count int var count int
@ -138,7 +156,7 @@ func HasOrgAccount(s domain.StoreContext, orgID, userID string) bool {
} }
// DeleteAccount deletes the database record in the account table for user ID. // DeleteAccount deletes the database record in the account table for user ID.
func DeleteAccount(s domain.StoreContext, ID string) (rows int64, err error) { func (s Scope) DeleteAccount(ctx domain.RequestContext, ID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.DeleteConstrained(s.Context.Transaction, "account", s.Context.OrgID, ID) return b.DeleteConstrained(ctx.Transaction, "account", ctx.OrgID, ID)
} }

View file

@ -9,23 +9,30 @@
// //
// https://documize.com // https://documize.com
// Package eventing records user events. // Package audit records user events.
package eventing package audit
import ( import (
"time" "time"
"github.com/documize/community/core/env"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/model/audit"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Record adds event entry for specified user. // Record adds event entry for specified user.
func Record(s domain.StoreContext, t EventType) { func (s Scope) Record(ctx domain.RequestContext, t audit.EventType) {
e := AppEvent{} e := audit.AppEvent{}
e.OrgID = s.Context.OrgID e.OrgID = ctx.OrgID
e.UserID = s.Context.UserID e.UserID = ctx.UserID
e.Created = time.Now().UTC() e.Created = time.Now().UTC()
e.IP = s.Context.ClientIP e.IP = ctx.ClientIP
e.Type = string(t) e.Type = string(t)
tx, err := s.Runtime.Db.Beginx() tx, err := s.Runtime.Db.Beginx()

View file

@ -11,20 +11,7 @@
package auth package auth
import ( /*
"database/sql"
"errors"
"net/http"
"strings"
"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"
)
// Authenticate user based up HTTP Authorization header. // Authenticate user based up HTTP Authorization header.
// An encrypted authentication token is issued with an expiry date. // An encrypted authentication token is issued with an expiry date.
func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) { func (h *Handler) Authenticate(w http.ResponseWriter, r *http.Request) {
@ -205,3 +192,4 @@ func (h *Handler) ValidateAuthToken(w http.ResponseWriter, r *http.Request) {
response.WriteJSON(w, u) response.WriteJSON(w, u)
return return
} }
*/

View file

@ -11,11 +11,7 @@
package auth package auth
import ( /*
"github.com/documize/community/core/env"
"github.com/documize/community/domain/user"
)
// Handler contains the runtime information such as logging and database. // Handler contains the runtime information such as logging and database.
type Handler struct { type Handler struct {
Runtime *env.Runtime Runtime *env.Runtime
@ -26,3 +22,4 @@ type AuthenticationModel struct {
Token string `json:"token"` Token string `json:"token"`
User user.User `json:"user"` User user.User `json:"user"`
} }
*/

View file

@ -1,3 +1,15 @@
// 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 domain defines data structures for moving data between services.
package domain package domain
import ( import (
@ -5,7 +17,6 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/documize/community/core/env"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
@ -51,13 +62,13 @@ func GetRequestContext(r *http.Request) RequestContext {
return r.Context().Value(DocumizeContextKey).(RequestContext) return r.Context().Value(DocumizeContextKey).(RequestContext)
} }
// StoreContext provides data persistence methods with runtime and request context. // // Scope provides data persistence methods with runtime and request context.
type StoreContext struct { // type Scope struct {
Runtime *env.Runtime // Runtime *env.Runtime
Context RequestContext // Context RequestContext
} // }
// NewContext returns request scoped user context and store context for persistence logic. // // NewScope returns request scoped user context and store context for persistence logic.
func NewContext(rt *env.Runtime, r *http.Request) StoreContext { // func NewScope(rt *env.Runtime, r *http.Request) Scope {
return StoreContext{Runtime: rt, Context: GetRequestContext(r)} // return Scope{Runtime: rt, Context: GetRequestContext(r)}
} // }

View file

@ -14,14 +14,20 @@ package document
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// MoveDocumentSpace changes the label for client's organization's documents which have space "id", to "move". // MoveDocumentSpace changes the label for client's organization's documents which have space "id", to "move".
func MoveDocumentSpace(s domain.StoreContext, id, move string) (err error) { func (s Scope) MoveDocumentSpace(ctx domain.RequestContext, id, move string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE document SET labelid=? WHERE orgid=? AND labelid=?") stmt, err := ctx.Transaction.Preparex("UPDATE document SET labelid=? WHERE orgid=? AND labelid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -29,7 +35,7 @@ func MoveDocumentSpace(s domain.StoreContext, id, move string) (err error) {
return return
} }
_, err = stmt.Exec(move, s.Context.OrgID, id) _, err = stmt.Exec(move, ctx.OrgID, id)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute document space move %s", id)) err = errors.Wrap(err, fmt.Sprintf("execute document space move %s", id))
return return

View file

@ -0,0 +1,94 @@
// 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 organization
import (
"database/sql"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/documize/community/core/env"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"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
}
// Get returns the requested organization.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "org.Get"
ctx := domain.GetRequestContext(r)
orgID := request.Param(r, "orgID")
if orgID != ctx.OrgID {
response.WriteForbiddenError(w)
return
}
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, org)
}
// Update saves organization amends.
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "org.Update"
ctx := domain.GetRequestContext(r)
if !ctx.Administrator {
response.WriteForbiddenError(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteServerError(w, method, err)
return
}
var org = org.Organization{}
err = json.Unmarshal(body, &org)
org.RefID = ctx.OrgID
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = h.Store.Organization.UpdateOrganization(ctx, org)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
ctx.Transaction.Commit()
response.WriteJSON(w, org)
}

View file

@ -21,16 +21,22 @@ import (
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql" "github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/org"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// AddOrganization inserts the passed organization record into the organization table. // AddOrganization inserts the passed organization record into the organization table.
func AddOrganization(s domain.StoreContext, org Organization) error { func (s Scope) AddOrganization(ctx domain.RequestContext, org org.Organization) error {
org.Created = time.Now().UTC() org.Created = time.Now().UTC()
org.Revised = time.Now().UTC() org.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex( stmt, err := ctx.Transaction.Preparex(
"INSERT INTO organization (refid, company, title, message, url, domain, email, allowanonymousaccess, serial, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") "INSERT INTO organization (refid, company, title, message, url, domain, email, allowanonymousaccess, serial, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -51,7 +57,7 @@ func AddOrganization(s domain.StoreContext, org Organization) error {
} }
// GetOrganization returns the Organization reocrod from the organization database table with the given id. // GetOrganization returns the Organization reocrod from the organization database table with the given id.
func GetOrganization(s domain.StoreContext, id string) (org Organization, err error) { func (s Scope) GetOrganization(ctx domain.RequestContext, id string) (org org.Organization, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE refid=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -71,7 +77,7 @@ func GetOrganization(s domain.StoreContext, id string) (org Organization, err er
} }
// GetOrganizationByDomain returns the organization matching a given URL subdomain. // GetOrganizationByDomain returns the organization matching a given URL subdomain.
func GetOrganizationByDomain(s domain.StoreContext, subdomain string) (org Organization, err error) { func (s Scope) GetOrganizationByDomain(ctx domain.RequestContext, subdomain string) (org org.Organization, err error) {
err = nil err = nil
subdomain = strings.ToLower(subdomain) subdomain = strings.ToLower(subdomain)
@ -98,10 +104,10 @@ func GetOrganizationByDomain(s domain.StoreContext, subdomain string) (org Organ
} }
// UpdateOrganization updates the given organization record in the database to the values supplied. // UpdateOrganization updates the given organization record in the database to the values supplied.
func UpdateOrganization(s domain.StoreContext, org Organization) (err error) { func (s Scope) UpdateOrganization(ctx domain.RequestContext, org org.Organization) (err error) {
org.Revised = time.Now().UTC() org.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.PrepareNamed("UPDATE organization SET title=:title, message=:message, service=:conversionendpoint, email=:email, allowanonymousaccess=:allowanonymousaccess, revised=:revised WHERE refid=:refid") stmt, err := ctx.Transaction.PrepareNamed("UPDATE organization SET title=:title, message=:message, service=:conversionendpoint, email=:email, allowanonymousaccess=:allowanonymousaccess, revised=:revised WHERE refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -119,14 +125,14 @@ func UpdateOrganization(s domain.StoreContext, org Organization) (err error) {
} }
// DeleteOrganization deletes the orgID organization from the organization table. // DeleteOrganization deletes the orgID organization from the organization table.
func DeleteOrganization(s domain.StoreContext, orgID string) (rows int64, err error) { func (s Scope) DeleteOrganization(ctx domain.RequestContext, orgID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.Delete(s.Context.Transaction, "organization", orgID) return b.Delete(ctx.Transaction, "organization", orgID)
} }
// RemoveOrganization sets the orgID organization to be inactive, thus executing a "soft delete" operation. // RemoveOrganization sets the orgID organization to be inactive, thus executing a "soft delete" operation.
func RemoveOrganization(s domain.StoreContext, rc domain.RequestContext, orgID string) (err error) { func (s Scope) RemoveOrganization(ctx domain.RequestContext, orgID string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?") stmt, err := ctx.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -144,10 +150,10 @@ func RemoveOrganization(s domain.StoreContext, rc domain.RequestContext, orgID s
} }
// UpdateAuthConfig updates the given organization record in the database with the auth config details. // UpdateAuthConfig updates the given organization record in the database with the auth config details.
func UpdateAuthConfig(s domain.StoreContext, org Organization) (err error) { func (s Scope) UpdateAuthConfig(ctx domain.RequestContext, org org.Organization) (err error) {
org.Revised = time.Now().UTC() org.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.PrepareNamed("UPDATE organization SET allowanonymousaccess=:allowanonymousaccess, authprovider=:authprovider, authconfig=:authconfig, revised=:revised WHERE refid=:refid") stmt, err := ctx.Transaction.PrepareNamed("UPDATE organization SET allowanonymousaccess=:allowanonymousaccess, authprovider=:authprovider, authconfig=:authconfig, revised=:revised WHERE refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -165,7 +171,7 @@ func UpdateAuthConfig(s domain.StoreContext, org Organization) (err error) {
} }
// CheckDomain makes sure there is an organisation with the correct domain // CheckDomain makes sure there is an organisation with the correct domain
func CheckDomain(s domain.StoreContext, domain string) string { func (s Scope) CheckDomain(ctx domain.RequestContext, domain string) string {
row := s.Runtime.Db.QueryRow("SELECT COUNT(*) FROM organization WHERE domain=? AND active=1", domain) row := s.Runtime.Db.QueryRow("SELECT COUNT(*) FROM organization WHERE domain=? AND active=1", domain)
var count int var count int

View file

@ -14,22 +14,19 @@ package organization
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/documize/community/domain"
) )
// GetRequestSubdomain extracts subdomain from referring URL. // GetRequestSubdomain extracts subdomain from referring URL.
func GetRequestSubdomain(s domain.StoreContext, r *http.Request) string { func GetRequestSubdomain(r *http.Request) string {
return urlSubdomain(s, r.Referer()) return urlSubdomain(r.Referer())
} }
// GetSubdomainFromHost extracts the subdomain from the requesting URL. // GetSubdomainFromHost extracts the subdomain from the requesting URL.
func GetSubdomainFromHost(s domain.StoreContext, r *http.Request) string { func GetSubdomainFromHost(r *http.Request) string {
return urlSubdomain(s, r.Host) return urlSubdomain(r.Host)
} }
// Find the subdomain (which is actually the organisation). func urlSubdomain(url string) string {
func urlSubdomain(s domain.StoreContext, url string) string {
url = strings.ToLower(url) url = strings.ToLower(url)
url = strings.Replace(url, "https://", "", 1) url = strings.Replace(url, "https://", "", 1)
url = strings.Replace(url, "http://", "", 1) url = strings.Replace(url, "http://", "", 1)
@ -42,5 +39,5 @@ func urlSubdomain(s domain.StoreContext, url string) string {
url = "" url = ""
} }
return CheckDomain(s, url) return url
} }

View file

@ -18,17 +18,25 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/documize/community/core/env"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/uniqueid" "github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/eventing" "github.com/documize/community/model/audit"
"github.com/documize/community/model/pin"
) )
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *domain.Store
}
// Add saves pinned item. // Add saves pinned item.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "pin.Add" method := "pin.Add"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
userID := request.Param(r, "userID") userID := request.Param(r, "userID")
if len(userID) == 0 { if len(userID) == 0 {
@ -41,7 +49,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return return
} }
if !s.Context.Authenticated { if !ctx.Authenticated {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -53,7 +61,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return return
} }
var pin Pin var pin pin.Pin
err = json.Unmarshal(body, &pin) err = json.Unmarshal(body, &pin)
if err != nil { if err != nil {
response.WriteBadRequestError(w, method, "pin") response.WriteBadRequestError(w, method, "pin")
@ -61,31 +69,31 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
} }
pin.RefID = uniqueid.Generate() pin.RefID = uniqueid.Generate()
pin.OrgID = s.Context.OrgID pin.OrgID = ctx.OrgID
pin.UserID = s.Context.UserID pin.UserID = ctx.UserID
pin.Pin = strings.TrimSpace(pin.Pin) pin.Pin = strings.TrimSpace(pin.Pin)
if len(pin.Pin) > 20 { if len(pin.Pin) > 20 {
pin.Pin = pin.Pin[0:20] pin.Pin = pin.Pin[0:20]
} }
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
err = Add(s, pin) err = h.Store.Pin.Add(ctx, pin)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypePinAdd) h.Store.Audit.Record(ctx, audit.EventTypePinAdd)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
newPin, err := GetPin(s, pin.RefID) newPin, err := h.Store.Pin.GetPin(ctx, pin.RefID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
@ -97,7 +105,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
// GetUserPins returns users' pins. // GetUserPins returns users' pins.
func (h *Handler) GetUserPins(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetUserPins(w http.ResponseWriter, r *http.Request) {
method := "pin.GetUserPins" method := "pin.GetUserPins"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
userID := request.Param(r, "userID") userID := request.Param(r, "userID")
if len(userID) == 0 { if len(userID) == 0 {
@ -105,19 +113,19 @@ func (h *Handler) GetUserPins(w http.ResponseWriter, r *http.Request) {
return return
} }
if !s.Context.Authenticated { if !ctx.Authenticated {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
pins, err := GetUserPins(s, userID) pins, err := h.Store.Pin.GetUserPins(ctx, userID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
pins = []Pin{} pins = []pin.Pin{}
} }
response.WriteJSON(w, pins) response.WriteJSON(w, pins)
@ -126,7 +134,7 @@ func (h *Handler) GetUserPins(w http.ResponseWriter, r *http.Request) {
// DeleteUserPin removes saved user pin. // DeleteUserPin removes saved user pin.
func (h *Handler) DeleteUserPin(w http.ResponseWriter, r *http.Request) { func (h *Handler) DeleteUserPin(w http.ResponseWriter, r *http.Request) {
method := "pin.DeleteUserPin" method := "pin.DeleteUserPin"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
userID := request.Param(r, "userID") userID := request.Param(r, "userID")
if len(userID) == 0 { if len(userID) == 0 {
@ -145,28 +153,28 @@ func (h *Handler) DeleteUserPin(w http.ResponseWriter, r *http.Request) {
return return
} }
if s.Context.UserID != userID { if ctx.UserID != userID {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
var err error var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = DeletePin(s, pinID) _, err = h.Store.Pin.DeletePin(ctx, pinID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypePinDelete) h.Store.Audit.Record(ctx, audit.EventTypePinDelete)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteEmpty(w) response.WriteEmpty(w)
} }
@ -174,7 +182,7 @@ func (h *Handler) DeleteUserPin(w http.ResponseWriter, r *http.Request) {
// UpdatePinSequence records order of pinned items. // UpdatePinSequence records order of pinned items.
func (h *Handler) UpdatePinSequence(w http.ResponseWriter, r *http.Request) { func (h *Handler) UpdatePinSequence(w http.ResponseWriter, r *http.Request) {
method := "pin.DeleteUserPin" method := "pin.DeleteUserPin"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
userID := request.Param(r, "userID") userID := request.Param(r, "userID")
if len(userID) == 0 { if len(userID) == 0 {
@ -187,7 +195,7 @@ func (h *Handler) UpdatePinSequence(w http.ResponseWriter, r *http.Request) {
return return
} }
if !s.Context.Authenticated { if !ctx.Authenticated {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -207,26 +215,26 @@ func (h *Handler) UpdatePinSequence(w http.ResponseWriter, r *http.Request) {
return return
} }
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
for k, v := range pins { for k, v := range pins {
err = UpdatePinSequence(s, v, k+1) err = h.Store.Pin.UpdatePinSequence(ctx, v, k+1)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
} }
eventing.Record(s, eventing.EventTypePinResequence) h.Store.Audit.Record(ctx, audit.EventTypePinResequence)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
newPins, err := GetUserPins(s, userID) newPins, err := h.Store.Pin.GetUserPins(ctx, userID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return

View file

@ -15,17 +15,23 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/documize/community/core/api/entity" "github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql" "github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/pin"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add saves pinned item. // Add saves pinned item.
func Add(s domain.StoreContext, pin Pin) (err error) { func (s Scope) Add(ctx domain.RequestContext, pin pin.Pin) (err error) {
row := s.Runtime.Db.QueryRow("SELECT max(sequence) FROM pin WHERE orgid=? AND userid=?", s.Context.OrgID, s.Context.UserID) row := s.Runtime.Db.QueryRow("SELECT max(sequence) FROM pin WHERE orgid=? AND userid=?", ctx.OrgID, ctx.UserID)
var maxSeq int var maxSeq int
err = row.Scan(&maxSeq) err = row.Scan(&maxSeq)
@ -37,7 +43,7 @@ func Add(s domain.StoreContext, pin Pin) (err error) {
pin.Revised = time.Now().UTC() pin.Revised = time.Now().UTC()
pin.Sequence = maxSeq + 1 pin.Sequence = maxSeq + 1
stmt, err := s.Context.Transaction.Preparex("INSERT INTO pin (refid, orgid, userid, labelid, documentid, pin, sequence, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO pin (refid, orgid, userid, labelid, documentid, pin, sequence, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -55,7 +61,7 @@ func Add(s domain.StoreContext, pin Pin) (err error) {
} }
// GetPin returns requested pinned item. // GetPin returns requested pinned item.
func GetPin(s domain.StoreContext, id string) (pin Pin, err error) { func (s Scope) GetPin(ctx domain.RequestContext, id string) (pin pin.Pin, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, orgid, userid, labelid as folderid, documentid, pin, sequence, created, revised FROM pin WHERE orgid=? AND refid=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, orgid, userid, labelid as folderid, documentid, pin, sequence, created, revised FROM pin WHERE orgid=? AND refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -64,7 +70,7 @@ func GetPin(s domain.StoreContext, id string) (pin Pin, err error) {
return return
} }
err = stmt.Get(&pin, s.Context.OrgID, id) err = stmt.Get(&pin, ctx.OrgID, id)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select for pin %s", id)) err = errors.Wrap(err, fmt.Sprintf("execute select for pin %s", id))
return return
@ -74,11 +80,11 @@ func GetPin(s domain.StoreContext, id string) (pin Pin, err error) {
} }
// GetUserPins returns pinned items for specified user. // GetUserPins returns pinned items for specified user.
func GetUserPins(s domain.StoreContext, userID string) (pins []Pin, err error) { func (s Scope) GetUserPins(ctx domain.RequestContext, userID string) (pins []pin.Pin, err error) {
err = s.Runtime.Db.Select(&pins, "SELECT id, refid, orgid, userid, labelid as folderid, documentid, pin, sequence, created, revised FROM pin WHERE orgid=? AND userid=? ORDER BY sequence", s.Context.OrgID, userID) err = s.Runtime.Db.Select(&pins, "SELECT id, refid, orgid, userid, labelid as folderid, documentid, pin, sequence, created, revised FROM pin WHERE orgid=? AND userid=? ORDER BY sequence", ctx.OrgID, userID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select pins for org %s and user %s", s.Context.OrgID, userID)) err = errors.Wrap(err, fmt.Sprintf("execute select pins for org %s and user %s", ctx.OrgID, userID))
return return
} }
@ -86,11 +92,11 @@ func GetUserPins(s domain.StoreContext, userID string) (pins []Pin, err error) {
} }
// UpdatePin updates existing pinned item. // UpdatePin updates existing pinned item.
func UpdatePin(s domain.StoreContext, pin entity.Pin) (err error) { func (s Scope) UpdatePin(ctx domain.RequestContext, pin pin.Pin) (err error) {
pin.Revised = time.Now().UTC() pin.Revised = time.Now().UTC()
var stmt *sqlx.NamedStmt var stmt *sqlx.NamedStmt
stmt, err = s.Context.Transaction.PrepareNamed("UPDATE pin SET labelid=:folderid, documentid=:documentid, pin=:pin, sequence=:sequence, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err = ctx.Transaction.PrepareNamed("UPDATE pin SET labelid=:folderid, documentid=:documentid, pin=:pin, sequence=:sequence, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -108,8 +114,8 @@ func UpdatePin(s domain.StoreContext, pin entity.Pin) (err error) {
} }
// UpdatePinSequence updates existing pinned item sequence number // UpdatePinSequence updates existing pinned item sequence number
func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err error) { func (s Scope) UpdatePinSequence(ctx domain.RequestContext, pinID string, sequence int) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE pin SET sequence=?, revised=? WHERE orgid=? AND userid=? AND refid=?") stmt, err := ctx.Transaction.Preparex("UPDATE pin SET sequence=?, revised=? WHERE orgid=? AND userid=? AND refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -117,7 +123,7 @@ func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err e
return return
} }
_, err = stmt.Exec(sequence, time.Now().UTC(), s.Context.OrgID, s.Context.UserID, pinID) _, err = stmt.Exec(sequence, time.Now().UTC(), ctx.OrgID, ctx.UserID, pinID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute pin sequence update %s", pinID)) err = errors.Wrap(err, fmt.Sprintf("execute pin sequence update %s", pinID))
return return
@ -127,19 +133,19 @@ func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err e
} }
// DeletePin removes folder from the store. // DeletePin removes folder from the store.
func DeletePin(s domain.StoreContext, id string) (rows int64, err error) { func (s Scope) DeletePin(ctx domain.RequestContext, id string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.DeleteConstrained(s.Context.Transaction, "pin", s.Context.OrgID, id) return b.DeleteConstrained(ctx.Transaction, "pin", ctx.OrgID, id)
} }
// DeletePinnedSpace removes any pins for specified space. // DeletePinnedSpace removes any pins for specified space.
func DeletePinnedSpace(s domain.StoreContext, spaceID string) (rows int64, err error) { func (s Scope) DeletePinnedSpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.DeleteWhere(s.Context.Transaction, fmt.Sprintf("DELETE FROM pin WHERE orgid=\"%s\" AND labelid=\"%s\"", s.Context.OrgID, spaceID)) return b.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM pin WHERE orgid=\"%s\" AND labelid=\"%s\"", ctx.OrgID, spaceID))
} }
// DeletePinnedDocument removes any pins for specified document. // DeletePinnedDocument removes any pins for specified document.
func DeletePinnedDocument(s domain.StoreContext, documentID string) (rows int64, err error) { func (s Scope) DeletePinnedDocument(ctx domain.RequestContext, documentID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.DeleteWhere(s.Context.Transaction, fmt.Sprintf("DELETE FROM pin WHERE orgid=\"%s\" AND documentid=\"%s\"", s.Context.OrgID, documentID)) return b.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM pin WHERE orgid=\"%s\" AND documentid=\"%s\"", ctx.OrgID, documentID))
} }

View file

@ -23,6 +23,7 @@ import (
"github.com/documize/api/wordsmith/log" "github.com/documize/api/wordsmith/log"
"github.com/documize/community/core/api/mail" "github.com/documize/community/core/api/mail"
"github.com/documize/community/core/env"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
@ -30,25 +31,29 @@ import (
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
"github.com/documize/community/core/uniqueid" "github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/account" "github.com/documize/community/model/account"
"github.com/documize/community/domain/document" "github.com/documize/community/model/audit"
"github.com/documize/community/domain/eventing" "github.com/documize/community/model/space"
"github.com/documize/community/domain/organization" "github.com/documize/community/model/user"
"github.com/documize/community/domain/pin"
"github.com/documize/community/domain/user"
) )
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store *domain.Store
}
// Add creates a new space. // Add creates a new space.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "AddSpace" method := "space.Add"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() { if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w) response.WriteBadLicense(w)
return return
} }
if !s.Context.Editor { if !ctx.Editor {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -60,7 +65,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return return
} }
var space = Space{} var space = space.Space{}
err = json.Unmarshal(body, &space) err = json.Unmarshal(body, &space)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
@ -72,27 +77,27 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return return
} }
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
space.RefID = uniqueid.Generate() space.RefID = uniqueid.Generate()
space.OrgID = s.Context.OrgID space.OrgID = ctx.OrgID
err = addSpace(s, space) err = h.Store.Space.Add(ctx, space)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpaceAdd) h.Store.Audit.Record(ctx, audit.EventTypeSpaceAdd)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
space, _ = Get(s, space.RefID) space, _ = h.Store.Space.Get(ctx, space.RefID)
response.WriteJSON(w, space) response.WriteJSON(w, space)
} }
@ -100,7 +105,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
// Get returns the requested space. // Get returns the requested space.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "Get" method := "Get"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID") id := request.Param(r, "folderID")
if len(id) == 0 { if len(id) == 0 {
@ -108,7 +113,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
return return
} }
sp, err := Get(s, id) sp, err := h.Store.Space.Get(ctx, id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id) response.WriteNotFoundError(w, method, id)
return return
@ -124,16 +129,16 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
// GetAll returns spaces the user can see. // GetAll returns spaces the user can see.
func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
method := "GetAll" method := "GetAll"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
sp, err := GetAll(s) sp, err := h.Store.Space.GetAll(ctx)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if len(sp) == 0 { if len(sp) == 0 {
sp = []Space{} sp = []space.Space{}
} }
response.WriteJSON(w, sp) response.WriteJSON(w, sp)
@ -141,17 +146,17 @@ func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
// GetSpaceViewers returns the users that can see the shared spaces. // GetSpaceViewers returns the users that can see the shared spaces.
func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) {
method := "GetSpaceViewers" method := "space.Viewers"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
v, err := Viewers(s) v, err := h.Store.Space.Viewers(ctx)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if len(v) == 0 { if len(v) == 0 {
v = []Viewer{} v = []space.Viewer{}
} }
response.WriteJSON(w, v) response.WriteJSON(w, v)
@ -160,9 +165,9 @@ func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) {
// Update processes request to save space object to the database // Update processes request to save space object to the database
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) { func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "space.Update" method := "space.Update"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
if !s.Context.Editor { if !ctx.Editor {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -180,7 +185,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return return
} }
var sp Space var sp space.Space
err = json.Unmarshal(body, &sp) err = json.Unmarshal(body, &sp)
if err != nil { if err != nil {
response.WriteBadRequestError(w, method, "marshal") response.WriteBadRequestError(w, method, "marshal")
@ -194,22 +199,22 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
sp.RefID = folderID sp.RefID = folderID
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
err = Update(s, sp) err = h.Store.Space.Update(ctx, sp)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpaceUpdate) h.Store.Audit.Record(ctx, audit.EventTypeSpaceUpdate)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteJSON(w, sp) response.WriteJSON(w, sp)
} }
@ -217,68 +222,68 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
// Remove moves documents to another folder before deleting it // Remove moves documents to another folder before deleting it
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) { func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
method := "space.Remove" method := "space.Remove"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() { if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w) response.WriteBadLicense(w)
return return
} }
if !s.Context.Editor { if !ctx.Editor {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
id := request.Param(r, "folderID") id := request.Param(r, "folderID")
move := request.Param(r, "moveToId")
if len(id) == 0 { if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID") response.WriteMissingDataError(w, method, "folderID")
return return
} }
move := request.Param(r, "moveToId")
if len(move) == 0 { if len(move) == 0 {
response.WriteMissingDataError(w, method, "moveToId") response.WriteMissingDataError(w, method, "moveToId")
return return
} }
var err error var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = Delete(s, id) _, err = h.Store.Space.Delete(ctx, id)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
err = document.MoveDocumentSpace(s, id, move) err = h.Store.Document.MoveDocumentSpace(ctx, id, move)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
err = MoveSpaceRoles(s, id, move) err = h.Store.Space.MoveSpaceRoles(ctx, id, move)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = pin.DeletePinnedSpace(s, id) _, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpaceDelete) h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteEmpty(w) response.WriteEmpty(w)
} }
@ -286,14 +291,14 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
// Delete deletes empty space. // Delete deletes empty space.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "space.Delete" method := "space.Delete"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() { if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w) response.WriteBadLicense(w)
return return
} }
if !s.Context.Editor { if !ctx.Editor {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -305,45 +310,46 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
} }
var err error var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = Delete(s, id) _, err = h.Store.Space.Delete(ctx, id)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = DeleteSpaceRoles(s, id) _, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
_, err = pin.DeletePinnedSpace(s, id) _, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpaceDelete) h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
ctx.Transaction.Commit()
s.Context.Transaction.Commit()
response.WriteEmpty(w) response.WriteEmpty(w)
} }
// SetPermissions persists specified spac3 permissions // SetPermissions persists specified spac3 permissions
func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) { func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.SetPermissions" method := "space.SetPermissions"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
if !s.Context.Editor { if !ctx.Editor {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -354,13 +360,13 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return return
} }
sp, err := Get(s, id) sp, err := h.Store.Space.Get(ctx, id)
if err != nil { if err != nil {
response.WriteNotFoundError(w, method, "No such space") response.WriteNotFoundError(w, method, "No such space")
return return
} }
if sp.UserID != s.Context.UserID { if sp.UserID != ctx.UserID {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -372,14 +378,14 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return return
} }
var model = RolesModel{} var model = space.RolesModel{}
err = json.Unmarshal(body, &model) err = json.Unmarshal(body, &model)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
@ -387,9 +393,9 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// We compare new permisions to what we had before. // We compare new permisions to what we had before.
// Why? So we can send out folder invitation emails. // Why? So we can send out folder invitation emails.
previousRoles, err := GetRoles(s, id) previousRoles, err := h.Store.Space.GetRoles(ctx, id)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
@ -402,17 +408,17 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
} }
// Who is sharing this folder? // Who is sharing this folder?
inviter, err := user.Get(s, s.Context.UserID) inviter, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
// Nuke all previous permissions for this folder // Nuke all previous permissions for this folder
_, err = DeleteSpaceRoles(s, id) _, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
@ -421,14 +427,14 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
hasEveryoneRole := false hasEveryoneRole := false
roleCount := 0 roleCount := 0
url := s.Context.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name))) url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
for _, role := range model.Roles { for _, role := range model.Roles {
role.OrgID = s.Context.OrgID role.OrgID = ctx.OrgID
role.LabelID = id role.LabelID = id
// Ensure the folder owner always has access! // Ensure the folder owner always has access!
if role.UserID == s.Context.UserID { if role.UserID == ctx.UserID {
me = true me = true
role.CanView = true role.CanView = true
role.CanEdit = true role.CanEdit = true
@ -442,7 +448,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
if role.CanView || role.CanEdit { if role.CanView || role.CanEdit {
roleID := uniqueid.Generate() roleID := uniqueid.Generate()
role.RefID = roleID role.RefID = roleID
err = AddRole(s, role) err = h.Store.Space.AddRole(ctx, role)
roleCount++ roleCount++
log.IfErr(err) log.IfErr(err)
@ -453,7 +459,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// we skip 'everyone' (user id != empty string) // we skip 'everyone' (user id != empty string)
if len(role.UserID) > 0 { if len(role.UserID) > 0 {
var existingUser user.User var existingUser user.User
existingUser, err = user.Get(s, role.UserID) existingUser, err = h.Store.User.Get(ctx, role.UserID)
if err == nil { if err == nil {
go mail.ShareFolderExistingUser(existingUser.Email, inviter.Fullname(), url, sp.Name, model.Message) go mail.ShareFolderExistingUser(existingUser.Email, inviter.Fullname(), url, sp.Name, model.Message)
@ -468,18 +474,18 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// Do we need to ensure permissions for space owner when shared? // Do we need to ensure permissions for space owner when shared?
if !me { if !me {
role := Role{} role := space.Role{}
role.LabelID = id role.LabelID = id
role.OrgID = s.Context.OrgID role.OrgID = ctx.OrgID
role.UserID = s.Context.UserID role.UserID = ctx.UserID
role.CanEdit = true role.CanEdit = true
role.CanView = true role.CanView = true
roleID := uniqueid.Generate() roleID := uniqueid.Generate()
role.RefID = roleID role.RefID = roleID
err = AddRole(s, role) err = h.Store.Space.AddRole(ctx, role)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
@ -487,25 +493,25 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// Mark up folder type as either public, private or restricted access. // Mark up folder type as either public, private or restricted access.
if hasEveryoneRole { if hasEveryoneRole {
sp.Type = ScopePublic sp.Type = space.ScopePublic
} else { } else {
if roleCount > 1 { if roleCount > 1 {
sp.Type = ScopeRestricted sp.Type = space.ScopeRestricted
} else { } else {
sp.Type = ScopePrivate sp.Type = space.ScopePrivate
} }
} }
err = Update(s, sp) err = h.Store.Space.Update(ctx, sp)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpacePermission) h.Store.Audit.Record(ctx, audit.EventTypeSpacePermission)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteEmpty(w) response.WriteEmpty(w)
} }
@ -513,7 +519,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// GetPermissions returns user permissions for the requested folder. // GetPermissions returns user permissions for the requested folder.
func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.GetPermissions" method := "space.GetPermissions"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID") folderID := request.Param(r, "folderID")
if len(folderID) == 0 { if len(folderID) == 0 {
@ -521,14 +527,14 @@ func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
return return
} }
roles, err := GetRoles(s, folderID) roles, err := h.Store.Space.GetRoles(ctx, folderID)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if len(roles) == 0 { if len(roles) == 0 {
roles = []Role{} roles = []space.Role{}
} }
response.WriteJSON(w, roles) response.WriteJSON(w, roles)
@ -537,7 +543,7 @@ func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
// AcceptInvitation records the fact that a user has completed space onboard process. // AcceptInvitation records the fact that a user has completed space onboard process.
func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) { func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
method := "space.AcceptInvitation" method := "space.AcceptInvitation"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID") folderID := request.Param(r, "folderID")
if len(folderID) == 0 { if len(folderID) == 0 {
@ -545,14 +551,14 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return return
} }
org, err := organization.GetOrganizationByDomain(s, s.Context.Subdomain) org, err := h.Store.Organization.GetOrganizationByDomain(ctx, ctx.Subdomain)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
// AcceptShare does not authenticate the user hence the context needs to set up // AcceptShare does not authenticate the user hence the context needs to set up
s.Context.OrgID = org.RefID ctx.OrgID = org.RefID
defer streamutil.Close(r.Body) defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
@ -561,7 +567,7 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return return
} }
var model = AcceptShareModel{} var model = space.AcceptShareModel{}
err = json.Unmarshal(body, &model) err = json.Unmarshal(body, &model)
if err != nil { if err != nil {
response.WriteBadRequestError(w, method, err.Error()) response.WriteBadRequestError(w, method, err.Error())
@ -573,44 +579,44 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return return
} }
u, err := user.GetBySerial(s, model.Serial) u, err := h.Store.User.GetBySerial(ctx, model.Serial)
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
response.WriteDuplicateError(w, method, "user") response.WriteDuplicateError(w, method, "user")
return return
} }
// AcceptShare does not authenticate the user hence the context needs to set up // AcceptShare does not authenticate the user hence the context needs to set up
s.Context.UserID = u.RefID ctx.UserID = u.RefID
u.Firstname = model.Firstname u.Firstname = model.Firstname
u.Lastname = model.Lastname u.Lastname = model.Lastname
u.Initials = stringutil.MakeInitials(u.Firstname, u.Lastname) u.Initials = stringutil.MakeInitials(u.Firstname, u.Lastname)
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
err = user.UpdateUser(s, u) err = h.Store.User.UpdateUser(ctx, u)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
salt := secrets.GenerateSalt() salt := secrets.GenerateSalt()
err = user.UpdateUserPassword(s, u.RefID, salt, secrets.GeneratePassword(model.Password, salt)) err = h.Store.User.UpdateUserPassword(ctx, u.RefID, salt, secrets.GeneratePassword(model.Password, salt))
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
eventing.Record(s, eventing.EventTypeSpaceJoin) h.Store.Audit.Record(ctx, audit.EventTypeSpaceJoin)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteJSON(w, u) response.WriteJSON(w, u)
} }
@ -618,7 +624,7 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
// Invite sends users folder invitation emails. // Invite sends users folder invitation emails.
func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) { func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
method := "space.Invite" method := "space.Invite"
s := domain.NewContext(h.Runtime, r) ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID") id := request.Param(r, "folderID")
if len(id) == 0 { if len(id) == 0 {
@ -626,13 +632,13 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
return return
} }
sp, err := Get(s, id) sp, err := h.Store.Space.Get(ctx, id)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if sp.UserID != s.Context.UserID { if sp.UserID != ctx.UserID {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -644,38 +650,38 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
return return
} }
var model = InvitationModel{} var model = space.InvitationModel{}
err = json.Unmarshal(body, &model) err = json.Unmarshal(body, &model)
if err != nil { if err != nil {
response.WriteBadRequestError(w, method, "json") response.WriteBadRequestError(w, method, "json")
return return
} }
s.Context.Transaction, err = h.Runtime.Db.Beginx() ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
inviter, err := user.Get(s, s.Context.UserID) inviter, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
for _, email := range model.Recipients { for _, email := range model.Recipients {
u, err := user.GetByEmail(s, email) u, err := h.Store.User.GetByEmail(ctx, email)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if len(u.RefID) > 0 { if len(u.RefID) > 0 {
// Ensure they have access to this organization // Ensure they have access to this organization
accounts, err2 := account.GetUserAccounts(s, u.RefID) accounts, err2 := h.Store.Account.GetUserAccounts(ctx, u.RefID)
if err2 != nil { if err2 != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
@ -683,7 +689,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
// we create if they c // we create if they c
hasAccess := false hasAccess := false
for _, a := range accounts { for _, a := range accounts {
if a.OrgID == s.Context.OrgID { if a.OrgID == ctx.OrgID {
hasAccess = true hasAccess = true
} }
} }
@ -691,52 +697,52 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
if !hasAccess { if !hasAccess {
var a account.Account var a account.Account
a.UserID = u.RefID a.UserID = u.RefID
a.OrgID = s.Context.OrgID a.OrgID = ctx.OrgID
a.Admin = false a.Admin = false
a.Editor = false a.Editor = false
a.Active = true a.Active = true
accountID := uniqueid.Generate() accountID := uniqueid.Generate()
a.RefID = accountID a.RefID = accountID
err = account.Add(s, a) err = h.Store.Account.Add(ctx, a)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
} }
// Ensure they have space roles // Ensure they have space roles
DeleteUserSpaceRoles(s, sp.RefID, u.RefID) h.Store.Space.DeleteUserSpaceRoles(ctx, sp.RefID, u.RefID)
role := Role{} role := space.Role{}
role.LabelID = sp.RefID role.LabelID = sp.RefID
role.OrgID = s.Context.OrgID role.OrgID = ctx.OrgID
role.UserID = u.RefID role.UserID = u.RefID
role.CanEdit = false role.CanEdit = false
role.CanView = true role.CanView = true
roleID := uniqueid.Generate() roleID := uniqueid.Generate()
role.RefID = roleID role.RefID = roleID
err = AddRole(s, role) err = h.Store.Space.AddRole(ctx, role)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
url := s.Context.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name))) url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
go mail.ShareFolderExistingUser(email, inviter.Fullname(), url, sp.Name, model.Message) go mail.ShareFolderExistingUser(email, inviter.Fullname(), url, sp.Name, model.Message)
h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with existing user %s", inviter.Email, sp.Name, email)) h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with existing user %s", inviter.Email, sp.Name, email))
} else { } else {
// On-board new user // On-board new user
if strings.Contains(email, "@") { if strings.Contains(email, "@") {
url := s.Context.GetAppURL(fmt.Sprintf("auth/share/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name))) url := ctx.GetAppURL(fmt.Sprintf("auth/share/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
err = inviteNewUserToSharedSpace(s, email, inviter, url, sp, model.Message) err = inviteNewUserToSharedSpace(ctx, h.Store, email, inviter, url, sp, model.Message)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
@ -747,20 +753,20 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
} }
// We ensure that the folder is marked as restricted as a minimum! // We ensure that the folder is marked as restricted as a minimum!
if len(model.Recipients) > 0 && sp.Type == ScopePrivate { if len(model.Recipients) > 0 && sp.Type == space.ScopePrivate {
sp.Type = ScopeRestricted sp.Type = space.ScopeRestricted
err = Update(s, sp) err = h.Store.Space.Update(ctx, sp)
if err != nil { if err != nil {
s.Context.Transaction.Rollback() ctx.Transaction.Rollback()
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
} }
eventing.Record(s, eventing.EventTypeSpaceInvite) h.Store.Audit.Record(ctx, audit.EventTypeSpaceInvite)
s.Context.Transaction.Commit() ctx.Transaction.Commit()
response.WriteEmpty(w) response.WriteEmpty(w)
} }

View file

@ -9,28 +9,34 @@
// //
// https://documize.com // https://documize.com
// Package space handles API calls and persistence for spaces. // Package mysql handles data persistence for spaces.
// Spaces in Documize contain documents. package mysql
package space
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"time" "time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql" "github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/space"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add adds new folder into the store. // Add adds new folder into the store.
func Add(s domain.StoreContext, sp Space) (err error) { func (s Scope) Add(ctx domain.RequestContext, sp space.Space) (err error) {
sp.UserID = s.Context.UserID sp.UserID = ctx.UserID
sp.Created = time.Now().UTC() sp.Created = time.Now().UTC()
sp.Revised = time.Now().UTC() sp.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO label (refid, label, orgid, userid, type, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO label (refid, label, orgid, userid, type, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -48,7 +54,7 @@ func Add(s domain.StoreContext, sp Space) (err error) {
} }
// Get returns a space from the store. // Get returns a space from the store.
func Get(s domain.StoreContext, id string) (sp Space, err error) { func (s Scope) Get(ctx domain.RequestContext, id string) (sp space.Space, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? and refid=?") stmt, err := s.Runtime.Db.Preparex("SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label WHERE orgid=? and refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -57,7 +63,7 @@ func Get(s domain.StoreContext, id string) (sp Space, err error) {
return return
} }
err = stmt.Get(&sp, s.Context.OrgID, id) err = stmt.Get(&sp, ctx.OrgID, id)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for label %s", id)) err = errors.Wrap(err, fmt.Sprintf("unable to execute select for label %s", id))
return return
@ -67,7 +73,7 @@ func Get(s domain.StoreContext, id string) (sp Space, err error) {
} }
// PublicSpaces returns folders that anyone can see. // PublicSpaces returns folders that anyone can see.
func PublicSpaces(s domain.StoreContext, orgID string) (sp []Space, err error) { func (s Scope) PublicSpaces(ctx domain.RequestContext, orgID string) (sp []space.Space, err error) {
sql := "SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1" sql := "SELECT id,refid,label as name,orgid,userid,type,created,revised FROM label a where orgid=? AND type=1"
err = s.Runtime.Db.Select(&sp, sql, orgID) err = s.Runtime.Db.Select(&sp, sql, orgID)
@ -82,7 +88,7 @@ func PublicSpaces(s domain.StoreContext, orgID string) (sp []Space, err error) {
// GetAll returns folders that the user can see. // GetAll returns folders that the user can see.
// Also handles which folders can be seen by anonymous users. // Also handles which folders can be seen by anonymous users.
func GetAll(s domain.StoreContext) (sp []Space, err error) { func (s Scope) GetAll(ctx domain.RequestContext) (sp []space.Space, err error) {
sql := ` sql := `
(SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?) (SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?)
UNION ALL UNION ALL
@ -94,16 +100,16 @@ UNION ALL
ORDER BY name` ORDER BY name`
err = s.Runtime.Db.Select(&sp, sql, err = s.Runtime.Db.Select(&sp, sql,
s.Context.OrgID, ctx.OrgID,
s.Context.UserID, ctx.UserID,
s.Context.OrgID, ctx.OrgID,
s.Context.OrgID, ctx.OrgID,
s.Context.OrgID, ctx.OrgID,
s.Context.OrgID, ctx.OrgID,
s.Context.UserID) ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("Unable to execute select labels for org %s", s.Context.OrgID)) err = errors.Wrap(err, fmt.Sprintf("Unable to execute select labels for org %s", ctx.OrgID))
return return
} }
@ -111,10 +117,10 @@ ORDER BY name`
} }
// Update saves space changes. // Update saves space changes.
func Update(s domain.StoreContext, sp Space) (err error) { func (s Scope) Update(ctx domain.RequestContext, sp space.Space) (err error) {
sp.Revised = time.Now().UTC() sp.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.PrepareNamed("UPDATE label SET label=:name, type=:type, userid=:userid, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err := ctx.Transaction.PrepareNamed("UPDATE label SET label=:name, type=:type, userid=:userid, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -132,8 +138,8 @@ func Update(s domain.StoreContext, sp Space) (err error) {
} }
// ChangeOwner transfer space ownership. // ChangeOwner transfer space ownership.
func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err error) { func (s Scope) ChangeOwner(ctx domain.RequestContext, currentOwner, newOwner string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?") stmt, err := ctx.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -141,7 +147,7 @@ func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err erro
return return
} }
_, err = stmt.Exec(newOwner, currentOwner, s.Context.OrgID) _, err = stmt.Exec(newOwner, currentOwner, ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute change space owner for %s", currentOwner)) err = errors.Wrap(err, fmt.Sprintf("unable to execute change space owner for %s", currentOwner))
return return
@ -151,7 +157,7 @@ func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err erro
} }
// Viewers returns the list of people who can see shared folders. // Viewers returns the list of people who can see shared folders.
func Viewers(s domain.StoreContext) (v []Viewer, err error) { func (s Scope) Viewers(ctx domain.RequestContext) (v []space.Viewer, err error) {
sql := ` sql := `
SELECT a.userid, SELECT a.userid,
COALESCE(u.firstname, '') as firstname, COALESCE(u.firstname, '') as firstname,
@ -167,23 +173,23 @@ WHERE a.orgid=? AND b.type != 2
GROUP BY a.labelid,a.userid GROUP BY a.labelid,a.userid
ORDER BY u.firstname,u.lastname` ORDER BY u.firstname,u.lastname`
err = s.Runtime.Db.Select(&v, sql, s.Context.OrgID) err = s.Runtime.Db.Select(&v, sql, ctx.OrgID)
return return
} }
// Delete removes space from the store. // Delete removes space from the store.
func Delete(s domain.StoreContext, id string) (rows int64, err error) { func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
return b.DeleteConstrained(s.Context.Transaction, "label", s.Context.OrgID, id) return b.DeleteConstrained(ctx.Transaction, "label", ctx.OrgID, id)
} }
// AddRole inserts the given record into the labelrole database table. // AddRole inserts the given record into the labelrole database table.
func AddRole(s domain.StoreContext, r Role) (err error) { func (s Scope) AddRole(ctx domain.RequestContext, r space.Role) (err error) {
r.Created = time.Now().UTC() r.Created = time.Now().UTC()
r.Revised = time.Now().UTC() r.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO labelrole (refid, labelid, orgid, userid, canview, canedit, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO labelrole (refid, labelid, orgid, userid, canview, canedit, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -201,10 +207,10 @@ func AddRole(s domain.StoreContext, r Role) (err error) {
} }
// GetRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user. // GetRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user.
func GetRoles(s domain.StoreContext, labelID string) (r []Role, err error) { func (s Scope) GetRoles(ctx domain.RequestContext, labelID string) (r []space.Role, err error) {
query := `SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND labelid=?` // was + "GROUP BY userid" query := `SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND labelid=?` // was + "GROUP BY userid"
err = s.Runtime.Db.Select(&r, query, s.Context.OrgID, labelID) err = s.Runtime.Db.Select(&r, query, ctx.OrgID, labelID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil
@ -220,19 +226,19 @@ func GetRoles(s domain.StoreContext, labelID string) (r []Role, err error) {
// GetUserRoles returns a slice of role records, for both the client's user and organization, and // GetUserRoles returns a slice of role records, for both the client's user and organization, and
// those space roles that exist for all users in the client's organization. // those space roles that exist for all users in the client's organization.
func GetUserRoles(s domain.StoreContext) (r []Role, err error) { func (s Scope) GetUserRoles(ctx domain.RequestContext) (r []space.Role, err error) {
err = s.Runtime.Db.Select(&r, ` err = s.Runtime.Db.Select(&r, `
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? and userid=? SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? and userid=?
UNION ALL UNION ALL
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND userid=''`, SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND userid=''`,
s.Context.OrgID, s.Context.UserID, s.Context.OrgID) ctx.OrgID, ctx.UserID, ctx.OrgID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil
} }
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for user space roles %s", s.Context.UserID)) err = errors.Wrap(err, fmt.Sprintf("unable to execute select for user space roles %s", ctx.UserID))
return return
} }
@ -240,36 +246,36 @@ func GetUserRoles(s domain.StoreContext) (r []Role, err error) {
} }
// DeleteRole deletes the labelRoleID record from the labelrole table. // DeleteRole deletes the labelRoleID record from the labelrole table.
func DeleteRole(s domain.StoreContext, roleID string) (rows int64, err error) { func (s Scope) DeleteRole(ctx domain.RequestContext, roleID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND refid='%s'", s.Context.OrgID, roleID) sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND refid='%s'", ctx.OrgID, roleID)
return b.DeleteWhere(s.Context.Transaction, sql) return b.DeleteWhere(ctx.Transaction, sql)
} }
// DeleteSpaceRoles deletes records from the labelrole table which have the given space ID. // DeleteSpaceRoles deletes records from the labelrole table which have the given space ID.
func DeleteSpaceRoles(s domain.StoreContext, spaceID string) (rows int64, err error) { func (s Scope) DeleteSpaceRoles(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s'", s.Context.OrgID, spaceID) sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID)
return b.DeleteWhere(s.Context.Transaction, sql) return b.DeleteWhere(ctx.Transaction, sql)
} }
// DeleteUserSpaceRoles removes all roles for the specified user, for the specified space. // DeleteUserSpaceRoles removes all roles for the specified user, for the specified space.
func DeleteUserSpaceRoles(s domain.StoreContext, spaceID, userID string) (rows int64, err error) { func (s Scope) DeleteUserSpaceRoles(ctx domain.RequestContext, spaceID, userID string) (rows int64, err error) {
b := mysql.BaseQuery{} b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s' AND userid='%s'", sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND labelid='%s' AND userid='%s'",
s.Context.OrgID, spaceID, userID) ctx.OrgID, spaceID, userID)
return b.DeleteWhere(s.Context.Transaction, sql) return b.DeleteWhere(ctx.Transaction, sql)
} }
// MoveSpaceRoles changes the space ID for space role records from previousLabel to newLabel. // MoveSpaceRoles changes the space ID for space role records from previousLabel to newLabel.
func MoveSpaceRoles(s domain.StoreContext, previousLabel, newLabel string) (err error) { func (s Scope) MoveSpaceRoles(ctx domain.RequestContext, previousLabel, newLabel string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE labelrole SET labelid=? WHERE labelid=? AND orgid=?") stmt, err := ctx.Transaction.Preparex("UPDATE labelrole SET labelid=? WHERE labelid=? AND orgid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -277,7 +283,7 @@ func MoveSpaceRoles(s domain.StoreContext, previousLabel, newLabel string) (err
return return
} }
_, err = stmt.Exec(newLabel, previousLabel, s.Context.OrgID) _, err = stmt.Exec(newLabel, previousLabel, ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute move space roles for label %s", previousLabel)) err = errors.Wrap(err, fmt.Sprintf("unable to execute move space roles for label %s", previousLabel))
} }

View file

@ -13,13 +13,7 @@
// Spaces in Documize contain documents. // Spaces in Documize contain documents.
package space package space
import ( /*
"database/sql"
"fmt"
"github.com/documize/community/domain"
)
// CanViewSpace returns if the user has permission to view the given spaceID. // CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(s domain.StoreContext, spaceID string) (hasPermission bool) { func CanViewSpace(s domain.StoreContext, spaceID string) (hasPermission bool) {
roles, err := GetRoles(s, spaceID) roles, err := GetRoles(s, spaceID)
@ -60,3 +54,4 @@ func CanViewSpaceDocuments(s domain.StoreContext, spaceID string) (hasPermission
return false return false
} }
*/

View file

@ -18,29 +18,30 @@ import (
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/uniqueid" "github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/account" "github.com/documize/community/model/account"
"github.com/documize/community/domain/user" "github.com/documize/community/model/space"
"github.com/documize/community/model/user"
) )
// addSpace prepares and creates space record. // addSpace prepares and creates space record.
func addSpace(s domain.StoreContext, sp Space) (err error) { func addSpace(ctx domain.RequestContext, s *domain.Store, sp space.Space) (err error) {
sp.Type = ScopePrivate sp.Type = space.ScopePrivate
sp.UserID = s.Context.UserID sp.UserID = ctx.UserID
err = Add(s, sp) err = s.Space.Add(ctx, sp)
if err != nil { if err != nil {
return return
} }
role := Role{} role := space.Role{}
role.LabelID = sp.RefID role.LabelID = sp.RefID
role.OrgID = sp.OrgID role.OrgID = sp.OrgID
role.UserID = s.Context.UserID role.UserID = ctx.UserID
role.CanEdit = true role.CanEdit = true
role.CanView = true role.CanView = true
role.RefID = uniqueid.Generate() role.RefID = uniqueid.Generate()
err = AddRole(s, role) err = s.Space.AddRole(ctx, role)
return return
} }
@ -49,8 +50,8 @@ func addSpace(s domain.StoreContext, sp Space) (err error) {
// We create the user account with default values and then take them // We create the user account with default values and then take them
// through a welcome process designed to capture profile data. // through a welcome process designed to capture profile data.
// We add them to the organization and grant them view-only folder access. // We add them to the organization and grant them view-only folder access.
func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy user.User, func inviteNewUserToSharedSpace(ctx domain.RequestContext, s *domain.Store, email string, invitedBy user.User,
baseURL string, sp Space, invitationMessage string) (err error) { baseURL string, sp space.Space, invitationMessage string) (err error) {
var u = user.User{} var u = user.User{}
u.Email = email u.Email = email
@ -62,7 +63,7 @@ func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy u
userID := uniqueid.Generate() userID := uniqueid.Generate()
u.RefID = userID u.RefID = userID
err = user.Add(s, u) err = s.User.Add(ctx, u)
if err != nil { if err != nil {
return return
} }
@ -70,28 +71,28 @@ func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy u
// Let's give this user access to the organization // Let's give this user access to the organization
var a account.Account var a account.Account
a.UserID = userID a.UserID = userID
a.OrgID = s.Context.OrgID a.OrgID = ctx.OrgID
a.Admin = false a.Admin = false
a.Editor = false a.Editor = false
a.Active = true a.Active = true
accountID := uniqueid.Generate() accountID := uniqueid.Generate()
a.RefID = accountID a.RefID = accountID
err = account.Add(s, a) err = s.Account.Add(ctx, a)
if err != nil { if err != nil {
return return
} }
role := Role{} role := space.Role{}
role.LabelID = sp.RefID role.LabelID = sp.RefID
role.OrgID = s.Context.OrgID role.OrgID = ctx.OrgID
role.UserID = userID role.UserID = userID
role.CanEdit = false role.CanEdit = false
role.CanView = true role.CanView = true
roleID := uniqueid.Generate() roleID := uniqueid.Generate()
role.RefID = roleID role.RefID = roleID
err = AddRole(s, role) err = s.Space.AddRole(ctx, role)
if err != nil { if err != nil {
return return
} }

118
domain/storer.go Normal file
View file

@ -0,0 +1,118 @@
// 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 domain ...
package domain
import (
"github.com/documize/community/model/account"
"github.com/documize/community/model/audit"
"github.com/documize/community/model/org"
"github.com/documize/community/model/pin"
"github.com/documize/community/model/space"
"github.com/documize/community/model/user"
)
// Store provides access to data store (database)
type Store struct {
Space SpaceStorer
User UserStorer
Account AccountStorer
Organization OrganizationStorer
Pin PinStorer
Audit AuditStorer
Document DocumentStorer
}
// SpaceStorer defines required methods for space management
type SpaceStorer interface {
Add(ctx RequestContext, sp space.Space) (err error)
Get(ctx RequestContext, id string) (sp space.Space, err error)
PublicSpaces(ctx RequestContext, orgID string) (sp []space.Space, err error)
GetAll(ctx RequestContext) (sp []space.Space, err error)
Update(ctx RequestContext, sp space.Space) (err error)
ChangeOwner(ctx RequestContext, currentOwner, newOwner string) (err error)
Viewers(ctx RequestContext) (v []space.Viewer, err error)
Delete(ctx RequestContext, id string) (rows int64, err error)
AddRole(ctx RequestContext, r space.Role) (err error)
GetRoles(ctx RequestContext, labelID string) (r []space.Role, err error)
GetUserRoles(ctx RequestContext) (r []space.Role, err error)
DeleteRole(ctx RequestContext, roleID string) (rows int64, err error)
DeleteSpaceRoles(ctx RequestContext, spaceID string) (rows int64, err error)
DeleteUserSpaceRoles(ctx RequestContext, spaceID, userID string) (rows int64, err error)
MoveSpaceRoles(ctx RequestContext, previousLabel, newLabel string) (err error)
}
// UserStorer defines required methods for user management
type UserStorer interface {
Add(ctx RequestContext, u user.User) (err error)
Get(ctx RequestContext, id string) (u user.User, err error)
GetByDomain(ctx RequestContext, domain, email string) (u user.User, err error)
GetByEmail(ctx RequestContext, email string) (u user.User, err error)
GetByToken(ctx RequestContext, token string) (u user.User, err error)
GetBySerial(ctx RequestContext, serial string) (u user.User, err error)
GetActiveUsersForOrganization(ctx RequestContext) (u []user.User, err error)
GetUsersForOrganization(ctx RequestContext) (u []user.User, err error)
GetSpaceUsers(ctx RequestContext, folderID string) (u []user.User, err error)
UpdateUser(ctx RequestContext, u user.User) (err error)
UpdateUserPassword(ctx RequestContext, userID, salt, password string) (err error)
DeactiveUser(ctx RequestContext, userID string) (err error)
ForgotUserPassword(ctx RequestContext, email, token string) (err error)
CountActiveUsers(ctx RequestContext) (c int)
}
// AccountStorer defines required methods for account management
type AccountStorer interface {
Add(ctx RequestContext, account account.Account) (err error)
GetUserAccount(ctx RequestContext, userID string) (account account.Account, err error)
GetUserAccounts(ctx RequestContext, userID string) (t []account.Account, err error)
GetAccountsByOrg(ctx RequestContext) (t []account.Account, err error)
DeleteAccount(ctx RequestContext, ID string) (rows int64, err error)
UpdateAccount(ctx RequestContext, account account.Account) (err error)
HasOrgAccount(ctx RequestContext, orgID, userID string) bool
CountOrgAccounts(ctx RequestContext) int
}
// OrganizationStorer defines required methods for organization management
type OrganizationStorer interface {
AddOrganization(ctx RequestContext, org org.Organization) error
GetOrganization(ctx RequestContext, id string) (org org.Organization, err error)
GetOrganizationByDomain(ctx RequestContext, subdomain string) (org org.Organization, err error)
UpdateOrganization(ctx RequestContext, org org.Organization) (err error)
DeleteOrganization(ctx RequestContext, orgID string) (rows int64, err error)
RemoveOrganization(ctx RequestContext, orgID string) (err error)
UpdateAuthConfig(ctx RequestContext, org org.Organization) (err error)
CheckDomain(ctx RequestContext, domain string) string
}
// PinStorer defines required methods for pin management
type PinStorer interface {
Add(ctx RequestContext, pin pin.Pin) (err error)
GetPin(ctx RequestContext, id string) (pin pin.Pin, err error)
GetUserPins(ctx RequestContext, userID string) (pins []pin.Pin, err error)
UpdatePin(ctx RequestContext, pin pin.Pin) (err error)
UpdatePinSequence(ctx RequestContext, pinID string, sequence int) (err error)
DeletePin(ctx RequestContext, id string) (rows int64, err error)
DeletePinnedSpace(ctx RequestContext, spaceID string) (rows int64, err error)
DeletePinnedDocument(ctx RequestContext, documentID string) (rows int64, err error)
}
// AuditStorer defines required methods for audit trails
type AuditStorer interface {
Record(ctx RequestContext, t audit.EventType)
}
// DocumentStorer defines required methods for document handling
type DocumentStorer interface {
MoveDocumentSpace(ctx RequestContext, id, move string) (err error)
}
// https://github.com/golang-sql/sqlexp/blob/c2488a8be21d20d31abf0d05c2735efd2d09afe4/quoter.go#L46

616
domain/user/endpoint.go Normal file
View file

@ -0,0 +1,616 @@
// 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 user
import (
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
Store domain.Store
}
/*
// AddUser is the endpoint that enables an administrator to add a new user for their orgaisation.
func (h *Handler) AddUser(w http.ResponseWriter, r *http.Request) {
method := "user.AddUser"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
}
if !s.Context.Administrator {
response.WriteForbiddenError(w)
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
userModel := model.User{}
err = json.Unmarshal(body, &userModel)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
// data validation
userModel.Email = strings.ToLower(strings.TrimSpace(userModel.Email))
userModel.Firstname = strings.TrimSpace(userModel.Firstname)
userModel.Lastname = strings.TrimSpace(userModel.Lastname)
userModel.Password = strings.TrimSpace(userModel.Password)
if len(userModel.Email) == 0 {
response.WriteMissingDataError(w, method, "email")
return
}
if len(userModel.Firstname) == 0 {
response.WriteMissingDataError(w, method, "firsrtname")
return
}
if len(userModel.Lastname) == 0 {
response.WriteMissingDataError(w, method, "lastname")
return
}
userModel.Initials = stringutil.MakeInitials(userModel.Firstname, userModel.Lastname)
requestedPassword := secrets.GenerateRandomPassword()
userModel.Salt = secrets.GenerateSalt()
userModel.Password = secrets.GeneratePassword(requestedPassword, userModel.Salt)
// only create account if not dupe
addUser := true
addAccount := true
var userID string
userDupe, err := h.Store.User.GetByEmail(s, userModel.Email)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if userModel.Email == userDupe.Email {
addUser = false
userID = userDupe.RefID
h.Runtime.Log.Info("Dupe user found, will not add " + userModel.Email)
}
s.Context.Transaction, err = request.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
if addUser {
userID = uniqueid.Generate()
userModel.RefID = userID
err = h.Store.User.Add(s, userModel)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
h.Runtime.Log.Info("Adding user")
} else {
AttachUserAccounts(s, s.Context.OrgID, &userDupe)
for _, a := range userDupe.Accounts {
if a.OrgID == s.Context.OrgID {
addAccount = false
h.Runtime.Log.Info("Dupe account found, will not add")
break
}
}
}
// set up user account for the org
if addAccount {
var a model.Account
a.RefID = uniqueid.Generate()
a.UserID = userID
a.OrgID = s.Context.OrgID
a.Editor = true
a.Admin = false
a.Active = true
err = account.Add(s, a)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
if addUser {
event.Handler().Publish(string(event.TypeAddUser))
eventing.Record(s, eventing.EventTypeUserAdd)
}
if addAccount {
event.Handler().Publish(string(event.TypeAddAccount))
eventing.Record(s, eventing.EventTypeAccountAdd)
}
s.Context.Transaction.Commit()
// If we did not add user or give them access (account) then we error back
if !addUser && !addAccount {
response.WriteDuplicateError(w, method, "user")
return
}
// Invite new user
inviter, err := h.Store.User.Get(s, s.Context.UserID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
// Prepare invitation email (that contains SSO link)
if addUser && addAccount {
size := len(requestedPassword)
auth := fmt.Sprintf("%s:%s:%s", s.Context.AppURL, userModel.Email, requestedPassword[:size])
encrypted := secrets.EncodeBase64([]byte(auth))
url := fmt.Sprintf("%s/%s", s.Context.GetAppURL("auth/sso"), url.QueryEscape(string(encrypted)))
go mail.InviteNewUser(userModel.Email, inviter.Fullname(), url, userModel.Email, requestedPassword)
h.Runtime.Log.Info(fmt.Sprintf("%s invited by %s on %s", userModel.Email, inviter.Email, s.Context.AppURL))
} else {
go mail.InviteExistingUser(userModel.Email, inviter.Fullname(), s.Context.GetAppURL(""))
h.Runtime.Log.Info(fmt.Sprintf("%s is giving access to an existing user %s", inviter.Email, userModel.Email))
}
response.WriteJSON(w, userModel)
}
/*
// GetOrganizationUsers is the endpoint that allows administrators to view the users in their organisation.
func (h *Handler) GetOrganizationUsers(w http.ResponseWriter, r *http.Request) {
method := "pin.GetUserPins"
s := domain.NewContext(h.Runtime, r)
if !s.Context.Editor && !s.Context.Administrator {
response.WriteForbiddenError(w)
return
}
active, err := strconv.ParseBool(request.Query("active"))
if err != nil {
active = false
}
u := []User{}
if active {
u, err = GetActiveUsersForOrganization(s)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
} else {
u, err = GetUsersForOrganization(s)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
}
if len(u) == 0 {
u = []User{}
}
for i := range u {
AttachUserAccounts(s, s.Context.OrgID, &u[i])
}
response.WriteJSON(w, u)
}
// GetSpaceUsers returns every user within a given space
func (h *Handler) GetSpaceUsers(w http.ResponseWriter, r *http.Request) {
method := "user.GetSpaceUsers"
s := domain.NewContext(h.Runtime, r)
var u []User
var err error
folderID := request.Param("folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
// check to see space type as it determines user selection criteria
folder, err := space.Get(s, folderID)
if err != nil && err != sql.ErrNoRows {
h.Runtime.Log.Error("cannot get space", err)
response.WriteJSON(w, u)
return
}
switch folder.Type {
case entity.FolderTypePublic:
u, err = GetActiveUsersForOrganization(s)
break
case entity.FolderTypePrivate:
// just me
var me User
user, err = Get(s, s.Context.UserID)
u = append(u, me)
break
case entity.FolderTypeRestricted:
u, err = GetSpaceUsers(s, folderID)
break
}
if len(u) == 0 {
u = []User
}
if err != nil && err != sql.ErrNoRows {
h.Runtime.Log.Error("cannot get users for space", err)
response.WriteJSON(w, u)
return
}
response.WriteJSON(w, u)
}
// GetUser returns user specified by ID
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
method := "user.GetUser"
s := domain.NewContext(h.Runtime, r)
userID := request.Param("userID")
if len(userID) == 0 {
response.WriteMissingDataError(w, method, "userId")
return
}
if userID != s.Context.UserID {
response.WriteBadRequestError(w, method, "userId mismatch")
return
}
u, err := GetSecuredUser(s, s.Context.OrgID, userID)
if err == sql.ErrNoRows {
response.WriteNotFoundError(s, method, s.Context.UserID)
return
}
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(u)
// DeleteUser is the endpoint to delete a user specified by userID, the caller must be an Administrator.
func (h *Handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
method := "user.DeleteUser"
s := domain.NewContext(h.Runtime, r)
if !s.Context.Administrator {
response.WriteForbiddenError(w)
return
}
userID := response.Params("userID")
if len(userID) == 0 {
response.WriteMissingDataError(w, method, "userID")
return
}
if userID == s.Context.UserID {
response.WriteBadRequestError(w, method, "cannot delete self")
return
}
var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = DeactiveUser(s, userID)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = space.ChangeLabelOwner(s, userID, s.Context.UserID)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeUserDelete)
event.Handler().Publish(string(event.TypeRemoveUser))
s.Context.Transaction.Commit()
response.WriteEmpty()
}
// UpdateUser is the endpoint to update user information for the given userID.
// Note that unless they have admin privildges, a user can only update their own information.
// Also, only admins can update user roles in organisations.
func (h *Handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
method := "user.DeleteUser"
s := domain.NewContext(h.Runtime, r)
userID := request.Param("userID")
if len(userID) == 0 {
response.WriteBadRequestError(w, method, "user id must be numeric")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WritePayloadError(w, method, err)
return
}
u := User{}
err = json.Unmarshal(body, &u)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
// can only update your own account unless you are an admin
if s.Context.UserID != userID && !s.Context.Administrator {
response.WriteForbiddenError(w)
return
}
// can only update your own account unless you are an admin
if len(u.Email) == 0 {
response.WriteMissingDataError(w, method, "email")
return
}
s.Context.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
u.RefID = userID
u.Initials = stringutil.MakeInitials(u.Firstname, u.Lastname)
err = UpdateUser(s, u)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
// Now we update user roles for this organization.
// That means we have to first find their account record
// for this organization.
a, err := account.GetUserAccount(s, userID)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
a.Editor = u.Editor
a.Admin = u.Admin
a.Active = u.Active
err = account.UpdateAccount(s, account)
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeUserUpdate)
s.Context.Transaction.Commit()
response.WriteJSON(u)
}
// ChangeUserPassword accepts password change from within the app.
func (h *Handler) ChangeUserPassword(w http.ResponseWriter, r *http.Request) {
method := "user.ChangeUserPassword"
s := domain.NewContext(h.Runtime, r)
userID := response.Param("userID")
if len(userID) == 0 {
response.WriteMissingDataError(w, method, "user id")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
return
}
newPassword := string(body)
// can only update your own account unless you are an admin
if userID != s.Context.UserID && !s.Context.Administrator {
response.WriteForbiddenError(w)
return
}
s.Context.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
u, err := Get(s, userID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
u.Salt = secrets.GenerateSalt()
err = UpdateUserPassword(s, userID, user.Salt, secrets.GeneratePassword(newPassword, user.Salt))
if err != nil {
response.WriteServerError(w, method, err)
return
}
s.Context.Transaction.Rollback()
response.WriteEmpty(w)
}
// GetUserFolderPermissions returns folder permission for authenticated user.
func (h *Handler) GetUserFolderPermissions(w http.ResponseWriter, r *http.Request) {
method := "user.ChangeUserPassword"
s := domain.NewContext(h.Runtime, r)
userID := request.Param("userID")
if userID != p.Context.UserID {
response.WriteForbiddenError(w)
return
}
roles, err := space.GetUserLabelRoles(s, userID)
if err == sql.ErrNoRows {
err = nil
roles = []space.Role{}
}
if err != nil {
response.WriteServerError(w, method, err)
return
}
response.WriteJSON(w, roles)
}
// ForgotUserPassword initiates the change password procedure.
// Generates a reset token and sends email to the user.
// User has to click link in email and then provide a new password.
func (h *Handler) ForgotUserPassword(w http.ResponseWriter, r *http.Request) {
method := "user.ForgotUserPassword"
s := domain.NewContext(h.Runtime, r)
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "cannot ready payload")
return
}
u := new(User)
err = json.Unmarshal(body, &u)
if err != nil {
response.WriteBadRequestError(w, method, "JSON body")
return
}
s.Context.Transaction, err = request.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
token := secrets.GenerateSalt()
err = ForgotUserPassword(s, u.Email, token)
if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
if err == sql.ErrNoRows {
response.WriteEmpty(w)
h.Runtime.Log.Info(fmt.Errorf("User %s not found for password reset process", u.Email))
return
}
s.Context.Transaction.Commit()
appURL := s.Context.GetAppURL(fmt.Sprintf("auth/reset/%s", token))
go mail.PasswordReset(u.Email, appURL)
response.WriteEmpty(w)
}
// ResetUserPassword stores the newly chosen password for the user.
func (h *Handler) ResetUserPassword(w http.ResponseWriter, r *http.Request) {
method := "user.ForgotUserPassword"
s := domain.NewContext(h.Runtime, r)
token := request.Param("token")
if len(token) == 0 {
response.WriteMissingDataError(w, method, "missing token")
return
}
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, "JSON body")
return
}
newPassword := string(body)
s.Context.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
u, err := GetByToken(token)
if err != nil {
response.WriteServerError(w, method, err)
return
}
user.Salt = secrets.GenerateSalt()
err = UpdateUserPassword(s, u.RefID, u.Salt, secrets.GeneratePassword(newPassword, u.Salt))
if err != nil {
s.Context.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeUserPasswordReset)
s.Context.Transaction.Commit()
response.WriteEmpty(w)
}
*/

View file

@ -9,7 +9,7 @@
// //
// https://documize.com // https://documize.com
package user package mysql
import ( import (
"database/sql" "database/sql"
@ -17,17 +17,24 @@ import (
"strings" "strings"
"time" "time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/model/user"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add adds the given user record to the user table. // Add adds the given user record to the user table.
func Add(s domain.StoreContext, u User) (err error) { func (s Scope) Add(ctx domain.RequestContext, u user.User) (err error) {
u.Created = time.Now().UTC() u.Created = time.Now().UTC()
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
stmt, err := s.Context.Transaction.Preparex("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -45,7 +52,7 @@ func Add(s domain.StoreContext, u User) (err error) {
} }
// Get returns the user record for the given id. // Get returns the user record for the given id.
func Get(s domain.StoreContext, id string) (u User, err error) { func (s Scope) Get(ctx domain.RequestContext, id string) (u user.User, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE refid=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -64,7 +71,7 @@ func Get(s domain.StoreContext, id string) (u User, err error) {
} }
// GetByDomain matches user by email and domain. // GetByDomain matches user by email and domain.
func GetByDomain(s domain.StoreContext, domain, email string) (u User, err error) { func (s Scope) GetByDomain(ctx domain.RequestContext, domain, email string) (u user.User, err error) {
email = strings.TrimSpace(strings.ToLower(email)) email = strings.TrimSpace(strings.ToLower(email))
stmt, err := s.Runtime.Db.Preparex("SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?") stmt, err := s.Runtime.Db.Preparex("SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?")
@ -85,7 +92,7 @@ func GetByDomain(s domain.StoreContext, domain, email string) (u User, err error
} }
// GetByEmail returns a single row match on email. // GetByEmail returns a single row match on email.
func GetByEmail(s domain.StoreContext, email string) (u User, err error) { func (s Scope) GetByEmail(ctx domain.RequestContext, email string) (u user.User, err error) {
email = strings.TrimSpace(strings.ToLower(email)) email = strings.TrimSpace(strings.ToLower(email))
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE TRIM(LOWER(email))=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE TRIM(LOWER(email))=?")
@ -106,7 +113,7 @@ func GetByEmail(s domain.StoreContext, email string) (u User, err error) {
} }
// GetByToken returns a user record given a reset token value. // GetByToken returns a user record given a reset token value.
func GetByToken(s domain.StoreContext, token string) (u User, err error) { func (s Scope) GetByToken(ctx domain.RequestContext, token string) (u user.User, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE reset=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE reset=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -127,7 +134,7 @@ func GetByToken(s domain.StoreContext, token string) (u User, err error) {
// GetBySerial is used to retrieve a user via their temporary password salt value! // GetBySerial is used to retrieve a user via their temporary password salt value!
// This occurs when we you share a folder with a new user and they have to complete // This occurs when we you share a folder with a new user and they have to complete
// the onboarding process. // the onboarding process.
func GetBySerial(s domain.StoreContext, serial string) (u User, err error) { func (s Scope) GetBySerial(ctx domain.RequestContext, serial string) (u user.User, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE salt=?") stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE salt=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -147,15 +154,15 @@ func GetBySerial(s domain.StoreContext, serial string) (u User, err error) {
// GetActiveUsersForOrganization returns a slice containing of active user records for the organization // GetActiveUsersForOrganization returns a slice containing of active user records for the organization
// identified in the Persister. // identified in the Persister.
func GetActiveUsersForOrganization(s domain.StoreContext) (u []User, err error) { func (s Scope) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []user.User, err error) {
err = s.Runtime.Db.Select(&u, err = s.Runtime.Db.Select(&u,
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised `SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised
FROM user u FROM user u
WHERE u.refid IN (SELECT userid FROM account WHERE orgid = ? AND active=1) ORDER BY u.firstname,u.lastname`, WHERE u.refid IN (SELECT userid FROM account WHERE orgid = ? AND active=1) ORDER BY u.firstname,u.lastname`,
s.Context.OrgID) ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("get active users by org %s", s.Context.OrgID)) err = errors.Wrap(err, fmt.Sprintf("get active users by org %s", ctx.OrgID))
return return
} }
@ -164,12 +171,12 @@ func GetActiveUsersForOrganization(s domain.StoreContext) (u []User, err error)
// GetUsersForOrganization returns a slice containing all of the user records for the organizaiton // GetUsersForOrganization returns a slice containing all of the user records for the organizaiton
// identified in the Persister. // identified in the Persister.
func GetUsersForOrganization(s domain.StoreContext) (u []User, err error) { func (s Scope) GetUsersForOrganization(ctx domain.RequestContext) (u []user.User, err error) {
err = s.Runtime.Db.Select(&u, err = s.Runtime.Db.Select(&u,
"SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, created, revised FROM user WHERE refid IN (SELECT userid FROM account where orgid = ?) ORDER BY firstname,lastname", s.Context.OrgID) "SELECT id, refid, firstname, lastname, email, initials, password, salt, reset, created, revised FROM user WHERE refid IN (SELECT userid FROM account where orgid = ?) ORDER BY firstname,lastname", ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf(" get users for org %s", s.Context.OrgID)) err = errors.Wrap(err, fmt.Sprintf(" get users for org %s", ctx.OrgID))
return return
} }
@ -177,17 +184,17 @@ func GetUsersForOrganization(s domain.StoreContext) (u []User, err error) {
} }
// GetSpaceUsers returns a slice containing all user records for given folder. // GetSpaceUsers returns a slice containing all user records for given folder.
func GetSpaceUsers(s domain.StoreContext, folderID string) (u []User, err error) { func (s Scope) GetSpaceUsers(ctx domain.RequestContext, folderID string) (u []user.User, err error) {
err = s.Runtime.Db.Select(&u, err = s.Runtime.Db.Select(&u,
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised `SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised
FROM user u, account a FROM user u, account a
WHERE u.refid IN (SELECT userid from labelrole WHERE orgid=? AND labelid=?) WHERE u.refid IN (SELECT userid from labelrole WHERE orgid=? AND labelid=?)
AND a.orgid=? AND u.refid = a.userid AND a.active=1 AND a.orgid=? AND u.refid = a.userid AND a.active=1
ORDER BY u.firstname, u.lastname`, ORDER BY u.firstname, u.lastname`,
s.Context.OrgID, folderID, s.Context.OrgID) ctx.OrgID, folderID, ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("get space users for org %s", s.Context.OrgID)) err = errors.Wrap(err, fmt.Sprintf("get space users for org %s", ctx.OrgID))
return return
} }
@ -195,11 +202,11 @@ func GetSpaceUsers(s domain.StoreContext, folderID string) (u []User, err error)
} }
// UpdateUser updates the user table using the given replacement user record. // UpdateUser updates the user table using the given replacement user record.
func UpdateUser(s domain.StoreContext, u User) (err error) { func (s Scope) UpdateUser(ctx domain.RequestContext, u user.User) (err error) {
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
u.Email = strings.ToLower(u.Email) u.Email = strings.ToLower(u.Email)
stmt, err := s.Context.Transaction.PrepareNamed( stmt, err := ctx.Transaction.PrepareNamed(
"UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, initials=:initials WHERE refid=:refid") "UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, initials=:initials WHERE refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
@ -218,8 +225,8 @@ func UpdateUser(s domain.StoreContext, u User) (err error) {
} }
// UpdateUserPassword updates a user record with new password and salt values. // UpdateUserPassword updates a user record with new password and salt values.
func UpdateUserPassword(s domain.StoreContext, userID, salt, password string) (err error) { func (s Scope) UpdateUserPassword(ctx domain.RequestContext, userID, salt, password string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE user SET salt=?, password=?, reset='' WHERE refid=?") stmt, err := ctx.Transaction.Preparex("UPDATE user SET salt=?, password=?, reset='' WHERE refid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -237,8 +244,8 @@ func UpdateUserPassword(s domain.StoreContext, userID, salt, password string) (e
} }
// DeactiveUser deletes the account record for the given userID and persister.Context.OrgID. // DeactiveUser deletes the account record for the given userID and persister.Context.OrgID.
func DeactiveUser(s domain.StoreContext, userID string) (err error) { func (s Scope) DeactiveUser(ctx domain.RequestContext, userID string) (err error) {
stmt, err := s.Context.Transaction.Preparex("DELETE FROM account WHERE userid=? and orgid=?") stmt, err := ctx.Transaction.Preparex("DELETE FROM account WHERE userid=? and orgid=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -246,7 +253,7 @@ func DeactiveUser(s domain.StoreContext, userID string) (err error) {
return return
} }
_, err = stmt.Exec(userID, s.Context.OrgID) _, err = stmt.Exec(userID, ctx.OrgID)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute user deactivation") err = errors.Wrap(err, "execute user deactivation")
@ -257,8 +264,8 @@ func DeactiveUser(s domain.StoreContext, userID string) (err error) {
} }
// ForgotUserPassword sets the password to '' and the reset field to token, for a user identified by email. // ForgotUserPassword sets the password to '' and the reset field to token, for a user identified by email.
func ForgotUserPassword(s domain.StoreContext, email, token string) (err error) { func (s Scope) ForgotUserPassword(ctx domain.RequestContext, email, token string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE user SET reset=?, password='' WHERE LOWER(email)=?") stmt, err := ctx.Transaction.Preparex("UPDATE user SET reset=?, password='' WHERE LOWER(email)=?")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -276,7 +283,7 @@ func ForgotUserPassword(s domain.StoreContext, email, token string) (err error)
} }
// CountActiveUsers returns the number of active users in the system. // CountActiveUsers returns the number of active users in the system.
func CountActiveUsers(s domain.StoreContext) (c int) { func (s Scope) CountActiveUsers(ctx domain.RequestContext) (c int) {
row := s.Runtime.Db.QueryRow("SELECT count(*) FROM user u WHERE u.refid IN (SELECT userid FROM account WHERE active=1)") row := s.Runtime.Db.QueryRow("SELECT count(*) FROM user u WHERE u.refid IN (SELECT userid FROM account WHERE active=1)")
err := row.Scan(&c) err := row.Scan(&c)

View file

@ -13,23 +13,23 @@ package user
import ( import (
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/account" "github.com/documize/community/model/user"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// GetSecuredUser contain associated accounts but credentials are wiped. // GetSecuredUser contain associated accounts but credentials are wiped.
func GetSecuredUser(s domain.StoreContext, orgID, q string) (u User, err error) { func GetSecuredUser(ctx domain.RequestContext, s domain.Store, orgID, q string) (u user.User, err error) {
u, err = Get(s, q) u, err = s.User.Get(ctx, q)
AttachUserAccounts(s, orgID, &u) AttachUserAccounts(ctx, s, orgID, &u)
return return
} }
// AttachUserAccounts attachs user accounts to user object. // AttachUserAccounts attachs user accounts to user object.
func AttachUserAccounts(s domain.StoreContext, orgID string, u *User) { func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string, u *user.User) {
u.ProtectSecrets() u.ProtectSecrets()
a, err := account.GetUserAccounts(s, u.RefID) a, err := s.Account.GetUserAccounts(ctx, u.RefID)
if err != nil { if err != nil {
err = errors.Wrap(err, "fetch user accounts") err = errors.Wrap(err, "fetch user accounts")
return return

View file

@ -19,11 +19,12 @@ import (
"github.com/documize/community/core/database" "github.com/documize/community/core/database"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/domain"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
// InitRuntime prepares runtime using command line and environment variables. // InitRuntime prepares runtime using command line and environment variables.
func InitRuntime(r *env.Runtime) bool { func InitRuntime(r *env.Runtime, s *domain.Store) bool {
// We need SALT to hash auth JWT tokens // We need SALT to hash auth JWT tokens
if r.Flags.Salt == "" { if r.Flags.Salt == "" {
r.Flags.Salt = secrets.RandSalt() r.Flags.Salt = secrets.RandSalt()
@ -76,6 +77,9 @@ func InitRuntime(r *env.Runtime) bool {
} }
} }
// setup store based upon database type
AttachStore(r, s)
return true return true
} }

36
edition/boot/store.go Normal file
View file

@ -0,0 +1,36 @@
// 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 boot prepares runtime environment.
package boot
import (
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
account "github.com/documize/community/domain/account/mysql"
audit "github.com/documize/community/domain/audit/mysql"
org "github.com/documize/community/domain/organization/mysql"
pin "github.com/documize/community/domain/pin/mysql"
space "github.com/documize/community/domain/space/mysql"
user "github.com/documize/community/domain/user/mysql"
doc "github.com/documize/community/domain/document/mysql"
)
// AttachStore selects database persistence layer
func AttachStore(r *env.Runtime, s *domain.Store) {
s.Space = space.Scope{Runtime: r}
s.Account = account.Scope{Runtime: r}
s.Organization = org.Scope{Runtime: r}
s.User = user.Scope{Runtime: r}
s.Pin = pin.Scope{Runtime: r}
s.Audit = audit.Scope{Runtime: r}
s.Document = doc.Scope{Runtime: r}
}

View file

@ -24,6 +24,7 @@ import (
_ "github.com/documize/community/embed" // the compressed front-end code and static data _ "github.com/documize/community/embed" // the compressed front-end code and static data
"github.com/documize/community/server" "github.com/documize/community/server"
_ "github.com/go-sql-driver/mysql" // the mysql driver is required behind the scenes _ "github.com/go-sql-driver/mysql" // the mysql driver is required behind the scenes
"github.com/documize/community/domain"
) )
var rt env.Runtime var rt env.Runtime
@ -49,9 +50,12 @@ func main() {
rt.Product.License.Trial = false rt.Product.License.Trial = false
rt.Product.License.Edition = "Community" rt.Product.License.Edition = "Community"
// setup store
s := domain.Store{}
// parse settings from command line and environment // parse settings from command line and environment
rt.Flags = env.ParseFlags() rt.Flags = env.ParseFlags()
flagsOK := boot.InitRuntime(&rt) flagsOK := boot.InitRuntime(&rt, &s)
if flagsOK { if flagsOK {
// runtime.Log = runtime.Log.SetDB(runtime.Db) // runtime.Log = runtime.Log.SetDB(runtime.Db)
@ -65,5 +69,5 @@ func main() {
section.Register(rt) section.Register(rt)
ready := make(chan struct{}, 1) // channel signals router ready ready := make(chan struct{}, 1) // channel signals router ready
server.Start(&rt, ready) server.Start(&rt, &s, ready)
} }

View file

@ -11,19 +11,11 @@
package account package account
import ( import "github.com/documize/community/model"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
}
// Account links a User to an Organization. // Account links a User to an Organization.
type Account struct { type Account struct {
domain.BaseEntity model.BaseEntity
Admin bool `json:"admin"` Admin bool `json:"admin"`
Editor bool `json:"editor"` Editor bool `json:"editor"`
UserID string `json:"userId"` UserID string `json:"userId"`

View file

@ -10,7 +10,7 @@
// https://documize.com // https://documize.com
// Package eventing records and propagates events based on user actions. // Package eventing records and propagates events based on user actions.
package eventing package audit
import "time" import "time"

View file

@ -9,8 +9,8 @@
// //
// https://documize.com // https://documize.com
// Package domain ... // Package model ...
package domain package model
import ( import (
"time" "time"

View file

@ -9,21 +9,13 @@
// //
// https://documize.com // https://documize.com
package organization package org
import ( import "github.com/documize/community/model"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
}
// Organization defines a company that uses this app. // Organization defines a company that uses this app.
type Organization struct { type Organization struct {
domain.BaseEntity model.BaseEntity
Company string `json:"-"` Company string `json:"-"`
Title string `json:"title"` Title string `json:"title"`
Message string `json:"message"` Message string `json:"message"`

View file

@ -11,19 +11,11 @@
package pin package pin
import ( import "github.com/documize/community/model"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
}
// Pin defines a saved link to a document or space // Pin defines a saved link to a document or space
type Pin struct { type Pin struct {
domain.BaseEntity model.BaseEntity
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
UserID string `json:"userId"` UserID string `json:"userId"`
FolderID string `json:"folderId"` FolderID string `json:"folderId"`

View file

@ -9,23 +9,13 @@
// //
// https://documize.com // https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space package space
import ( import "github.com/documize/community/model"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
)
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
}
// Space defines a container for documents. // Space defines a container for documents.
type Space struct { type Space struct {
domain.BaseEntity model.BaseEntity
Name string `json:"name"` Name string `json:"name"`
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
UserID string `json:"userId"` UserID string `json:"userId"`
@ -63,7 +53,7 @@ func (l *Space) IsRestricted() bool {
// Role determines user permissions for a folder. // Role determines user permissions for a folder.
type Role struct { type Role struct {
domain.BaseEntityObfuscated model.BaseEntityObfuscated
OrgID string `json:"-"` OrgID string `json:"-"`
LabelID string `json:"folderId"` LabelID string `json:"folderId"`
UserID string `json:"userId"` UserID string `json:"userId"`

View file

@ -14,19 +14,13 @@ package user
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/env" "github.com/documize/community/model"
"github.com/documize/community/domain" "github.com/documize/community/model/account"
"github.com/documize/community/domain/account"
) )
// Handler contains the runtime information such as logging and database.
type Handler struct {
Runtime *env.Runtime
}
// User defines a login. // User defines a login.
type User struct { type User struct {
domain.BaseEntity model.BaseEntity
Firstname string `json:"firstname"` Firstname string `json:"firstname"`
Lastname string `json:"lastname"` Lastname string `json:"lastname"`
Email string `json:"email"` Email string `json:"email"`

View file

@ -25,10 +25,12 @@ import (
"github.com/documize/community/domain/auth" "github.com/documize/community/domain/auth"
"github.com/documize/community/domain/organization" "github.com/documize/community/domain/organization"
"github.com/documize/community/domain/user" "github.com/documize/community/domain/user"
"github.com/documize/community/model/org"
) )
type middleware struct { type middleware struct {
Runtime *env.Runtime Runtime *env.Runtime
Store *domain.Store
} }
func (m *middleware) cors(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { func (m *middleware) cors(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
@ -65,8 +67,6 @@ func (m *middleware) metrics(w http.ResponseWriter, r *http.Request, next http.H
func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
method := "Authorize" method := "Authorize"
s := domain.StoreContext{Runtime: m.Runtime, Context: domain.RequestContext{}}
// Let certain requests pass straight through // Let certain requests pass straight through
authenticated := preAuthorizeStaticAssets(m.Runtime, r) authenticated := preAuthorizeStaticAssets(m.Runtime, r)
@ -74,13 +74,15 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
token := auth.FindJWT(r) token := auth.FindJWT(r)
rc, _, tokenErr := auth.DecodeJWT(m.Runtime, token) rc, _, tokenErr := auth.DecodeJWT(m.Runtime, token)
var org = organization.Organization{} var org = org.Organization{}
var err = errors.New("") var err = errors.New("")
if len(rc.OrgID) == 0 { if len(rc.OrgID) == 0 {
org, err = organization.GetOrganizationByDomain(s, organization.GetRequestSubdomain(s, r)) dom := organization.GetRequestSubdomain(r)
dom = m.Store.Organization.CheckDomain(rc, dom)
org, err = m.Store.Organization.GetOrganizationByDomain(rc, dom)
} else { } else {
org, err = organization.GetOrganization(s, rc.OrgID) org, err = m.Store.Organization.GetOrganization(rc, rc.OrgID)
} }
// Inability to find org record spells the end of this request. // Inability to find org record spells the end of this request.
@ -96,8 +98,8 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
} }
rc.Subdomain = org.Domain rc.Subdomain = org.Domain
dom := organization.GetSubdomainFromHost(s, r) dom := organization.GetSubdomainFromHost(r)
dom2 := organization.GetRequestSubdomain(s, r) dom2 := organization.GetRequestSubdomain(r)
if org.Domain != dom && org.Domain != dom2 { if org.Domain != dom && org.Domain != dom2 {
m.Runtime.Log.Info(fmt.Sprintf("domain mismatch %s vs. %s vs. %s", dom, dom2, org.Domain)) m.Runtime.Log.Info(fmt.Sprintf("domain mismatch %s vs. %s vs. %s", dom, dom2, org.Domain))
@ -130,7 +132,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
rc.Editor = false rc.Editor = false
rc.Global = false rc.Global = false
rc.AppURL = r.Host rc.AppURL = r.Host
rc.Subdomain = organization.GetSubdomainFromHost(s, r) rc.Subdomain = organization.GetSubdomainFromHost(r)
rc.SSL = r.TLS != nil rc.SSL = r.TLS != nil
// get user IP from request // get user IP from request
@ -148,8 +150,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
// Fetch user permissions for this org // Fetch user permissions for this org
if rc.Authenticated { if rc.Authenticated {
u, err := user.GetSecuredUser(s, org.RefID, rc.UserID) u, err := user.GetSecuredUser(rc, *m.Store, org.RefID, rc.UserID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return

View file

@ -17,11 +17,15 @@ import (
"github.com/documize/community/core/api" "github.com/documize/community/core/api"
"github.com/documize/community/core/api/endpoint" "github.com/documize/community/core/api/endpoint"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/pin"
"github.com/documize/community/domain/space"
"github.com/documize/community/server/web" "github.com/documize/community/server/web"
) )
// RegisterEndpoints register routes for serving API endpoints // RegisterEndpoints register routes for serving API endpoints
func RegisterEndpoints(rt *env.Runtime) { func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
//************************************************** //**************************************************
// Non-secure routes // Non-secure routes
//************************************************** //**************************************************
@ -75,20 +79,22 @@ func RegisterEndpoints(rt *env.Runtime) {
Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/copy/{targetID}", []string{"POST", "OPTIONS"}, nil, endpoint.CopyPage) Add(rt, RoutePrefixPrivate, "documents/{documentID}/pages/{pageID}/copy/{targetID}", []string{"POST", "OPTIONS"}, nil, endpoint.CopyPage)
// Organization // Organization
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetOrganization) organization := organization.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, endpoint.UpdateOrganization) Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, organization.Get)
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
// Folder // Space
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"DELETE", "OPTIONS"}, nil, endpoint.DeleteFolder) space := space.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "folders/{folderID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, endpoint.RemoveFolder) Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"PUT", "OPTIONS"}, nil, endpoint.SetFolderPermissions) Add(rt, RoutePrefixPrivate, "folders/{folderID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolderPermissions) Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"PUT", "OPTIONS"}, nil, space.SetPermissions)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/invitation", []string{"POST", "OPTIONS"}, nil, endpoint.InviteToFolder) Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"GET", "OPTIONS"}, nil, space.GetPermissions)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, endpoint.GetFolderVisibility) Add(rt, RoutePrefixPrivate, "folders/{folderID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
Add(rt, RoutePrefixPrivate, "folders", []string{"POST", "OPTIONS"}, nil, endpoint.AddFolder) Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, space.GetSpaceViewers)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolders) Add(rt, RoutePrefixPrivate, "folders", []string{"POST", "OPTIONS"}, nil, space.Add)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolder) Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, nil, space.GetAll)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"PUT", "OPTIONS"}, nil, endpoint.UpdateFolder) Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"GET", "OPTIONS"}, nil, space.Get)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"PUT", "OPTIONS"}, nil, space.Update)
// Users // Users
Add(rt, RoutePrefixPrivate, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, endpoint.ChangeUserPassword) Add(rt, RoutePrefixPrivate, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, endpoint.ChangeUserPassword)
@ -136,10 +142,11 @@ func RegisterEndpoints(rt *env.Runtime) {
Add(rt, RoutePrefixPrivate, "global/auth", []string{"PUT", "OPTIONS"}, nil, endpoint.SaveAuthConfig) Add(rt, RoutePrefixPrivate, "global/auth", []string{"PUT", "OPTIONS"}, nil, endpoint.SaveAuthConfig)
// Pinned items // Pinned items
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, endpoint.AddPin) pin := pin.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetUserPins) Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, pin.Add)
Add(rt, RoutePrefixPrivate, "pin/{userID}/sequence", []string{"POST", "OPTIONS"}, nil, endpoint.UpdatePinSequence) Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, pin.GetUserPins)
Add(rt, RoutePrefixPrivate, "pin/{userID}/{pinID}", []string{"DELETE", "OPTIONS"}, nil, endpoint.DeleteUserPin) Add(rt, RoutePrefixPrivate, "pin/{userID}/sequence", []string{"POST", "OPTIONS"}, nil, pin.UpdatePinSequence)
Add(rt, RoutePrefixPrivate, "pin/{userID}/{pinID}", []string{"DELETE", "OPTIONS"}, nil, pin.DeleteUserPin)
// Single page app handler // Single page app handler
Add(rt, RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, endpoint.GetRobots) Add(rt, RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, endpoint.GetRobots)

View file

@ -22,6 +22,7 @@ import (
"github.com/documize/community/core/api/plugins" "github.com/documize/community/core/api/plugins"
"github.com/documize/community/core/database" "github.com/documize/community/core/database"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/server/routing" "github.com/documize/community/server/routing"
"github.com/documize/community/server/web" "github.com/documize/community/server/web"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -30,8 +31,7 @@ import (
var testHost string // used during automated testing var testHost string // used during automated testing
// Start router to handle all HTTP traffic. // Start router to handle all HTTP traffic.
func Start(rt *env.Runtime, ready chan struct{}) { func Start(rt *env.Runtime, s *domain.Store, ready chan struct{}) {
err := plugins.LibSetup() err := plugins.LibSetup()
if err != nil { if err != nil {
rt.Log.Error("Terminating before running - invalid plugin.json", err) rt.Log.Error("Terminating before running - invalid plugin.json", err)
@ -54,10 +54,10 @@ func Start(rt *env.Runtime, ready chan struct{}) {
} }
// define middleware // define middleware
cm := middleware{Runtime: rt} cm := middleware{Runtime: rt, Store: s}
// define API endpoints // define API endpoints
routing.RegisterEndpoints(rt) routing.RegisterEndpoints(rt, s)
// wire up API endpoints // wire up API endpoints
router := mux.NewRouter() router := mux.NewRouter()