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)
}
// 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) {
u, err = p.GetUser(user)
AttachUserAccounts(p, orgID, &u)
@ -726,6 +726,7 @@ func GetSecuredUser(p request.Persister, orgID, user string) (u entity.User, err
return
}
// AttachUserAccounts ...
func AttachUserAccounts(p request.Persister, orgID string, user *entity.User) {
user.ProtectSecrets()
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 (
"database/sql"
"fmt"
"time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/account"
"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.
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.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)
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.
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=?")
defer streamutil.Close(stmt)
@ -44,7 +62,7 @@ func GetUserAccount(s domain.StoreContext, userID string) (account Account, err
return
}
err = stmt.Get(&account, s.Context.OrgID, userID)
err = stmt.Get(&account, ctx.OrgID, userID)
if err != sql.ErrNoRows && err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select for account by user %s", userID))
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.
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)
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.
func GetAccountsByOrg(s domain.StoreContext) (t []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)
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", ctx.OrgID)
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
}
// CountOrgAccounts returns the numnber of active user accounts for specified organization.
func CountOrgAccounts(s domain.StoreContext) (c int) {
row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? AND active=1", s.Context.OrgID)
func (s Scope) CountOrgAccounts(ctx domain.RequestContext) (c int) {
row := s.Runtime.Db.QueryRow("SELECT count(*) FROM account WHERE orgid=? AND active=1", ctx.OrgID)
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.
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()
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)
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.
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)
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.
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{}
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
// Package eventing records user events.
package eventing
// Package audit records user events.
package audit
import (
"time"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/model/audit"
"github.com/pkg/errors"
)
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Record adds event entry for specified user.
func Record(s domain.StoreContext, t EventType) {
e := AppEvent{}
e.OrgID = s.Context.OrgID
e.UserID = s.Context.UserID
func (s Scope) Record(ctx domain.RequestContext, t audit.EventType) {
e := audit.AppEvent{}
e.OrgID = ctx.OrgID
e.UserID = ctx.UserID
e.Created = time.Now().UTC()
e.IP = s.Context.ClientIP
e.IP = ctx.ClientIP
e.Type = string(t)
tx, err := s.Runtime.Db.Beginx()

View file

@ -11,20 +11,7 @@
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.
// An encrypted authentication token is issued with an expiry date.
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)
return
}
*/

View file

@ -11,11 +11,7 @@
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.
type Handler struct {
Runtime *env.Runtime
@ -26,3 +22,4 @@ type AuthenticationModel struct {
Token string `json:"token"`
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
import (
@ -5,7 +17,6 @@ import (
"net/http"
"time"
"github.com/documize/community/core/env"
"github.com/jmoiron/sqlx"
)
@ -51,13 +62,13 @@ func GetRequestContext(r *http.Request) RequestContext {
return r.Context().Value(DocumizeContextKey).(RequestContext)
}
// StoreContext provides data persistence methods with runtime and request context.
type StoreContext struct {
Runtime *env.Runtime
Context RequestContext
}
// // Scope provides data persistence methods with runtime and request context.
// type Scope struct {
// Runtime *env.Runtime
// Context RequestContext
// }
// NewContext returns request scoped user context and store context for persistence logic.
func NewContext(rt *env.Runtime, r *http.Request) StoreContext {
return StoreContext{Runtime: rt, Context: GetRequestContext(r)}
}
// // NewScope returns request scoped user context and store context for persistence logic.
// func NewScope(rt *env.Runtime, r *http.Request) Scope {
// return Scope{Runtime: rt, Context: GetRequestContext(r)}
// }

View file

@ -14,14 +14,20 @@ package document
import (
"fmt"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"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".
func MoveDocumentSpace(s domain.StoreContext, id, move string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE document SET labelid=? WHERE orgid=? AND labelid=?")
func (s Scope) MoveDocumentSpace(ctx domain.RequestContext, id, move string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE document SET labelid=? WHERE orgid=? AND labelid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -29,7 +35,7 @@ func MoveDocumentSpace(s domain.StoreContext, id, move string) (err error) {
return
}
_, err = stmt.Exec(move, s.Context.OrgID, id)
_, err = stmt.Exec(move, ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute document space move %s", id))
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/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/org"
"github.com/jmoiron/sqlx"
"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.
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.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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
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.
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=?")
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.
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
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.
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()
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)
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.
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{}
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.
func RemoveOrganization(s domain.StoreContext, rc domain.RequestContext, orgID string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?")
func (s Scope) RemoveOrganization(ctx domain.RequestContext, orgID string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE organization SET active=0 WHERE refid=?")
defer streamutil.Close(stmt)
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.
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()
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)
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
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)
var count int

View file

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

View file

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

View file

@ -15,17 +15,23 @@ import (
"fmt"
"time"
"github.com/documize/community/core/api/entity"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/pin"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add saves pinned item.
func Add(s domain.StoreContext, pin Pin) (err error) {
row := s.Runtime.Db.QueryRow("SELECT max(sequence) FROM pin WHERE orgid=? AND userid=?", s.Context.OrgID, s.Context.UserID)
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=?", ctx.OrgID, ctx.UserID)
var maxSeq int
err = row.Scan(&maxSeq)
@ -37,7 +43,7 @@ func Add(s domain.StoreContext, pin Pin) (err error) {
pin.Revised = time.Now().UTC()
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)
if err != nil {
@ -55,7 +61,7 @@ func Add(s domain.StoreContext, pin Pin) (err error) {
}
// 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=?")
defer streamutil.Close(stmt)
@ -64,7 +70,7 @@ func GetPin(s domain.StoreContext, id string) (pin Pin, err error) {
return
}
err = stmt.Get(&pin, s.Context.OrgID, id)
err = stmt.Get(&pin, ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute select for pin %s", id))
return
@ -74,11 +80,11 @@ func GetPin(s domain.StoreContext, id string) (pin Pin, err error) {
}
// GetUserPins returns pinned items for specified user.
func GetUserPins(s domain.StoreContext, userID string) (pins []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)
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", ctx.OrgID, userID)
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
}
@ -86,11 +92,11 @@ func GetUserPins(s domain.StoreContext, userID string) (pins []Pin, err error) {
}
// 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()
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)
if err != nil {
@ -108,8 +114,8 @@ func UpdatePin(s domain.StoreContext, pin entity.Pin) (err error) {
}
// UpdatePinSequence updates existing pinned item sequence number
func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE pin SET sequence=?, revised=? WHERE orgid=? AND userid=? AND refid=?")
func (s Scope) UpdatePinSequence(ctx domain.RequestContext, pinID string, sequence int) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE pin SET sequence=?, revised=? WHERE orgid=? AND userid=? AND refid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -117,7 +123,7 @@ func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err e
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 {
err = errors.Wrap(err, fmt.Sprintf("execute pin sequence update %s", pinID))
return
@ -127,19 +133,19 @@ func UpdatePinSequence(s domain.StoreContext, pinID string, sequence int) (err e
}
// 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{}
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.
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{}
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.
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{}
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/community/core/api/mail"
"github.com/documize/community/core/env"
"github.com/documize/community/core/request"
"github.com/documize/community/core/response"
"github.com/documize/community/core/secrets"
@ -30,25 +31,29 @@ import (
"github.com/documize/community/core/stringutil"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/account"
"github.com/documize/community/domain/document"
"github.com/documize/community/domain/eventing"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/pin"
"github.com/documize/community/domain/user"
"github.com/documize/community/model/account"
"github.com/documize/community/model/audit"
"github.com/documize/community/model/space"
"github.com/documize/community/model/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.
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
method := "AddSpace"
s := domain.NewContext(h.Runtime, r)
method := "space.Add"
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !s.Context.Editor {
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
@ -60,7 +65,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return
}
var space = Space{}
var space = space.Space{}
err = json.Unmarshal(body, &space)
if err != nil {
response.WriteServerError(w, method, err)
@ -72,27 +77,27 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return
}
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
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)
}
@ -100,7 +105,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
// Get returns the requested space.
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "Get"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID")
if len(id) == 0 {
@ -108,7 +113,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
return
}
sp, err := Get(s, id)
sp, err := h.Store.Space.Get(ctx, id)
if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, id)
return
@ -124,16 +129,16 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
// GetAll returns spaces the user can see.
func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
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 {
response.WriteServerError(w, method, err)
return
}
if len(sp) == 0 {
sp = []Space{}
sp = []space.Space{}
}
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.
func (h *Handler) GetSpaceViewers(w http.ResponseWriter, r *http.Request) {
method := "GetSpaceViewers"
s := domain.NewContext(h.Runtime, r)
method := "space.Viewers"
ctx := domain.GetRequestContext(r)
v, err := Viewers(s)
v, err := h.Store.Space.Viewers(ctx)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(v) == 0 {
v = []Viewer{}
v = []space.Viewer{}
}
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
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
method := "space.Update"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
if !s.Context.Editor {
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
@ -180,7 +185,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return
}
var sp Space
var sp space.Space
err = json.Unmarshal(body, &sp)
if err != nil {
response.WriteBadRequestError(w, method, "marshal")
@ -194,22 +199,22 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
sp.RefID = folderID
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
err = Update(s, sp)
err = h.Store.Space.Update(ctx, sp)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceUpdate)
h.Store.Audit.Record(ctx, audit.EventTypeSpaceUpdate)
s.Context.Transaction.Commit()
ctx.Transaction.Commit()
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
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
method := "space.Remove"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !s.Context.Editor {
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
id := request.Param(r, "folderID")
move := request.Param(r, "moveToId")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
return
}
move := request.Param(r, "moveToId")
if len(move) == 0 {
response.WriteMissingDataError(w, method, "moveToId")
return
}
var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
_, err = Delete(s, id)
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = document.MoveDocumentSpace(s, id, move)
err = h.Store.Document.MoveDocumentSpace(ctx, id, move)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
err = MoveSpaceRoles(s, id, move)
err = h.Store.Space.MoveSpaceRoles(ctx, id, move)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = pin.DeletePinnedSpace(s, id)
_, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceDelete)
h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
s.Context.Transaction.Commit()
ctx.Transaction.Commit()
response.WriteEmpty(w)
}
@ -286,14 +291,14 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
// Delete deletes empty space.
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
method := "space.Delete"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
if !h.Runtime.Product.License.IsValid() {
response.WriteBadLicense(w)
return
}
if !s.Context.Editor {
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
@ -305,45 +310,46 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
}
var err error
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
_, err = Delete(s, id)
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = DeleteSpaceRoles(s, id)
_, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
_, err = pin.DeletePinnedSpace(s, id)
_, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
if err != nil && err != sql.ErrNoRows {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceDelete)
h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
ctx.Transaction.Commit()
s.Context.Transaction.Commit()
response.WriteEmpty(w)
}
// SetPermissions persists specified spac3 permissions
func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.SetPermissions"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
if !s.Context.Editor {
if !ctx.Editor {
response.WriteForbiddenError(w)
return
}
@ -354,13 +360,13 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return
}
sp, err := Get(s, id)
sp, err := h.Store.Space.Get(ctx, id)
if err != nil {
response.WriteNotFoundError(w, method, "No such space")
return
}
if sp.UserID != s.Context.UserID {
if sp.UserID != ctx.UserID {
response.WriteForbiddenError(w)
return
}
@ -372,14 +378,14 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return
}
var model = RolesModel{}
var model = space.RolesModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteServerError(w, method, err)
return
}
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
@ -387,9 +393,9 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// We compare new permisions to what we had before.
// 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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
@ -402,17 +408,17 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
}
// 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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
// Nuke all previous permissions for this folder
_, err = DeleteSpaceRoles(s, id)
_, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
@ -421,14 +427,14 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
hasEveryoneRole := false
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 {
role.OrgID = s.Context.OrgID
role.OrgID = ctx.OrgID
role.LabelID = id
// Ensure the folder owner always has access!
if role.UserID == s.Context.UserID {
if role.UserID == ctx.UserID {
me = true
role.CanView = true
role.CanEdit = true
@ -442,7 +448,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
if role.CanView || role.CanEdit {
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
err = h.Store.Space.AddRole(ctx, role)
roleCount++
log.IfErr(err)
@ -453,7 +459,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// we skip 'everyone' (user id != empty string)
if len(role.UserID) > 0 {
var existingUser user.User
existingUser, err = user.Get(s, role.UserID)
existingUser, err = h.Store.User.Get(ctx, role.UserID)
if err == nil {
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?
if !me {
role := Role{}
role := space.Role{}
role.LabelID = id
role.OrgID = s.Context.OrgID
role.UserID = s.Context.UserID
role.OrgID = ctx.OrgID
role.UserID = ctx.UserID
role.CanEdit = true
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
err = h.Store.Space.AddRole(ctx, role)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
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.
if hasEveryoneRole {
sp.Type = ScopePublic
sp.Type = space.ScopePublic
} else {
if roleCount > 1 {
sp.Type = ScopeRestricted
sp.Type = space.ScopeRestricted
} else {
sp.Type = ScopePrivate
sp.Type = space.ScopePrivate
}
}
err = Update(s, sp)
err = h.Store.Space.Update(ctx, sp)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpacePermission)
h.Store.Audit.Record(ctx, audit.EventTypeSpacePermission)
s.Context.Transaction.Commit()
ctx.Transaction.Commit()
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.
func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.GetPermissions"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
@ -521,14 +527,14 @@ func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
return
}
roles, err := GetRoles(s, folderID)
roles, err := h.Store.Space.GetRoles(ctx, folderID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(roles) == 0 {
roles = []Role{}
roles = []space.Role{}
}
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.
func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
method := "space.AcceptInvitation"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
@ -545,14 +551,14 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return
}
org, err := organization.GetOrganizationByDomain(s, s.Context.Subdomain)
org, err := h.Store.Organization.GetOrganizationByDomain(ctx, ctx.Subdomain)
if err != nil {
response.WriteServerError(w, method, err)
return
}
// 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)
body, err := ioutil.ReadAll(r.Body)
@ -561,7 +567,7 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return
}
var model = AcceptShareModel{}
var model = space.AcceptShareModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
@ -573,44 +579,44 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
return
}
u, err := user.GetBySerial(s, model.Serial)
u, err := h.Store.User.GetBySerial(ctx, model.Serial)
if err != nil && err == sql.ErrNoRows {
response.WriteDuplicateError(w, method, "user")
return
}
// 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.Lastname = model.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 {
response.WriteServerError(w, method, err)
return
}
err = user.UpdateUser(s, u)
err = h.Store.User.UpdateUser(ctx, u)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
eventing.Record(s, eventing.EventTypeSpaceJoin)
h.Store.Audit.Record(ctx, audit.EventTypeSpaceJoin)
s.Context.Transaction.Commit()
ctx.Transaction.Commit()
response.WriteJSON(w, u)
}
@ -618,7 +624,7 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
// Invite sends users folder invitation emails.
func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
method := "space.Invite"
s := domain.NewContext(h.Runtime, r)
ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID")
if len(id) == 0 {
@ -626,13 +632,13 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
return
}
sp, err := Get(s, id)
sp, err := h.Store.Space.Get(ctx, id)
if err != nil {
response.WriteServerError(w, method, err)
return
}
if sp.UserID != s.Context.UserID {
if sp.UserID != ctx.UserID {
response.WriteForbiddenError(w)
return
}
@ -644,38 +650,38 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
return
}
var model = InvitationModel{}
var model = space.InvitationModel{}
err = json.Unmarshal(body, &model)
if err != nil {
response.WriteBadRequestError(w, method, "json")
return
}
s.Context.Transaction, err = h.Runtime.Db.Beginx()
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
return
}
inviter, err := user.Get(s, s.Context.UserID)
inviter, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil {
response.WriteServerError(w, method, err)
return
}
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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
if len(u.RefID) > 0 {
// 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 {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
@ -683,7 +689,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
// we create if they c
hasAccess := false
for _, a := range accounts {
if a.OrgID == s.Context.OrgID {
if a.OrgID == ctx.OrgID {
hasAccess = true
}
}
@ -691,52 +697,52 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
if !hasAccess {
var a account.Account
a.UserID = u.RefID
a.OrgID = s.Context.OrgID
a.OrgID = ctx.OrgID
a.Admin = false
a.Editor = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
err = account.Add(s, a)
err = h.Store.Account.Add(ctx, a)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
// 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.OrgID = s.Context.OrgID
role.OrgID = ctx.OrgID
role.UserID = u.RefID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
err = h.Store.Space.AddRole(ctx, role)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
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)
h.Runtime.Log.Info(fmt.Sprintf("%s is sharing space %s with existing user %s", inviter.Email, sp.Name, email))
} else {
// On-board new user
if strings.Contains(email, "@") {
url := s.Context.GetAppURL(fmt.Sprintf("auth/share/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
err = inviteNewUserToSharedSpace(s, email, inviter, url, sp, model.Message)
url := ctx.GetAppURL(fmt.Sprintf("auth/share/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
err = inviteNewUserToSharedSpace(ctx, h.Store, email, inviter, url, sp, model.Message)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
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!
if len(model.Recipients) > 0 && sp.Type == ScopePrivate {
sp.Type = ScopeRestricted
if len(model.Recipients) > 0 && sp.Type == space.ScopePrivate {
sp.Type = space.ScopeRestricted
err = Update(s, sp)
err = h.Store.Space.Update(ctx, sp)
if err != nil {
s.Context.Transaction.Rollback()
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
return
}
}
eventing.Record(s, eventing.EventTypeSpaceInvite)
h.Store.Audit.Record(ctx, audit.EventTypeSpaceInvite)
s.Context.Transaction.Commit()
ctx.Transaction.Commit()
response.WriteEmpty(w)
}

View file

@ -9,28 +9,34 @@
//
// https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
// Package mysql handles data persistence for spaces.
package mysql
import (
"database/sql"
"fmt"
"time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/domain/store/mysql"
"github.com/documize/community/model/space"
"github.com/pkg/errors"
)
// Scope provides data access to MySQL.
type Scope struct {
Runtime *env.Runtime
}
// Add adds new folder into the store.
func Add(s domain.StoreContext, sp Space) (err error) {
sp.UserID = s.Context.UserID
func (s Scope) Add(ctx domain.RequestContext, sp space.Space) (err error) {
sp.UserID = ctx.UserID
sp.Created = 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)
if err != nil {
@ -48,7 +54,7 @@ func Add(s domain.StoreContext, sp Space) (err error) {
}
// 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=?")
defer streamutil.Close(stmt)
@ -57,7 +63,7 @@ func Get(s domain.StoreContext, id string) (sp Space, err error) {
return
}
err = stmt.Get(&sp, s.Context.OrgID, id)
err = stmt.Get(&sp, ctx.OrgID, id)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for label %s", id))
return
@ -67,7 +73,7 @@ func Get(s domain.StoreContext, id string) (sp Space, err error) {
}
// 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"
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.
// 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 := `
(SELECT id,refid,label as name,orgid,userid,type,created,revised from label WHERE orgid=? AND type=2 AND userid=?)
UNION ALL
@ -94,16 +100,16 @@ UNION ALL
ORDER BY name`
err = s.Runtime.Db.Select(&sp, sql,
s.Context.OrgID,
s.Context.UserID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.OrgID,
s.Context.UserID)
ctx.OrgID,
ctx.UserID,
ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.UserID)
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
}
@ -111,10 +117,10 @@ ORDER BY name`
}
// 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()
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)
if err != nil {
@ -132,8 +138,8 @@ func Update(s domain.StoreContext, sp Space) (err error) {
}
// ChangeOwner transfer space ownership.
func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?")
func (s Scope) ChangeOwner(ctx domain.RequestContext, currentOwner, newOwner string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE label SET userid=? WHERE userid=? AND orgid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -141,7 +147,7 @@ func ChangeOwner(s domain.StoreContext, currentOwner, newOwner string) (err erro
return
}
_, err = stmt.Exec(newOwner, currentOwner, s.Context.OrgID)
_, err = stmt.Exec(newOwner, currentOwner, ctx.OrgID)
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute change space owner for %s", currentOwner))
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.
func Viewers(s domain.StoreContext) (v []Viewer, err error) {
func (s Scope) Viewers(ctx domain.RequestContext) (v []space.Viewer, err error) {
sql := `
SELECT a.userid,
COALESCE(u.firstname, '') as firstname,
@ -167,23 +173,23 @@ WHERE a.orgid=? AND b.type != 2
GROUP BY a.labelid,a.userid
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
}
// 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{}
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.
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.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)
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.
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"
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 {
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
// 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, `
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? and userid=?
UNION ALL
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 {
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
}
@ -240,36 +246,36 @@ func GetUserRoles(s domain.StoreContext) (r []Role, err error) {
}
// 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{}
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.
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{}
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.
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{}
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.
func MoveSpaceRoles(s domain.StoreContext, previousLabel, newLabel string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE labelrole SET labelid=? WHERE labelid=? AND orgid=?")
func (s Scope) MoveSpaceRoles(ctx domain.RequestContext, previousLabel, newLabel string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE labelrole SET labelid=? WHERE labelid=? AND orgid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -277,7 +283,7 @@ func MoveSpaceRoles(s domain.StoreContext, previousLabel, newLabel string) (err
return
}
_, err = stmt.Exec(newLabel, previousLabel, s.Context.OrgID)
_, err = stmt.Exec(newLabel, previousLabel, ctx.OrgID)
if err != nil {
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.
package space
import (
"database/sql"
"fmt"
"github.com/documize/community/domain"
)
/*
// CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(s domain.StoreContext, spaceID string) (hasPermission bool) {
roles, err := GetRoles(s, spaceID)
@ -60,3 +54,4 @@ func CanViewSpaceDocuments(s domain.StoreContext, spaceID string) (hasPermission
return false
}
*/

View file

@ -18,29 +18,30 @@ import (
"github.com/documize/community/core/secrets"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain"
"github.com/documize/community/domain/account"
"github.com/documize/community/domain/user"
"github.com/documize/community/model/account"
"github.com/documize/community/model/space"
"github.com/documize/community/model/user"
)
// addSpace prepares and creates space record.
func addSpace(s domain.StoreContext, sp Space) (err error) {
sp.Type = ScopePrivate
sp.UserID = s.Context.UserID
func addSpace(ctx domain.RequestContext, s *domain.Store, sp space.Space) (err error) {
sp.Type = space.ScopePrivate
sp.UserID = ctx.UserID
err = Add(s, sp)
err = s.Space.Add(ctx, sp)
if err != nil {
return
}
role := Role{}
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = sp.OrgID
role.UserID = s.Context.UserID
role.UserID = ctx.UserID
role.CanEdit = true
role.CanView = true
role.RefID = uniqueid.Generate()
err = AddRole(s, role)
err = s.Space.AddRole(ctx, role)
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
// through a welcome process designed to capture profile data.
// We add them to the organization and grant them view-only folder access.
func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy user.User,
baseURL string, sp Space, invitationMessage string) (err error) {
func inviteNewUserToSharedSpace(ctx domain.RequestContext, s *domain.Store, email string, invitedBy user.User,
baseURL string, sp space.Space, invitationMessage string) (err error) {
var u = user.User{}
u.Email = email
@ -62,7 +63,7 @@ func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy u
userID := uniqueid.Generate()
u.RefID = userID
err = user.Add(s, u)
err = s.User.Add(ctx, u)
if err != nil {
return
}
@ -70,28 +71,28 @@ func inviteNewUserToSharedSpace(s domain.StoreContext, email string, invitedBy u
// Let's give this user access to the organization
var a account.Account
a.UserID = userID
a.OrgID = s.Context.OrgID
a.OrgID = ctx.OrgID
a.Admin = false
a.Editor = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
err = account.Add(s, a)
err = s.Account.Add(ctx, a)
if err != nil {
return
}
role := Role{}
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = s.Context.OrgID
role.OrgID = ctx.OrgID
role.UserID = userID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
err = AddRole(s, role)
err = s.Space.AddRole(ctx, role)
if err != nil {
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
package user
package mysql
import (
"database/sql"
@ -17,17 +17,24 @@ import (
"strings"
"time"
"github.com/documize/community/core/env"
"github.com/documize/community/core/streamutil"
"github.com/documize/community/domain"
"github.com/documize/community/model/user"
"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.
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.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)
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.
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=?")
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.
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))
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.
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))
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.
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=?")
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!
// This occurs when we you share a folder with a new user and they have to complete
// 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=?")
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
// 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,
`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
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 {
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
}
@ -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
// 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,
"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 {
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
}
@ -177,17 +184,17 @@ func GetUsersForOrganization(s domain.StoreContext) (u []User, err error) {
}
// 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,
`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
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
ORDER BY u.firstname, u.lastname`,
s.Context.OrgID, folderID, s.Context.OrgID)
ctx.OrgID, folderID, ctx.OrgID)
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
}
@ -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.
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.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")
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.
func UpdateUserPassword(s domain.StoreContext, userID, salt, password string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE user SET salt=?, password=?, reset='' WHERE refid=?")
func (s Scope) UpdateUserPassword(ctx domain.RequestContext, userID, salt, password string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE user SET salt=?, password=?, reset='' WHERE refid=?")
defer streamutil.Close(stmt)
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.
func DeactiveUser(s domain.StoreContext, userID string) (err error) {
stmt, err := s.Context.Transaction.Preparex("DELETE FROM account WHERE userid=? and orgid=?")
func (s Scope) DeactiveUser(ctx domain.RequestContext, userID string) (err error) {
stmt, err := ctx.Transaction.Preparex("DELETE FROM account WHERE userid=? and orgid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -246,7 +253,7 @@ func DeactiveUser(s domain.StoreContext, userID string) (err error) {
return
}
_, err = stmt.Exec(userID, s.Context.OrgID)
_, err = stmt.Exec(userID, ctx.OrgID)
if err != nil {
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.
func ForgotUserPassword(s domain.StoreContext, email, token string) (err error) {
stmt, err := s.Context.Transaction.Preparex("UPDATE user SET reset=?, password='' WHERE LOWER(email)=?")
func (s Scope) ForgotUserPassword(ctx domain.RequestContext, email, token string) (err error) {
stmt, err := ctx.Transaction.Preparex("UPDATE user SET reset=?, password='' WHERE LOWER(email)=?")
defer streamutil.Close(stmt)
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.
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)")
err := row.Scan(&c)

View file

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

View file

@ -19,11 +19,12 @@ import (
"github.com/documize/community/core/database"
"github.com/documize/community/core/env"
"github.com/documize/community/core/secrets"
"github.com/documize/community/domain"
"github.com/jmoiron/sqlx"
)
// 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
if r.Flags.Salt == "" {
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
}

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/server"
_ "github.com/go-sql-driver/mysql" // the mysql driver is required behind the scenes
"github.com/documize/community/domain"
)
var rt env.Runtime
@ -49,9 +50,12 @@ func main() {
rt.Product.License.Trial = false
rt.Product.License.Edition = "Community"
// setup store
s := domain.Store{}
// parse settings from command line and environment
rt.Flags = env.ParseFlags()
flagsOK := boot.InitRuntime(&rt)
flagsOK := boot.InitRuntime(&rt, &s)
if flagsOK {
// runtime.Log = runtime.Log.SetDB(runtime.Db)
@ -65,5 +69,5 @@ func main() {
section.Register(rt)
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
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
}
import "github.com/documize/community/model"
// Account links a User to an Organization.
type Account struct {
domain.BaseEntity
model.BaseEntity
Admin bool `json:"admin"`
Editor bool `json:"editor"`
UserID string `json:"userId"`

View file

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

View file

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

View file

@ -9,21 +9,13 @@
//
// https://documize.com
package organization
package org
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
}
import "github.com/documize/community/model"
// Organization defines a company that uses this app.
type Organization struct {
domain.BaseEntity
model.BaseEntity
Company string `json:"-"`
Title string `json:"title"`
Message string `json:"message"`

View file

@ -11,19 +11,11 @@
package pin
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
}
import "github.com/documize/community/model"
// Pin defines a saved link to a document or space
type Pin struct {
domain.BaseEntity
model.BaseEntity
OrgID string `json:"orgId"`
UserID string `json:"userId"`
FolderID string `json:"folderId"`

View file

@ -9,23 +9,13 @@
//
// https://documize.com
// Package space handles API calls and persistence for spaces.
// Spaces in Documize contain documents.
package space
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
}
import "github.com/documize/community/model"
// Space defines a container for documents.
type Space struct {
domain.BaseEntity
model.BaseEntity
Name string `json:"name"`
OrgID string `json:"orgId"`
UserID string `json:"userId"`
@ -63,7 +53,7 @@ func (l *Space) IsRestricted() bool {
// Role determines user permissions for a folder.
type Role struct {
domain.BaseEntityObfuscated
model.BaseEntityObfuscated
OrgID string `json:"-"`
LabelID string `json:"folderId"`
UserID string `json:"userId"`

View file

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

View file

@ -25,10 +25,12 @@ import (
"github.com/documize/community/domain/auth"
"github.com/documize/community/domain/organization"
"github.com/documize/community/domain/user"
"github.com/documize/community/model/org"
)
type middleware struct {
Runtime *env.Runtime
Store *domain.Store
}
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) {
method := "Authorize"
s := domain.StoreContext{Runtime: m.Runtime, Context: domain.RequestContext{}}
// Let certain requests pass straight through
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)
rc, _, tokenErr := auth.DecodeJWT(m.Runtime, token)
var org = organization.Organization{}
var org = org.Organization{}
var err = errors.New("")
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 {
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.
@ -96,8 +98,8 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
}
rc.Subdomain = org.Domain
dom := organization.GetSubdomainFromHost(s, r)
dom2 := organization.GetRequestSubdomain(s, r)
dom := organization.GetSubdomainFromHost(r)
dom2 := organization.GetRequestSubdomain(r)
if org.Domain != dom && org.Domain != dom2 {
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.Global = false
rc.AppURL = r.Host
rc.Subdomain = organization.GetSubdomainFromHost(s, r)
rc.Subdomain = organization.GetSubdomainFromHost(r)
rc.SSL = r.TLS != nil
// 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
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 {
response.WriteServerError(w, method, err)
return

View file

@ -17,11 +17,15 @@ import (
"github.com/documize/community/core/api"
"github.com/documize/community/core/api/endpoint"
"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"
)
// RegisterEndpoints register routes for serving API endpoints
func RegisterEndpoints(rt *env.Runtime) {
func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
//**************************************************
// 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)
// Organization
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetOrganization)
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, endpoint.UpdateOrganization)
organization := organization.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"GET", "OPTIONS"}, nil, organization.Get)
Add(rt, RoutePrefixPrivate, "organizations/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
// Folder
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"DELETE", "OPTIONS"}, nil, endpoint.DeleteFolder)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, endpoint.RemoveFolder)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"PUT", "OPTIONS"}, nil, endpoint.SetFolderPermissions)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolderPermissions)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/invitation", []string{"POST", "OPTIONS"}, nil, endpoint.InviteToFolder)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, endpoint.GetFolderVisibility)
Add(rt, RoutePrefixPrivate, "folders", []string{"POST", "OPTIONS"}, nil, endpoint.AddFolder)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolders)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetFolder)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"PUT", "OPTIONS"}, nil, endpoint.UpdateFolder)
// Space
space := space.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"PUT", "OPTIONS"}, nil, space.SetPermissions)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/permissions", []string{"GET", "OPTIONS"}, nil, space.GetPermissions)
Add(rt, RoutePrefixPrivate, "folders/{folderID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, space.GetSpaceViewers)
Add(rt, RoutePrefixPrivate, "folders", []string{"POST", "OPTIONS"}, nil, space.Add)
Add(rt, RoutePrefixPrivate, "folders", []string{"GET", "OPTIONS"}, nil, space.GetAll)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"GET", "OPTIONS"}, nil, space.Get)
Add(rt, RoutePrefixPrivate, "folders/{folderID}", []string{"PUT", "OPTIONS"}, nil, space.Update)
// Users
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)
// Pinned items
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, endpoint.AddPin)
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, endpoint.GetUserPins)
Add(rt, RoutePrefixPrivate, "pin/{userID}/sequence", []string{"POST", "OPTIONS"}, nil, endpoint.UpdatePinSequence)
Add(rt, RoutePrefixPrivate, "pin/{userID}/{pinID}", []string{"DELETE", "OPTIONS"}, nil, endpoint.DeleteUserPin)
pin := pin.Handler{Runtime: rt, Store: s}
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"POST", "OPTIONS"}, nil, pin.Add)
Add(rt, RoutePrefixPrivate, "pin/{userID}", []string{"GET", "OPTIONS"}, nil, pin.GetUserPins)
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
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/database"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/server/routing"
"github.com/documize/community/server/web"
"github.com/gorilla/mux"
@ -30,8 +31,7 @@ import (
var testHost string // used during automated testing
// 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()
if err != nil {
rt.Log.Error("Terminating before running - invalid plugin.json", err)
@ -54,10 +54,10 @@ func Start(rt *env.Runtime, ready chan struct{}) {
}
// define middleware
cm := middleware{Runtime: rt}
cm := middleware{Runtime: rt, Store: s}
// define API endpoints
routing.RegisterEndpoints(rt)
routing.RegisterEndpoints(rt, s)
// wire up API endpoints
router := mux.NewRouter()