mirror of
https://github.com/documize/community.git
synced 2025-08-02 20:15:26 +02:00
Improve Space permissions
Closes out loopholes that allowed managers to kick owners.
This commit is contained in:
parent
09635b67ab
commit
5d632712e0
30 changed files with 1015 additions and 877 deletions
|
@ -504,8 +504,11 @@ func (h *Handler) FetchSpaceData(w http.ResponseWriter, r *http.Request) {
|
|||
fetch := category.FetchSpaceModel{}
|
||||
|
||||
// get space categories visible to user
|
||||
cat, err := h.Store.Category.GetBySpace(ctx, spaceID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
var cat []category.Category
|
||||
var err error
|
||||
|
||||
cat, err = h.Store.Category.GetBySpace(ctx, spaceID)
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get space categories visible to user failed", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
|
@ -527,7 +530,7 @@ func (h *Handler) FetchSpaceData(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// get category membership records
|
||||
member, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get document category membership for space", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
|
|
|
@ -51,6 +51,8 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
org.StripSecrets()
|
||||
|
||||
response.WriteJSON(w, org)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ type Store struct {
|
|||
func (s Store) AddOrganization(ctx domain.RequestContext, o org.Organization) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, c_domain, c_email, c_anonaccess, c_serial, c_maxtags, c_sub, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||
o.RefID, o.Company, o.Title, o.Message, strings.ToLower(o.Domain),
|
||||
strings.ToLower(o.Email), o.AllowAnonymousAccess, o.Serial, o.MaxTags, o.Subscription, o.Created, o.Revised)
|
||||
strings.ToLower(o.Email), o.AllowAnonymousAccess, o.Serial, o.MaxTags,
|
||||
o.Subscription, o.Created, o.Revised)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to execute insert for org")
|
||||
|
@ -43,13 +44,14 @@ func (s Store) AddOrganization(ctx domain.RequestContext, o org.Organization) (e
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetOrganization returns the Organization reocrod from the organization database table with the given id.
|
||||
// GetOrganization returns the Organization record from the organization database table with the given id.
|
||||
func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Organization, err error) {
|
||||
err = s.Runtime.Db.Get(&org, s.Bind(`SELECT id, c_refid AS refid,
|
||||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
|
||||
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_refid=?`),
|
||||
|
@ -80,7 +82,8 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
|
||||
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_domain=? AND c_active=true`),
|
||||
|
@ -95,7 +98,8 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
c_title AS title, c_message AS message, c_domain AS domain,
|
||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
|
||||
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_domain='' AND c_active=true`))
|
||||
|
|
|
@ -120,11 +120,6 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
|
||||
me := false
|
||||
hasEveryoneRole := false
|
||||
roleCount := 0
|
||||
|
||||
// Permissions can be assigned to both groups and individual users.
|
||||
// Pre-fetch users with group membership to help us work out
|
||||
// if user belongs to a group with permissions.
|
||||
|
@ -136,6 +131,18 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// url is sent in 'space shared with you' invitation emails.
|
||||
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
|
||||
// me tracks if the user who changed permissions, also has some space permissions.
|
||||
me := false
|
||||
// hasEveryRole tracks if "everyone" has been give access to space.
|
||||
hasEveryoneRole := false
|
||||
// hasOwner tracks is at least one person or user group has been marked as space owner.
|
||||
hasOwner := false
|
||||
// roleCount tracks the number of permission records created for this space.
|
||||
// It's used to determine if space has multiple participants, see below.
|
||||
roleCount := 0
|
||||
|
||||
for _, perm := range model.Permissions {
|
||||
perm.OrgID = ctx.OrgID
|
||||
perm.SpaceID = id
|
||||
|
@ -154,6 +161,12 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
me = true
|
||||
}
|
||||
|
||||
// Detect is we have at least one space owner permission.
|
||||
// Result used below to prevent lock-outs.
|
||||
if hasOwner == false && perm.SpaceOwner {
|
||||
hasOwner = true
|
||||
}
|
||||
|
||||
// Only persist if there is a role!
|
||||
if permission.HasAnyPermission(perm) {
|
||||
// identify publically shared spaces
|
||||
|
@ -209,8 +222,11 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// Do we need to ensure permissions for space owner when shared?
|
||||
if !me {
|
||||
// Catch and prevent lock-outs so we don't have
|
||||
// zombie spaces that nobody can access.
|
||||
if len(model.Permissions) == 0 {
|
||||
// When no permissions are assigned we
|
||||
// default to current user as being owner and viewer.
|
||||
perm := permission.Permission{}
|
||||
perm.OrgID = ctx.OrgID
|
||||
perm.Who = permission.UserPermission
|
||||
|
@ -218,15 +234,37 @@ func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
|||
perm.Scope = permission.ScopeRow
|
||||
perm.Location = permission.LocationSpace
|
||||
perm.RefID = id
|
||||
perm.Action = "" // we send array for actions below
|
||||
|
||||
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceView, permission.SpaceManage)
|
||||
perm.Action = "" // we send allowable actions in function call...
|
||||
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceOwner, permission.SpaceView)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// So we have permissions but we must check for at least one space owner.
|
||||
if !hasOwner {
|
||||
// So we have no space owner, make current user the owner
|
||||
// if we have no permssions thus far.
|
||||
if !me {
|
||||
perm := permission.Permission{}
|
||||
perm.OrgID = ctx.OrgID
|
||||
perm.Who = permission.UserPermission
|
||||
perm.WhoID = ctx.UserID
|
||||
perm.Scope = permission.ScopeRow
|
||||
perm.Location = permission.LocationSpace
|
||||
perm.RefID = id
|
||||
perm.Action = "" // we send allowable actions in function call...
|
||||
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceOwner, permission.SpaceView)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark up space type as either public, private or restricted access.
|
||||
|
|
|
@ -116,24 +116,35 @@ func (s Store) GetViewable(ctx domain.RequestContext) (sp []space.Space, err err
|
|||
return
|
||||
}
|
||||
|
||||
// GetAll for admin users!
|
||||
func (s Store) GetAll(ctx domain.RequestContext) (sp []space.Space, err error) {
|
||||
qry := s.Bind(`SELECT id, c_refid AS refid,
|
||||
// AdminList returns all shared spaces and orphaned spaces that have no owner.
|
||||
func (s Store) AdminList(ctx domain.RequestContext) (sp []space.Space, err error) {
|
||||
qry := s.Bind(`
|
||||
SELECT id, c_refid AS refid,
|
||||
c_name AS name, c_orgid AS orgid, c_userid AS userid,
|
||||
c_type AS type, c_lifecycle AS lifecycle, c_likes AS likes,
|
||||
c_created AS created, c_revised AS revised
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=?
|
||||
ORDER BY c_name`)
|
||||
|
||||
err = s.Runtime.Db.Select(&sp, qry, ctx.OrgID)
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND (c_type=? OR c_type=?)
|
||||
UNION ALL
|
||||
SELECT id, c_refid AS refid,
|
||||
c_name AS name, c_orgid AS orgid, c_userid AS userid,
|
||||
c_type AS type, c_lifecycle AS lifecycle, c_likes AS likes,
|
||||
c_created AS created, c_revised AS revised
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND (c_type=? OR c_type=?) AND c_refid NOT IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_action='own')
|
||||
ORDER BY name`)
|
||||
|
||||
err = s.Runtime.Db.Select(&sp, qry,
|
||||
ctx.OrgID, space.ScopePublic, space.ScopeRestricted,
|
||||
ctx.OrgID, space.ScopePublic, space.ScopeRestricted,
|
||||
ctx.OrgID)
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
sp = []space.Space{}
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("failed space.GetAll org %s", ctx.OrgID))
|
||||
err = errors.Wrap(err, fmt.Sprintf("failed space.AdminList org %s", ctx.OrgID))
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -59,9 +59,9 @@ type SpaceStorer interface {
|
|||
Get(ctx domain.RequestContext, id string) (sp space.Space, err error)
|
||||
PublicSpaces(ctx domain.RequestContext, orgID string) (sp []space.Space, err error)
|
||||
GetViewable(ctx domain.RequestContext) (sp []space.Space, err error)
|
||||
GetAll(ctx domain.RequestContext) (sp []space.Space, err error)
|
||||
Update(ctx domain.RequestContext, sp space.Space) (err error)
|
||||
Delete(ctx domain.RequestContext, id string) (rows int64, err error)
|
||||
AdminList(ctx domain.RequestContext) (sp []space.Space, err error)
|
||||
}
|
||||
|
||||
// CategoryStorer defines required methods for category and category membership management
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue