1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-02 12:05:23 +02:00

re-working space permissions -- WIP

This commit is contained in:
Harvey Kandola 2017-09-13 19:22:38 +01:00
parent c51ba65b1d
commit ae05cacf3f
37 changed files with 735 additions and 601 deletions

View file

@ -34,7 +34,7 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro
account.Created = time.Now().UTC()
account.Revised = time.Now().UTC()
stmt, err := ctx.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, users, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
stmt, err := ctx.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, users, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt)
if err != nil {

View file

@ -15,12 +15,12 @@ import (
"database/sql"
"github.com/documize/community/domain"
sp "github.com/documize/community/model/space"
)
// CanViewDocumentInFolder returns if the user has permission to view a document within the specified folder.
func CanViewDocumentInFolder(ctx domain.RequestContext, s domain.Store, labelID string) (hasPermission bool) {
roles, err := s.Space.GetUserRoles(ctx)
func CanViewDocumentInFolder(ctx domain.RequestContext, s domain.Store, labelID string) bool {
roles, err := s.Space.GetUserPermissions(ctx, labelID)
if err == sql.ErrNoRows {
err = nil
}
@ -29,7 +29,8 @@ func CanViewDocumentInFolder(ctx domain.RequestContext, s domain.Store, labelID
}
for _, role := range roles {
if role.LabelID == labelID && (role.CanView || role.CanEdit) {
if role.RefID == labelID && role.Location == "space" && role.Scope == "object" &&
sp.HasPermission(role.Action, sp.SpaceView, sp.SpaceManage, sp.SpaceOwner) {
return true
}
}
@ -37,10 +38,9 @@ func CanViewDocumentInFolder(ctx domain.RequestContext, s domain.Store, labelID
return false
}
// CanViewDocument returns if the clinet has permission to view a given document.
func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID string) (hasPermission bool) {
// CanViewDocument returns if the client has permission to view a given document.
func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID string) bool {
document, err := s.Document.Get(ctx, documentID)
if err == sql.ErrNoRows {
err = nil
}
@ -48,8 +48,7 @@ func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID strin
return false
}
roles, err := s.Space.GetUserRoles(ctx)
roles, err := s.Space.GetUserPermissions(ctx, document.LabelID)
if err == sql.ErrNoRows {
err = nil
}
@ -58,7 +57,8 @@ func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID strin
}
for _, role := range roles {
if role.LabelID == document.LabelID && (role.CanView || role.CanEdit) {
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" &&
sp.HasPermission(role.Action, sp.SpaceView, sp.SpaceManage, sp.SpaceOwner) {
return true
}
}
@ -67,7 +67,7 @@ func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID strin
}
// CanChangeDocument returns if the clinet has permission to change a given document.
func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID string) (hasPermission bool) {
func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID string) bool {
document, err := s.Document.Get(ctx, documentID)
if err == sql.ErrNoRows {
@ -77,7 +77,7 @@ func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID str
return false
}
roles, err := s.Space.GetUserRoles(ctx)
roles, err := s.Space.GetUserPermissions(ctx, document.LabelID)
if err == sql.ErrNoRows {
err = nil
@ -87,7 +87,8 @@ func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID str
}
for _, role := range roles {
if role.LabelID == document.LabelID && role.CanEdit {
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" &&
sp.HasPermission(role.Action, sp.DocumentEdit) {
return true
}
}
@ -95,10 +96,9 @@ func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID str
return false
}
// CanUploadDocument returns if the client has permission to upload documents to the given folderID.
func CanUploadDocument(ctx domain.RequestContext, s domain.Store, folderID string) (hasPermission bool) {
roles, err := s.Space.GetUserRoles(ctx)
// CanUploadDocument returns if the client has permission to upload documents to the given space.
func CanUploadDocument(ctx domain.RequestContext, s domain.Store, spaceID string) bool {
roles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err == sql.ErrNoRows {
err = nil
}
@ -107,7 +107,8 @@ func CanUploadDocument(ctx domain.RequestContext, s domain.Store, folderID strin
}
for _, role := range roles {
if role.LabelID == folderID && role.CanEdit {
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
sp.HasPermission(role.Action, sp.DocumentAdd) {
return true
}
}

View file

@ -168,12 +168,12 @@ func (m *Mailer) PasswordReset(recipient, url string) {
}
}
// ShareFolderExistingUser provides an existing user with a link to a newly shared folder.
func (m *Mailer) ShareFolderExistingUser(recipient, inviter, url, folder, intro string) {
method := "ShareFolderExistingUser"
// ShareSpaceExistingUser provides an existing user with a link to a newly shared space.
func (m *Mailer) ShareSpaceExistingUser(recipient, inviter, url, folder, intro string) {
method := "ShareSpaceExistingUser"
m.LoadCredentials()
file, err := web.ReadFile("mail/share-folder-existing-user.html")
file, err := web.ReadFile("mail/share-space-existing-user.html")
if err != nil {
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
return
@ -218,12 +218,12 @@ func (m *Mailer) ShareFolderExistingUser(recipient, inviter, url, folder, intro
}
}
// ShareFolderNewUser invites new user providing Credentials, explaining the product and stating who is inviting them.
func (m *Mailer) ShareFolderNewUser(recipient, inviter, url, folder, invitationMessage string) {
method := "ShareFolderNewUser"
// ShareSpaceNewUser invites new user providing Credentials, explaining the product and stating who is inviting them.
func (m *Mailer) ShareSpaceNewUser(recipient, inviter, url, space, invitationMessage string) {
method := "ShareSpaceNewUser"
m.LoadCredentials()
file, err := web.ReadFile("mail/share-folder-new-user.html")
file, err := web.ReadFile("mail/share-space-new-user.html")
if err != nil {
m.Runtime.Log.Error(fmt.Sprintf("%s - unable to load email template", method), err)
return
@ -236,7 +236,7 @@ func (m *Mailer) ShareFolderNewUser(recipient, inviter, url, folder, invitationM
inviter = "Your colleague"
}
subject := fmt.Sprintf("%s has shared %s with you on Documize", inviter, folder)
subject := fmt.Sprintf("%s has shared %s with you on Documize", inviter, space)
e := NewEmail()
e.From = m.Credentials.SMTPsender
@ -254,7 +254,7 @@ func (m *Mailer) ShareFolderNewUser(recipient, inviter, url, folder, invitationM
inviter,
url,
invitationMessage,
folder,
space,
}
buffer := new(bytes.Buffer)

View file

@ -106,15 +106,16 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return
}
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = sp.OrgID
role.UserID = ctx.UserID
role.CanEdit = true
role.CanView = true
role.RefID = uniqueid.Generate()
perm := space.Permission{}
perm.OrgID = sp.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = h.Store.Space.AddRole(ctx, role)
err = h.Store.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -138,7 +139,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
return
}
spCloneRoles, err := h.Store.Space.GetRoles(ctx, model.CloneID)
spCloneRoles, err := h.Store.Space.GetPermissions(ctx, model.CloneID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
@ -147,10 +148,9 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
if model.CopyPermission {
for _, r := range spCloneRoles {
r.RefID = uniqueid.Generate()
r.LabelID = sp.RefID
r.RefID = sp.RefID
err = h.Store.Space.AddRole(ctx, r)
err = h.Store.Space.AddPermission(ctx, r)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -279,9 +279,9 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
method := "Get"
ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID")
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -349,9 +349,9 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return
}
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -377,7 +377,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
return
}
sp.RefID = folderID
sp.RefID = spaceID
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
@ -401,7 +401,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
response.WriteJSON(w, sp)
}
// Remove moves documents to another folder before deleting it
// Remove moves documents to another space before deleting it
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
method := "space.Remove"
ctx := domain.GetRequestContext(r)
@ -416,9 +416,9 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
return
}
id := request.Param(r, "folderID")
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -436,14 +436,6 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
return
}
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
err = h.Store.Document.MoveDocumentSpace(ctx, id, move)
if err != nil {
ctx.Transaction.Rollback()
@ -452,7 +444,15 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
return
}
err = h.Store.Space.MoveSpaceRoles(ctx, id, move)
_, err = h.Store.Space.Delete(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
_, err = h.Store.Space.DeletePermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -490,9 +490,9 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
return
}
id := request.Param(r, "folderID")
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -512,7 +512,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
return
}
_, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
_, err = h.Store.Space.DeletePermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -545,9 +545,9 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return
}
id := request.Param(r, "folderID")
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -586,8 +586,8 @@ 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 := h.Store.Space.GetRoles(ctx, id)
// Why? So we can send out space invitation emails.
previousRoles, err := h.Store.Space.GetPermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -599,10 +599,10 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
previousRoleUsers := make(map[string]bool)
for _, v := range previousRoles {
previousRoleUsers[v.UserID] = true
previousRoleUsers[v.WhoID] = true
}
// Who is sharing this folder?
// Who is sharing this space?
inviter, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil {
ctx.Transaction.Rollback()
@ -611,8 +611,8 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
return
}
// Nuke all previous permissions for this folder
_, err = h.Store.Space.DeleteSpaceRoles(ctx, id)
// Nuke all previous permissions for this space
_, err = h.Store.Space.DeletePermissions(ctx, id)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -626,44 +626,40 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
for _, role := range model.Roles {
role.OrgID = ctx.OrgID
role.LabelID = id
for _, perm := range model.Permissions {
perm.OrgID = ctx.OrgID
perm.RefID = id
// Ensure the folder owner always has access!
if role.UserID == ctx.UserID {
// Ensure the space owner always has access!
if perm.WhoID == ctx.UserID {
me = true
role.CanView = true
role.CanEdit = true
}
if len(role.UserID) == 0 && (role.CanView || role.CanEdit) {
if len(perm.WhoID) == 0 {
hasEveryoneRole = true
}
// Only persist if there is a role!
if role.CanView || role.CanEdit {
roleID := uniqueid.Generate()
role.RefID = roleID
err = h.Store.Space.AddRole(ctx, role)
if perm.Action == "TBC" {
err = h.Store.Space.AddPermission(ctx, perm)
if err != nil {
h.Runtime.Log.Error("add role", err)
}
roleCount++
// We send out folder invitation emails to those users
// We send out space invitation emails to those users
// that have *just* been given permissions.
if _, isExisting := previousRoleUsers[role.UserID]; !isExisting {
if _, isExisting := previousRoleUsers[perm.WhoID]; !isExisting {
// we skip 'everyone' (user id != empty string)
if len(role.UserID) > 0 {
if len(perm.WhoID) > 0 {
var existingUser user.User
existingUser, err = h.Store.User.Get(ctx, role.UserID)
existingUser, err = h.Store.User.Get(ctx, perm.WhoID)
if err == nil {
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
go mailer.ShareFolderExistingUser(existingUser.Email, inviter.Fullname(), url, sp.Name, model.Message)
go mailer.ShareSpaceExistingUser(existingUser.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, existingUser.Email))
} else {
response.WriteServerError(w, method, err)
@ -675,16 +671,16 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
// Do we need to ensure permissions for space owner when shared?
if !me {
role := space.Role{}
role.LabelID = id
role.OrgID = ctx.OrgID
role.UserID = ctx.UserID
role.CanEdit = true
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = id
perm.Action = "" // we send array for actions below
err = h.Store.Space.AddRole(ctx, role)
err = h.Store.Space.AddPermission(ctx, perm)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -692,7 +688,7 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
}
}
// Mark up folder type as either public, private or restricted access.
// Mark up space type as either public, private or restricted access.
if hasEveryoneRole {
sp.Type = space.ScopePublic
} else {
@ -718,28 +714,52 @@ func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
response.WriteEmpty(w)
}
// GetPermissions returns user permissions for the requested folder.
// GetPermissions returns permissions for the requested space, for all users.
func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.GetPermissions"
ctx := domain.GetRequestContext(r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
roles, err := h.Store.Space.GetRoles(ctx, folderID)
perms, err := h.Store.Space.GetPermissions(ctx, spaceID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(roles) == 0 {
roles = []space.Role{}
if len(perms) == 0 {
perms = []space.Permission{}
}
response.WriteJSON(w, roles)
response.WriteJSON(w, perms)
}
// GetUserPermissions returns permissions for the requested space, for current user.
func (h *Handler) GetUserPermissions(w http.ResponseWriter, r *http.Request) {
method := "space.GetUserPermissions"
ctx := domain.GetRequestContext(r)
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
perms, err := h.Store.Space.GetUserPermissions(ctx, spaceID)
if err != nil && err != sql.ErrNoRows {
response.WriteServerError(w, method, err)
return
}
if len(perms) == 0 {
perms = []space.Permission{}
}
response.WriteJSON(w, perms)
}
// AcceptInvitation records the fact that a user has completed space onboard process.
@ -747,10 +767,10 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
method := "space.AcceptInvitation"
ctx := domain.GetRequestContext(r)
ctx.Subdomain = organization.GetSubdomainFromHost(r)
folderID := request.Param(r, "folderID")
if len(folderID) == 0 {
response.WriteMissingDataError(w, method, "folderID")
spaceID := request.Param(r, "spaceID")
if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -831,14 +851,14 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
response.WriteJSON(w, u)
}
// Invite sends users folder invitation emails.
// Invite sends users space invitation emails.
func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
method := "space.Invite"
ctx := domain.GetRequestContext(r)
id := request.Param(r, "folderID")
id := request.Param(r, "spaceID")
if len(id) == 0 {
response.WriteMissingDataError(w, method, "folderID")
response.WriteMissingDataError(w, method, "spaceID")
return
}
@ -917,6 +937,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
a.OrgID = ctx.OrgID
a.Admin = false
a.Editor = false
a.Users = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
@ -931,18 +952,18 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
}
// Ensure they have space roles
h.Store.Space.DeleteUserSpaceRoles(ctx, sp.RefID, u.RefID)
h.Store.Space.DeleteUserPermissions(ctx, sp.RefID, u.RefID)
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = ctx.OrgID
role.UserID = u.RefID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
perm := space.Permission{}
perm.OrgID = sp.OrgID
perm.Who = "user"
perm.WhoID = u.RefID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = h.Store.Space.AddRole(ctx, role)
err = h.Store.Space.AddPermissions(ctx, perm, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
@ -952,7 +973,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
go mailer.ShareFolderExistingUser(email, inviter.Fullname(), url, sp.Name, model.Message)
go mailer.ShareSpaceExistingUser(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 {
@ -973,7 +994,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
}
}
// We ensure that the folder is marked as restricted as a minimum!
// We ensure that the space is marked as restricted as a minimum!
if len(model.Recipients) > 0 && sp.Type == space.ScopePrivate {
sp.Type = space.ScopeRestricted

View file

@ -184,109 +184,98 @@ func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err err
return b.DeleteConstrained(ctx.Transaction, "label", ctx.OrgID, id)
}
// AddRole inserts the given record into the labelrole database table.
func (s Scope) AddRole(ctx domain.RequestContext, r space.Role) (err error) {
// AddPermission inserts the given record into the labelrole database table.
func (s Scope) AddPermission(ctx domain.RequestContext, r space.Permission) (err error) {
r.Created = time.Now().UTC()
r.Revised = time.Now().UTC()
stmt, err := ctx.Transaction.Preparex("INSERT INTO labelrole (refid, labelid, orgid, userid, canview, canedit, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
stmt, err := ctx.Transaction.Preparex("INSERT INTO labelrole (orgid, who, whoid, action, scope, location, refid, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt)
if err != nil {
err = errors.Wrap(err, "unable to prepare insert for space role")
err = errors.Wrap(err, "unable to prepare insert for space permission")
return
}
_, err = stmt.Exec(r.RefID, r.LabelID, r.OrgID, r.UserID, r.CanView, r.CanEdit, r.Created, r.Revised)
_, err = stmt.Exec(r.OrgID, r.Who, r.WhoID, r.Action, r.Scope, r.Location, r.RefID, r.Created)
if err != nil {
err = errors.Wrap(err, "unable to execute insert for space role")
err = errors.Wrap(err, "unable to execute insert for space permission")
return
}
return
}
// GetRoles returns a slice of labelrole records, for the given labelID in the client's organization, grouped by user.
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, ctx.OrgID, labelID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for space roles %s", labelID))
return
// AddPermissions inserts records into permission database table, one per action.
func (s Scope) AddPermissions(ctx domain.RequestContext, r space.Permission, actions ...space.PermissionAction) (err error) {
for _, a := range actions {
r.Action = string(a)
s.AddPermission(ctx, r)
}
return
}
// 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 (s Scope) GetUserRoles(ctx domain.RequestContext) (r []space.Role, err error) {
// GetUserPermissions returns space permissions for user.
// Context is used to for user ID.
func (s Scope) GetUserPermissions(ctx domain.RequestContext, spaceID string) (r []space.Permission, err error) {
err = s.Runtime.Db.Select(&r, `
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? and userid=?
SELECT id, orgid, who, whoid, action, scope, location, refid
FROM permission WHERE orgid=? AND location='space' AND refid=? AND who='user' AND (whoid=? OR whoid='')
UNION ALL
SELECT id, refid, labelid, orgid, userid, canview, canedit, created, revised FROM labelrole WHERE orgid=? AND userid=''`,
ctx.OrgID, ctx.UserID, ctx.OrgID)
SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid
FROM permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.location='space' AND refid=?
AND p.who='role' AND (r.userid=? OR r.userid='')`,
ctx.OrgID, spaceID, ctx.UserID, ctx.OrgID, spaceID, 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", ctx.UserID))
err = errors.Wrap(err, fmt.Sprintf("unable to execute select user permissions %s", ctx.UserID))
return
}
return
}
// DeleteRole deletes the labelRoleID record from the labelrole table.
func (s Scope) DeleteRole(ctx domain.RequestContext, roleID string) (rows int64, err error) {
// GetPermissions returns space permissions for all users.
func (s Scope) GetPermissions(ctx domain.RequestContext, spaceID string) (r []space.Permission, err error) {
err = s.Runtime.Db.Select(&r, `
SELECT id, orgid, who, whoid, action, scope, location, refid
FROM permission WHERE orgid=? AND location='space' AND refid=? AND who='user' AND (whoid=? OR whoid='')
UNION ALL
SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid
FROM permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.location='space' AND p.refid=?
AND p.who='role' AND (r.userid=? OR r.userid='')`,
ctx.OrgID, spaceID, ctx.UserID, ctx.OrgID, spaceID, ctx.OrgID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select space permissions %s", ctx.UserID))
return
}
return
}
// DeletePermissions removes records from permissions table for given space ID.
func (s Scope) DeletePermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
b := mysql.BaseQuery{}
sql := fmt.Sprintf("DELETE FROM labelrole WHERE orgid='%s' AND refid='%s'", ctx.OrgID, roleID)
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND location='space' AND refid='%s'",
ctx.OrgID, spaceID)
return b.DeleteWhere(ctx.Transaction, sql)
}
// DeleteSpaceRoles deletes records from the labelrole table which have the given space ID.
func (s Scope) DeleteSpaceRoles(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
// DeleteUserPermissions removes all roles for the specified user, for the specified space.
func (s Scope) DeleteUserPermissions(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'", ctx.OrgID, spaceID)
return b.DeleteWhere(ctx.Transaction, sql)
}
// DeleteUserSpaceRoles removes all roles for the specified user, for the specified space.
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'",
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND location='space' AND refid='%s' who='user' AND whoid='%s'",
ctx.OrgID, spaceID, userID)
return b.DeleteWhere(ctx.Transaction, sql)
}
// MoveSpaceRoles changes the space ID for space role records from previousLabel to newLabel.
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 {
err = errors.Wrap(err, fmt.Sprintf("unable to prepare move space roles for label %s", previousLabel))
return
}
_, 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))
}
return
}

View file

@ -17,11 +17,12 @@ import (
"database/sql"
"github.com/documize/community/domain"
"github.com/documize/community/model/space"
)
// CanViewSpace returns if the user has permission to view the given spaceID.
func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) (hasPermission bool) {
roles, err := s.Space.GetRoles(ctx, spaceID)
func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) bool {
roles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err == sql.ErrNoRows {
err = nil
}
@ -30,27 +31,8 @@ func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) (ha
}
for _, role := range roles {
if role.LabelID == spaceID && (role.CanView || role.CanEdit) {
return true
}
}
return false
}
// CanViewSpaceDocuments returns if the user has permission to view a document within the specified space.
func CanViewSpaceDocuments(ctx domain.RequestContext, s domain.Store, spaceID string) (hasPermission bool) {
roles, err := s.Space.GetRoles(ctx, spaceID)
if err == sql.ErrNoRows {
err = nil
}
if err != nil {
return false
}
for _, role := range roles {
if role.LabelID == spaceID && (role.CanView || role.CanEdit) {
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
space.HasPermission(role.Action, space.SpaceView, space.SpaceManage, space.SpaceOwner) {
return true
}
}

View file

@ -24,33 +24,10 @@ import (
"github.com/documize/community/model/user"
)
// addSpace prepares and creates space record.
func addSpace(ctx domain.RequestContext, s *domain.Store, sp space.Space) (err error) {
sp.Type = space.ScopePrivate
sp.UserID = ctx.UserID
err = s.Space.Add(ctx, sp)
if err != nil {
return
}
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = sp.OrgID
role.UserID = ctx.UserID
role.CanEdit = true
role.CanView = true
role.RefID = uniqueid.Generate()
err = s.Space.AddRole(ctx, role)
return
}
// Invite new user to a folder that someone has shared with them.
// Invite new user to a space that someone has shared with them.
// 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.
// We add them to the organization and grant them view-only space access.
func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *domain.Store, email string, invitedBy user.User,
baseURL string, sp space.Space, invitationMessage string) (err error) {
@ -75,25 +52,25 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *d
a.OrgID = ctx.OrgID
a.Admin = false
a.Editor = false
a.Users = false
a.Active = true
accountID := uniqueid.Generate()
a.RefID = accountID
a.RefID = uniqueid.Generate()
err = s.Account.Add(ctx, a)
if err != nil {
return
}
role := space.Role{}
role.LabelID = sp.RefID
role.OrgID = ctx.OrgID
role.UserID = userID
role.CanEdit = false
role.CanView = true
roleID := uniqueid.Generate()
role.RefID = roleID
perm := space.Permission{}
perm.OrgID = sp.OrgID
perm.Who = "user"
perm.WhoID = userID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = sp.RefID
perm.Action = "" // we send array for actions below
err = s.Space.AddRole(ctx, role)
err = s.Space.AddPermissions(ctx, perm, space.SpaceView)
if err != nil {
return
}
@ -101,7 +78,7 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *d
mailer := mail.Mailer{Runtime: rt, Store: s, Context: ctx}
url := fmt.Sprintf("%s/%s", baseURL, u.Salt)
go mailer.ShareFolderNewUser(u.Email, invitedBy.Fullname(), url, sp.Name, invitationMessage)
go mailer.ShareSpaceNewUser(u.Email, invitedBy.Fullname(), url, sp.Name, invitationMessage)
return
}

View file

@ -1,7 +1,6 @@
package space
import (
"database/sql"
"testing"
"github.com/documize/community/core/uniqueid"
@ -39,17 +38,19 @@ func TestSpace(t *testing.T) {
t.Error("failed to add sp space")
}
r.RefID = uniqueid.Generate()
r.LabelID = spaceID
r.OrgID = ctx.OrgID
r.UserID = "testAddSpace"
r.CanView = true
r.CanEdit = true
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = spaceID
perm.Action = "" // we send array for actions below
err = s.Space.AddRole(ctx, r)
err = s.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add role r")
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
@ -104,17 +105,19 @@ func TestSpace(t *testing.T) {
t.Error("failed to add sp2")
}
r2.RefID = uniqueid.Generate()
r2.LabelID = spaceID2
r2.OrgID = ctx.OrgID
r2.UserID = ctx.UserID
r2.CanView = true
r2.CanEdit = true
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = spaceID2
perm.Action = "" // we send array for actions below
err = s.Space.AddRole(ctx, r2)
err = s.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add role")
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
@ -171,22 +174,24 @@ func TestSpace(t *testing.T) {
t.Run("Add Role", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
r3.CanView = true
r3.CanEdit = true
r3.RefID = uniqueid.Generate()
r3.LabelID = spaceID
r3.OrgID = ctx.OrgID
r3.UserID = "testAddRole"
perm := space.Permission{}
perm.OrgID = ctx.OrgID
perm.Who = "user"
perm.WhoID = ctx.UserID
perm.Scope = "object"
perm.Location = "space"
perm.RefID = spaceID
perm.Action = "" // we send array for actions below
err = s.Space.AddRole(ctx, r3)
err = s.Space.AddPermissions(ctx, perm, space.DocumentAdd, space.DocumentDelete, space.DocumentMove)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to add role")
return
t.Error("failed to add permission")
}
ctx.Transaction.Commit()
roles, err := s.Space.GetRoles(ctx, spaceID)
roles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err != nil || roles == nil {
t.Error("Could not get any roles")
return
@ -194,61 +199,16 @@ func TestSpace(t *testing.T) {
// TODO: could we Verify the role was added with the if r3.UserID == Returned.UserID?
})
t.Run("Get User Roles", func(t *testing.T) {
userRoles, err := s.Space.GetUserRoles(ctx)
t.Run("Get User Permissions", func(t *testing.T) {
userRoles, err := s.Space.GetUserPermissions(ctx, spaceID)
if err != nil || userRoles == nil {
t.Error("failed to get user roles")
return
}
})
t.Run("Move space Roles", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
err := s.Space.MoveSpaceRoles(ctx, spaceID, spaceID2)
if err != nil {
ctx.Transaction.Rollback()
t.Error("failed to move space roles")
return
}
ctx.Transaction.Commit()
})
t.Run("Delete Role", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
rowsDeleted, err := s.Space.DeleteRole(ctx, r3.RefID)
if err != nil || rowsDeleted == 0 {
ctx.Transaction.Rollback()
t.Error("failed to delete roles")
return
}
ctx.Transaction.Commit()
})
t.Run("Delete space Roles", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err := s.Space.DeleteSpaceRoles(ctx, spaceID)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
t.Error("failed to delete space roles")
return
}
ctx.Transaction.Commit()
})
t.Run("Delete user space Roles", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err := s.Space.DeleteUserSpaceRoles(ctx, spaceID2, ctx.UserID)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
t.Error("failed to delete user space roles")
return
}
ctx.Transaction.Commit()
})
//Delete spaces last, otherwise tests may fail
t.Run("Delete Space", func(t *testing.T) {
// teardown
t.Run("Delete space", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err = s.Space.Delete(ctx, spaceID)
@ -261,11 +221,7 @@ func TestSpace(t *testing.T) {
ctx.Transaction.Commit()
})
//
// teardown code goes here
//
t.Run("Delete sp2 Space", func(t *testing.T) {
t.Run("Delete space 2", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
_, err = s.Space.Delete(ctx, spaceID2)
@ -277,15 +233,4 @@ func TestSpace(t *testing.T) {
ctx.Transaction.Commit()
})
t.Run("Delete r Role", func(t *testing.T) {
ctx.Transaction, err = rt.Db.Beginx()
rowsDeleted, err := s.Space.DeleteRole(ctx, r.RefID)
if err != nil || rowsDeleted == 0 {
ctx.Transaction.Rollback()
t.Error("failed to delete role r in teardown")
return
}
ctx.Transaction.Commit()
})
}

View file

@ -56,13 +56,13 @@ type SpaceStorer interface {
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)
AddPermission(ctx RequestContext, r space.Permission) (err error)
AddPermissions(ctx RequestContext, r space.Permission, actions ...space.PermissionAction) (err error)
GetUserPermissions(ctx RequestContext, spaceID string) (r []space.Permission, err error)
GetPermissions(ctx RequestContext, spaceID string) (r []space.Permission, err error)
DeleteUserPermissions(ctx RequestContext, spaceID, userID string) (rows int64, err error)
DeletePermissions(ctx RequestContext, spaceID string) (rows int64, err error)
}
// UserStorer defines required methods for user management

View file

@ -538,31 +538,6 @@ func (h *Handler) ChangePassword(w http.ResponseWriter, r *http.Request) {
response.WriteEmpty(w)
}
// UserSpacePermissions returns folder permission for authenticated user.
func (h *Handler) UserSpacePermissions(w http.ResponseWriter, r *http.Request) {
method := "user.UserSpacePermissions"
ctx := domain.GetRequestContext(r)
userID := request.Param(r, "userID")
if userID != ctx.UserID {
response.WriteForbiddenError(w)
return
}
roles, err := h.Store.Space.GetUserRoles(ctx)
if err == sql.ErrNoRows {
err = nil
roles = []space.Role{}
}
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
response.WriteJSON(w, roles)
}
// ForgotPassword 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.