mirror of
https://github.com/documize/community.git
synced 2025-07-23 23:29:42 +02:00
refactored permission code
This commit is contained in:
parent
c12c000ef3
commit
6a651770b5
24 changed files with 753 additions and 632 deletions
|
@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS `permission` (
|
||||||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||||
ENGINE = MyISAM;
|
ENGINE = MyISAM;
|
||||||
|
|
||||||
-- category represents "folder/label" assignment to document (1:M)
|
-- category represents "folder/label/category" assignment to document (1:M)
|
||||||
DROP TABLE IF EXISTS `category`;
|
DROP TABLE IF EXISTS `category`;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `category` (
|
CREATE TABLE IF NOT EXISTS `category` (
|
||||||
|
@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS `category` (
|
||||||
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`labelid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`labelid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`label` VARCHAR(30) NOT NULL,
|
`category` VARCHAR(30) NOT NULL,
|
||||||
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
UNIQUE INDEX `idx_category_id` (`id` ASC),
|
UNIQUE INDEX `idx_category_id` (`id` ASC),
|
||||||
INDEX `idx_category_refid` (`refid` ASC),
|
INDEX `idx_category_refid` (`refid` ASC),
|
||||||
|
@ -45,6 +45,7 @@ DROP TABLE IF EXISTS `categorymember`;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `categorymember` (
|
CREATE TABLE IF NOT EXISTS `categorymember` (
|
||||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`categoryid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`categoryid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
|
|
@ -25,8 +25,8 @@ import (
|
||||||
"github.com/documize/community/core/secrets"
|
"github.com/documize/community/core/secrets"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
|
||||||
"github.com/documize/community/domain/organization"
|
"github.com/documize/community/domain/organization"
|
||||||
|
"github.com/documize/community/domain/permission"
|
||||||
indexer "github.com/documize/community/domain/search"
|
indexer "github.com/documize/community/domain/search"
|
||||||
"github.com/documize/community/model/attachment"
|
"github.com/documize/community/model/attachment"
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
|
@ -89,7 +89,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"github.com/documize/community/core/streamutil"
|
"github.com/documize/community/core/streamutil"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
"github.com/documize/community/domain/permission"
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
"github.com/documize/community/model/block"
|
"github.com/documize/community/model/block"
|
||||||
)
|
)
|
||||||
|
@ -57,7 +57,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanUploadDocument(ctx, *h.Store, b.LabelID) {
|
if !permission.CanUploadDocument(ctx, *h.Store, b.LabelID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
b.RefID = blockID
|
b.RefID = blockID
|
||||||
|
|
||||||
if !document.CanUploadDocument(ctx, *h.Store, b.LabelID) {
|
if !permission.CanUploadDocument(ctx, *h.Store, b.LabelID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/conversion/store"
|
"github.com/documize/community/domain/conversion/store"
|
||||||
"github.com/documize/community/domain/document"
|
"github.com/documize/community/domain/permission"
|
||||||
"github.com/documize/community/model/activity"
|
"github.com/documize/community/model/activity"
|
||||||
"github.com/documize/community/model/attachment"
|
"github.com/documize/community/model/attachment"
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
|
@ -50,7 +50,7 @@ func (h *Handler) upload(w http.ResponseWriter, r *http.Request) (string, string
|
||||||
|
|
||||||
folderID := request.Param(r, "folderID")
|
folderID := request.Param(r, "folderID")
|
||||||
|
|
||||||
if !document.CanUploadDocument(ctx, *h.Store, folderID) {
|
if !permission.CanUploadDocument(ctx, *h.Store, folderID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return "", "", ""
|
return "", "", ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"github.com/documize/community/core/streamutil"
|
"github.com/documize/community/core/streamutil"
|
||||||
"github.com/documize/community/core/stringutil"
|
"github.com/documize/community/core/stringutil"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
|
"github.com/documize/community/domain/permission"
|
||||||
indexer "github.com/documize/community/domain/search"
|
indexer "github.com/documize/community/domain/search"
|
||||||
"github.com/documize/community/domain/space"
|
|
||||||
"github.com/documize/community/model/activity"
|
"github.com/documize/community/model/activity"
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
"github.com/documize/community/model/doc"
|
"github.com/documize/community/model/doc"
|
||||||
|
@ -61,7 +61,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !CanViewDocumentInFolder(ctx, *h.Store, document.LabelID) {
|
if !permission.CanViewSpaceDocument(ctx, *h.Store, document.LabelID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !space.CanViewSpace(ctx, *h.Store, spaceID) {
|
if !permission.CanViewSpace(ctx, *h.Store, spaceID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if !CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanDeleteDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,117 +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 document
|
|
||||||
|
|
||||||
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) bool {
|
|
||||||
roles, err := s.Space.GetUserPermissions(ctx, labelID)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
if role.RefID == labelID && role.Location == "space" && role.Scope == "object" &&
|
|
||||||
sp.HasPermission(role.Action, sp.SpaceView, sp.SpaceManage, sp.SpaceOwner) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
roles, err := s.Space.GetUserPermissions(ctx, document.LabelID)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" &&
|
|
||||||
sp.HasPermission(role.Action, sp.SpaceView, sp.SpaceManage, sp.SpaceOwner) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanChangeDocument returns if the clinet has permission to change a given document.
|
|
||||||
func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID string) bool {
|
|
||||||
document, err := s.Document.Get(ctx, documentID)
|
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
roles, err := s.Space.GetUserPermissions(ctx, document.LabelID)
|
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" &&
|
|
||||||
sp.HasPermission(role.Action, sp.DocumentEdit) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
|
|
||||||
sp.HasPermission(role.Action, sp.DocumentAdd) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"github.com/documize/community/core/response"
|
"github.com/documize/community/core/response"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
"github.com/documize/community/domain/permission"
|
||||||
"github.com/documize/community/model/attachment"
|
"github.com/documize/community/model/attachment"
|
||||||
"github.com/documize/community/model/link"
|
"github.com/documize/community/model/link"
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
|
@ -57,7 +57,7 @@ func (h *Handler) GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// permission check
|
// permission check
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ import (
|
||||||
"github.com/documize/community/core/streamutil"
|
"github.com/documize/community/core/streamutil"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
|
||||||
"github.com/documize/community/domain/link"
|
"github.com/documize/community/domain/link"
|
||||||
|
"github.com/documize/community/domain/permission"
|
||||||
indexer "github.com/documize/community/domain/search"
|
indexer "github.com/documize/community/domain/search"
|
||||||
"github.com/documize/community/domain/section/provider"
|
"github.com/documize/community/domain/section/provider"
|
||||||
"github.com/documize/community/model/activity"
|
"github.com/documize/community/model/activity"
|
||||||
|
@ -59,7 +59,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ func (h *Handler) GetPage(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ func (h *Handler) GetPages(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ func (h *Handler) GetPagesBatch(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -366,7 +366,7 @@ func (h *Handler) DeletePages(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -617,7 +617,7 @@ func (h *Handler) ChangePageSequence(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -678,7 +678,7 @@ func (h *Handler) ChangePageLevel(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -740,7 +740,7 @@ func (h *Handler) GetMeta(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -816,7 +816,7 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// permission
|
// permission
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -922,7 +922,7 @@ func (h *Handler) GetDocumentRevisions(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -953,7 +953,7 @@ func (h *Handler) GetRevisions(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1000,7 +1000,7 @@ func (h *Handler) GetDiff(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1064,7 @@ func (h *Handler) Rollback(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
278
domain/permission/endpoint.go
Normal file
278
domain/permission/endpoint.go
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
// 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 permission handles API calls and persistence for spaces.
|
||||||
|
// Spaces in Documize contain documents.
|
||||||
|
package permission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"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/core/stringutil"
|
||||||
|
"github.com/documize/community/domain"
|
||||||
|
"github.com/documize/community/domain/mail"
|
||||||
|
"github.com/documize/community/model/audit"
|
||||||
|
"github.com/documize/community/model/permission"
|
||||||
|
"github.com/documize/community/model/space"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler contains the runtime information such as logging and database.
|
||||||
|
type Handler struct {
|
||||||
|
Runtime *env.Runtime
|
||||||
|
Store *domain.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSpacePermissions persists specified space permissions
|
||||||
|
func (h *Handler) SetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "space.SetPermissions"
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
|
||||||
|
if !ctx.Editor {
|
||||||
|
response.WriteForbiddenError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := request.Param(r, "spaceID")
|
||||||
|
if len(id) == 0 {
|
||||||
|
response.WriteMissingDataError(w, method, "spaceID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sp, err := h.Store.Space.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteNotFoundError(w, method, "space not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp.UserID != ctx.UserID {
|
||||||
|
response.WriteForbiddenError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer streamutil.Close(r.Body)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteBadRequestError(w, method, err.Error())
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = permission.PermissionsModel{}
|
||||||
|
err = json.Unmarshal(body, &model)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
||||||
|
if err != nil {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We compare new permisions to what we had before.
|
||||||
|
// Why? So we can send out space invitation emails.
|
||||||
|
previousRoles, err := h.Store.Permission.GetSpacePermissions(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all previous roles as map for easy querying
|
||||||
|
previousRoleUsers := make(map[string]bool)
|
||||||
|
for _, v := range previousRoles {
|
||||||
|
previousRoleUsers[v.WhoID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Who is sharing this space?
|
||||||
|
inviter, err := h.Store.User.Get(ctx, ctx.UserID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nuke all previous permissions for this space
|
||||||
|
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
me := false
|
||||||
|
hasEveryoneRole := false
|
||||||
|
roleCount := 0
|
||||||
|
|
||||||
|
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
|
||||||
|
|
||||||
|
for _, perm := range model.Permissions {
|
||||||
|
perm.OrgID = ctx.OrgID
|
||||||
|
perm.SpaceID = id
|
||||||
|
|
||||||
|
// Ensure the space owner always has access!
|
||||||
|
if perm.UserID == ctx.UserID {
|
||||||
|
me = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only persist if there is a role!
|
||||||
|
if permission.HasAnyPermission(perm) {
|
||||||
|
// identify publically shared spaces
|
||||||
|
if len(perm.UserID) == 0 {
|
||||||
|
hasEveryoneRole = true
|
||||||
|
}
|
||||||
|
|
||||||
|
r := permission.EncodeUserPermissions(perm)
|
||||||
|
|
||||||
|
for _, p := range r {
|
||||||
|
err = h.Store.Permission.AddPermission(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Error("set permission", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
roleCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// We send out space invitation emails to those users
|
||||||
|
// that have *just* been given permissions.
|
||||||
|
if _, isExisting := previousRoleUsers[perm.UserID]; !isExisting {
|
||||||
|
|
||||||
|
// we skip 'everyone' (user id != empty string)
|
||||||
|
if len(perm.UserID) > 0 {
|
||||||
|
existingUser, err := h.Store.User.Get(ctx, perm.UserID)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to ensure permissions for space owner when shared?
|
||||||
|
if !me {
|
||||||
|
perm := permission.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.Permission.AddPermissions(ctx, perm, permission.SpaceView, permission.SpaceManage)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark up space type as either public, private or restricted access.
|
||||||
|
if hasEveryoneRole {
|
||||||
|
sp.Type = space.ScopePublic
|
||||||
|
} else {
|
||||||
|
if roleCount > 1 {
|
||||||
|
sp.Type = space.ScopeRestricted
|
||||||
|
} else {
|
||||||
|
sp.Type = space.ScopePrivate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.Store.Space.Update(ctx, sp)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Store.Audit.Record(ctx, audit.EventTypeSpacePermission)
|
||||||
|
|
||||||
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
response.WriteEmpty(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSpacePermissions returns permissions for alll users for given space.
|
||||||
|
func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "space.GetPermissions"
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
|
||||||
|
spaceID := request.Param(r, "spaceID")
|
||||||
|
if len(spaceID) == 0 {
|
||||||
|
response.WriteMissingDataError(w, method, "spaceID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
perms, err := h.Store.Permission.GetSpacePermissions(ctx, spaceID)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(perms) == 0 {
|
||||||
|
perms = []permission.Permission{}
|
||||||
|
}
|
||||||
|
|
||||||
|
userPerms := make(map[string][]permission.Permission)
|
||||||
|
for _, p := range perms {
|
||||||
|
userPerms[p.WhoID] = append(userPerms[p.WhoID], p)
|
||||||
|
}
|
||||||
|
|
||||||
|
records := []permission.Record{}
|
||||||
|
for _, up := range userPerms {
|
||||||
|
records = append(records, permission.DecodeUserPermissions(up))
|
||||||
|
}
|
||||||
|
|
||||||
|
response.WriteJSON(w, records)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserSpacePermissions returns permissions for the requested space, for current user.
|
||||||
|
func (h *Handler) GetUserSpacePermissions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "space.GetUserSpacePermissions"
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
|
||||||
|
spaceID := request.Param(r, "spaceID")
|
||||||
|
if len(spaceID) == 0 {
|
||||||
|
response.WriteMissingDataError(w, method, "spaceID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(perms) == 0 {
|
||||||
|
perms = []permission.Permission{}
|
||||||
|
}
|
||||||
|
|
||||||
|
record := permission.DecodeUserPermissions(perms)
|
||||||
|
response.WriteJSON(w, record)
|
||||||
|
}
|
136
domain/permission/mysql/store.go
Normal file
136
domain/permission/mysql/store.go
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// 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 handles data persistence for space and document permissions.
|
||||||
|
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/permission"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scope provides data access to MySQL.
|
||||||
|
type Scope struct {
|
||||||
|
Runtime *env.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPermission inserts the given record into the permisssion table.
|
||||||
|
func (s Scope) AddPermission(ctx domain.RequestContext, r permission.Permission) (err error) {
|
||||||
|
r.Created = time.Now().UTC()
|
||||||
|
|
||||||
|
stmt, err := ctx.Transaction.Preparex("INSERT INTO permission (orgid, who, whoid, action, scope, location, refid, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
|
defer streamutil.Close(stmt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "unable to prepare insert permission")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(r.OrgID, r.Who, r.WhoID, string(r.Action), r.Scope, r.Location, r.RefID, r.Created)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "unable to execute insert permission")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPermissions inserts records into permission database table, one per action.
|
||||||
|
func (s Scope) AddPermissions(ctx domain.RequestContext, r permission.Permission, actions ...permission.Action) (err error) {
|
||||||
|
for _, a := range actions {
|
||||||
|
r.Action = a
|
||||||
|
s.AddPermission(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserSpacePermissions returns space permissions for user.
|
||||||
|
// Context is used to for user ID.
|
||||||
|
func (s Scope) GetUserSpacePermissions(ctx domain.RequestContext, spaceID string) (r []permission.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 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 user permissions %s", ctx.UserID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSpacePermissions returns space permissions for all users.
|
||||||
|
func (s Scope) GetSpacePermissions(ctx domain.RequestContext, spaceID string) (r []permission.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'
|
||||||
|
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'`,
|
||||||
|
ctx.OrgID, spaceID, ctx.OrgID, spaceID)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSpacePermissions removes records from permissions table for given space ID.
|
||||||
|
func (s Scope) DeleteSpacePermissions(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
|
||||||
|
b := mysql.BaseQuery{}
|
||||||
|
|
||||||
|
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND location='space' AND refid='%s'", ctx.OrgID, spaceID)
|
||||||
|
|
||||||
|
return b.DeleteWhere(ctx.Transaction, sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserSpacePermissions removes all roles for the specified user, for the specified space.
|
||||||
|
func (s Scope) DeleteUserSpacePermissions(ctx domain.RequestContext, spaceID, userID string) (rows int64, err error) {
|
||||||
|
b := mysql.BaseQuery{}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserPermissions removes all roles for the specified user, for the specified space.
|
||||||
|
func (s Scope) DeleteUserPermissions(ctx domain.RequestContext, userID string) (rows int64, err error) {
|
||||||
|
b := mysql.BaseQuery{}
|
||||||
|
|
||||||
|
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND who='user' AND whoid='%s'",
|
||||||
|
ctx.OrgID, userID)
|
||||||
|
|
||||||
|
return b.DeleteWhere(ctx.Transaction, sql)
|
||||||
|
}
|
194
domain/permission/permission.go
Normal file
194
domain/permission/permission.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
// 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 permission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/documize/community/domain"
|
||||||
|
pm "github.com/documize/community/model/permission"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CanViewSpaceDocument returns if the user has permission to view a document within the specified folder.
|
||||||
|
func CanViewSpaceDocument(ctx domain.RequestContext, s domain.Store, labelID string) bool {
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, labelID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == labelID && role.Location == "space" && role.Scope == "object" &&
|
||||||
|
pm.HasPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" &&
|
||||||
|
pm.HasPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanChangeDocument returns if the clinet has permission to change a given document.
|
||||||
|
func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID string) bool {
|
||||||
|
document, err := s.Document.Get(ctx, documentID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" && role.Action == pm.DocumentEdit {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDeleteDocument returns if the clinet has permission to change a given document.
|
||||||
|
func CanDeleteDocument(ctx domain.RequestContext, s domain.Store, documentID string) bool {
|
||||||
|
document, err := s.Document.Get(ctx, documentID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" && role.Action == pm.DocumentDelete {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
|
||||||
|
pm.HasPermission(role.Action, pm.DocumentAdd) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanViewSpace returns if the user has permission to view the given spaceID.
|
||||||
|
func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) bool {
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, spaceID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
|
||||||
|
pm.HasPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasDocumentAction returns if user can perform specified action.
|
||||||
|
func HasDocumentAction(ctx domain.RequestContext, s domain.Store, documentID string, a pm.Action) bool {
|
||||||
|
document, err := s.Document.Get(ctx, documentID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.Permission.GetUserSpacePermissions(ctx, document.LabelID)
|
||||||
|
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" && role.Action == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"github.com/documize/community/core/response"
|
"github.com/documize/community/core/response"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
"github.com/documize/community/domain/permission"
|
||||||
"github.com/documize/community/domain/section/provider"
|
"github.com/documize/community/domain/section/provider"
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ func (h *Handler) RunSectionCommand(w http.ResponseWriter, r *http.Request) {
|
||||||
// it's up to the section handler to parse if required.
|
// it's up to the section handler to parse if required.
|
||||||
|
|
||||||
// Permission checks
|
// Permission checks
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, documentID) {
|
if !permission.CanChangeDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func (h *Handler) RefreshSections(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanViewDocument(ctx, *h.Store, documentID) {
|
if !permission.CanViewDocument(ctx, *h.Store, documentID) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
"github.com/documize/community/model/doc"
|
"github.com/documize/community/model/doc"
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
|
"github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/space"
|
"github.com/documize/community/model/space"
|
||||||
uuid "github.com/nu7hatch/gouuid"
|
uuid "github.com/nu7hatch/gouuid"
|
||||||
)
|
)
|
||||||
|
@ -105,7 +106,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
perm := space.Permission{}
|
perm := permission.Permission{}
|
||||||
perm.OrgID = sp.OrgID
|
perm.OrgID = sp.OrgID
|
||||||
perm.Who = "user"
|
perm.Who = "user"
|
||||||
perm.WhoID = ctx.UserID
|
perm.WhoID = ctx.UserID
|
||||||
|
@ -114,7 +115,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
perm.RefID = sp.RefID
|
perm.RefID = sp.RefID
|
||||||
perm.Action = "" // we send array for actions below
|
perm.Action = "" // we send array for actions below
|
||||||
|
|
||||||
err = h.Store.Space.AddPermissions(ctx, perm, space.SpaceOwner, space.SpaceManage, space.SpaceView)
|
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceOwner, permission.SpaceManage, permission.SpaceView)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
@ -138,7 +139,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
spCloneRoles, err := h.Store.Space.GetPermissions(ctx, model.CloneID)
|
spCloneRoles, err := h.Store.Permission.GetSpacePermissions(ctx, model.CloneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
h.Runtime.Log.Error(method, err)
|
h.Runtime.Log.Error(method, err)
|
||||||
|
@ -149,7 +150,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, r := range spCloneRoles {
|
for _, r := range spCloneRoles {
|
||||||
r.RefID = sp.RefID
|
r.RefID = sp.RefID
|
||||||
|
|
||||||
err = h.Store.Space.AddPermission(ctx, r)
|
err = h.Store.Permission.AddPermission(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
@ -451,7 +452,7 @@ func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = h.Store.Space.DeletePermissions(ctx, id)
|
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
@ -519,7 +520,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = h.Store.Space.DeletePermissions(ctx, id)
|
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
@ -542,245 +543,6 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
response.WriteEmpty(w)
|
response.WriteEmpty(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPermissions persists specified spac3 permissions
|
|
||||||
func (h *Handler) SetPermissions(w http.ResponseWriter, r *http.Request) {
|
|
||||||
method := "space.SetPermissions"
|
|
||||||
ctx := domain.GetRequestContext(r)
|
|
||||||
|
|
||||||
if !ctx.Editor {
|
|
||||||
response.WriteForbiddenError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
id := request.Param(r, "spaceID")
|
|
||||||
if len(id) == 0 {
|
|
||||||
response.WriteMissingDataError(w, method, "spaceID")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sp, err := h.Store.Space.Get(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
response.WriteNotFoundError(w, method, "space not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if sp.UserID != ctx.UserID {
|
|
||||||
response.WriteForbiddenError(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer streamutil.Close(r.Body)
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
response.WriteBadRequestError(w, method, err.Error())
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = space.PermissionsModel{}
|
|
||||||
err = json.Unmarshal(body, &model)
|
|
||||||
if err != nil {
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We compare new permisions to what we had before.
|
|
||||||
// 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)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store all previous roles as map for easy querying
|
|
||||||
previousRoleUsers := make(map[string]bool)
|
|
||||||
for _, v := range previousRoles {
|
|
||||||
previousRoleUsers[v.WhoID] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Who is sharing this space?
|
|
||||||
inviter, err := h.Store.User.Get(ctx, ctx.UserID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Transaction.Rollback()
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
me := false
|
|
||||||
hasEveryoneRole := false
|
|
||||||
roleCount := 0
|
|
||||||
|
|
||||||
url := ctx.GetAppURL(fmt.Sprintf("s/%s/%s", sp.RefID, stringutil.MakeSlug(sp.Name)))
|
|
||||||
|
|
||||||
for _, perm := range model.Permissions {
|
|
||||||
perm.OrgID = ctx.OrgID
|
|
||||||
perm.SpaceID = id
|
|
||||||
|
|
||||||
// Ensure the space owner always has access!
|
|
||||||
if perm.UserID == ctx.UserID {
|
|
||||||
me = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only persist if there is a role!
|
|
||||||
if space.HasAnyPermission(perm) {
|
|
||||||
// identify publically shared spaces
|
|
||||||
if len(perm.UserID) == 0 {
|
|
||||||
hasEveryoneRole = true
|
|
||||||
}
|
|
||||||
|
|
||||||
r := space.EncodeUserPermissions(perm)
|
|
||||||
|
|
||||||
for _, p := range r {
|
|
||||||
err = h.Store.Space.AddPermission(ctx, p)
|
|
||||||
if err != nil {
|
|
||||||
h.Runtime.Log.Error("set permission", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
roleCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
// We send out space invitation emails to those users
|
|
||||||
// that have *just* been given permissions.
|
|
||||||
if _, isExisting := previousRoleUsers[perm.UserID]; !isExisting {
|
|
||||||
|
|
||||||
// we skip 'everyone' (user id != empty string)
|
|
||||||
if len(perm.UserID) > 0 {
|
|
||||||
existingUser, err := h.Store.User.Get(ctx, perm.UserID)
|
|
||||||
if err != nil {
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx}
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we need to ensure permissions for space owner when shared?
|
|
||||||
if !me {
|
|
||||||
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.AddPermissions(ctx, perm, space.SpaceView, space.SpaceManage)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Transaction.Rollback()
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark up space type as either public, private or restricted access.
|
|
||||||
if hasEveryoneRole {
|
|
||||||
sp.Type = space.ScopePublic
|
|
||||||
} else {
|
|
||||||
if roleCount > 1 {
|
|
||||||
sp.Type = space.ScopeRestricted
|
|
||||||
} else {
|
|
||||||
sp.Type = space.ScopePrivate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.Store.Space.Update(ctx, sp)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Transaction.Rollback()
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
h.Runtime.Log.Error(method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
h.Store.Audit.Record(ctx, audit.EventTypeSpacePermission)
|
|
||||||
|
|
||||||
ctx.Transaction.Commit()
|
|
||||||
|
|
||||||
response.WriteEmpty(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPermissions returns permissions for alll users for given space.
|
|
||||||
func (h *Handler) GetPermissions(w http.ResponseWriter, r *http.Request) {
|
|
||||||
method := "space.GetPermissions"
|
|
||||||
ctx := domain.GetRequestContext(r)
|
|
||||||
|
|
||||||
spaceID := request.Param(r, "spaceID")
|
|
||||||
if len(spaceID) == 0 {
|
|
||||||
response.WriteMissingDataError(w, method, "spaceID")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
perms, err := h.Store.Space.GetPermissions(ctx, spaceID)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
response.WriteServerError(w, method, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(perms) == 0 {
|
|
||||||
perms = []space.Permission{}
|
|
||||||
}
|
|
||||||
|
|
||||||
userPerms := make(map[string][]space.Permission)
|
|
||||||
for _, p := range perms {
|
|
||||||
userPerms[p.WhoID] = append(userPerms[p.WhoID], p)
|
|
||||||
}
|
|
||||||
|
|
||||||
records := []space.PermissionRecord{}
|
|
||||||
for _, up := range userPerms {
|
|
||||||
records = append(records, space.DecodeUserPermissions(up))
|
|
||||||
}
|
|
||||||
|
|
||||||
response.WriteJSON(w, records)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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{}
|
|
||||||
}
|
|
||||||
|
|
||||||
record := space.DecodeUserPermissions(perms)
|
|
||||||
response.WriteJSON(w, record)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcceptInvitation records the fact that a user has completed space onboard process.
|
// AcceptInvitation records the fact that a user has completed space onboard process.
|
||||||
func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
|
||||||
method := "space.AcceptInvitation"
|
method := "space.AcceptInvitation"
|
||||||
|
@ -971,9 +733,9 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure they have space roles
|
// Ensure they have space roles
|
||||||
h.Store.Space.DeleteUserPermissions(ctx, sp.RefID, u.RefID)
|
h.Store.Permission.DeleteUserSpacePermissions(ctx, sp.RefID, u.RefID)
|
||||||
|
|
||||||
perm := space.Permission{}
|
perm := permission.Permission{}
|
||||||
perm.OrgID = sp.OrgID
|
perm.OrgID = sp.OrgID
|
||||||
perm.Who = "user"
|
perm.Who = "user"
|
||||||
perm.WhoID = u.RefID
|
perm.WhoID = u.RefID
|
||||||
|
@ -982,7 +744,7 @@ func (h *Handler) Invite(w http.ResponseWriter, r *http.Request) {
|
||||||
perm.RefID = sp.RefID
|
perm.RefID = sp.RefID
|
||||||
perm.Action = "" // we send array for actions below
|
perm.Action = "" // we send array for actions below
|
||||||
|
|
||||||
err = h.Store.Space.AddPermissions(ctx, perm, space.SpaceView)
|
err = h.Store.Permission.AddPermissions(ctx, perm, permission.SpaceView)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -163,108 +162,3 @@ func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err err
|
||||||
b := mysql.BaseQuery{}
|
b := mysql.BaseQuery{}
|
||||||
return b.DeleteConstrained(ctx.Transaction, "label", ctx.OrgID, id)
|
return b.DeleteConstrained(ctx.Transaction, "label", ctx.OrgID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPermission inserts the given record into the permisssion table.
|
|
||||||
func (s Scope) AddPermission(ctx domain.RequestContext, r space.Permission) (err error) {
|
|
||||||
r.Created = time.Now().UTC()
|
|
||||||
|
|
||||||
stmt, err := ctx.Transaction.Preparex("INSERT INTO permission (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 permission")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec(r.OrgID, r.Who, r.WhoID, string(r.Action), r.Scope, r.Location, r.RefID, r.Created)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.Wrap(err, "unable to execute insert for space permission")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = a
|
|
||||||
s.AddPermission(ctx, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, 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 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 user permissions %s", ctx.UserID))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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'
|
|
||||||
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'`,
|
|
||||||
ctx.OrgID, spaceID, ctx.OrgID, spaceID)
|
|
||||||
|
|
||||||
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 permission WHERE orgid='%s' AND location='space' AND refid='%s'", ctx.OrgID, spaceID)
|
|
||||||
|
|
||||||
return b.DeleteWhere(ctx.Transaction, sql)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteAllUserPermissions removes all roles for the specified user, for the specified space.
|
|
||||||
func (s Scope) DeleteAllUserPermissions(ctx domain.RequestContext, userID string) (rows int64, err error) {
|
|
||||||
b := mysql.BaseQuery{}
|
|
||||||
|
|
||||||
sql := fmt.Sprintf("DELETE FROM permission WHERE orgid='%s' AND who='user' AND whoid='%s'",
|
|
||||||
ctx.OrgID, userID)
|
|
||||||
|
|
||||||
return b.DeleteWhere(ctx.Transaction, sql)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,41 +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 space handles API calls and persistence for spaces.
|
|
||||||
// Spaces in Documize contain documents.
|
|
||||||
package space
|
|
||||||
|
|
||||||
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) bool {
|
|
||||||
roles, err := s.Space.GetUserPermissions(ctx, spaceID)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" &&
|
|
||||||
space.HasPermission(role.Action, space.SpaceView, space.SpaceManage, space.SpaceOwner) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/mail"
|
"github.com/documize/community/domain/mail"
|
||||||
"github.com/documize/community/model/account"
|
"github.com/documize/community/model/account"
|
||||||
|
"github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/space"
|
"github.com/documize/community/model/space"
|
||||||
"github.com/documize/community/model/user"
|
"github.com/documize/community/model/user"
|
||||||
)
|
)
|
||||||
|
@ -61,7 +62,7 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *d
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
perm := space.Permission{}
|
perm := permission.Permission{}
|
||||||
perm.OrgID = sp.OrgID
|
perm.OrgID = sp.OrgID
|
||||||
perm.Who = "user"
|
perm.Who = "user"
|
||||||
perm.WhoID = userID
|
perm.WhoID = userID
|
||||||
|
@ -70,7 +71,7 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *d
|
||||||
perm.RefID = sp.RefID
|
perm.RefID = sp.RefID
|
||||||
perm.Action = "" // we send array for actions below
|
perm.Action = "" // we send array for actions below
|
||||||
|
|
||||||
err = s.Space.AddPermissions(ctx, perm, space.SpaceView)
|
err = s.Permission.AddPermissions(ctx, perm, permission.SpaceView)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/documize/community/model/link"
|
"github.com/documize/community/model/link"
|
||||||
"github.com/documize/community/model/org"
|
"github.com/documize/community/model/org"
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
|
"github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/pin"
|
"github.com/documize/community/model/pin"
|
||||||
"github.com/documize/community/model/search"
|
"github.com/documize/community/model/search"
|
||||||
"github.com/documize/community/model/space"
|
"github.com/documize/community/model/space"
|
||||||
|
@ -40,6 +41,7 @@ type Store struct {
|
||||||
Organization OrganizationStorer
|
Organization OrganizationStorer
|
||||||
Page PageStorer
|
Page PageStorer
|
||||||
Pin PinStorer
|
Pin PinStorer
|
||||||
|
Permission PermissionStorer
|
||||||
Search SearchStorer
|
Search SearchStorer
|
||||||
Setting SettingStorer
|
Setting SettingStorer
|
||||||
Space SpaceStorer
|
Space SpaceStorer
|
||||||
|
@ -55,14 +57,17 @@ type SpaceStorer interface {
|
||||||
Update(ctx RequestContext, sp space.Space) (err error)
|
Update(ctx RequestContext, sp space.Space) (err error)
|
||||||
Viewers(ctx RequestContext) (v []space.Viewer, err error)
|
Viewers(ctx RequestContext) (v []space.Viewer, err error)
|
||||||
Delete(ctx RequestContext, id string) (rows int64, err error)
|
Delete(ctx RequestContext, id string) (rows int64, err error)
|
||||||
|
}
|
||||||
|
|
||||||
AddPermission(ctx RequestContext, r space.Permission) (err error)
|
// PermissionStorer defines required methods for space/document permission management
|
||||||
AddPermissions(ctx RequestContext, r space.Permission, actions ...space.PermissionAction) (err error)
|
type PermissionStorer interface {
|
||||||
GetUserPermissions(ctx RequestContext, spaceID string) (r []space.Permission, err error)
|
AddPermission(ctx RequestContext, r permission.Permission) (err error)
|
||||||
GetPermissions(ctx RequestContext, spaceID string) (r []space.Permission, err error)
|
AddPermissions(ctx RequestContext, r permission.Permission, actions ...permission.Action) (err error)
|
||||||
DeletePermissions(ctx RequestContext, spaceID string) (rows int64, err error)
|
GetUserSpacePermissions(ctx RequestContext, spaceID string) (r []permission.Permission, err error)
|
||||||
DeleteUserPermissions(ctx RequestContext, spaceID, userID string) (rows int64, err error)
|
GetSpacePermissions(ctx RequestContext, spaceID string) (r []permission.Permission, err error)
|
||||||
DeleteAllUserPermissions(ctx RequestContext, userID string) (rows int64, err error)
|
DeleteSpacePermissions(ctx RequestContext, spaceID string) (rows int64, err error)
|
||||||
|
DeleteUserSpacePermissions(ctx RequestContext, spaceID, userID string) (rows int64, err error)
|
||||||
|
DeleteUserPermissions(ctx RequestContext, userID string) (rows int64, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserStorer defines required methods for user management
|
// UserStorer defines required methods for user management
|
||||||
|
|
|
@ -27,12 +27,13 @@ import (
|
||||||
"github.com/documize/community/core/stringutil"
|
"github.com/documize/community/core/stringutil"
|
||||||
"github.com/documize/community/core/uniqueid"
|
"github.com/documize/community/core/uniqueid"
|
||||||
"github.com/documize/community/domain"
|
"github.com/documize/community/domain"
|
||||||
"github.com/documize/community/domain/document"
|
"github.com/documize/community/domain/permission"
|
||||||
indexer "github.com/documize/community/domain/search"
|
indexer "github.com/documize/community/domain/search"
|
||||||
"github.com/documize/community/model/attachment"
|
"github.com/documize/community/model/attachment"
|
||||||
"github.com/documize/community/model/audit"
|
"github.com/documize/community/model/audit"
|
||||||
"github.com/documize/community/model/doc"
|
"github.com/documize/community/model/doc"
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
|
pm "github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/template"
|
"github.com/documize/community/model/template"
|
||||||
uuid "github.com/nu7hatch/gouuid"
|
uuid "github.com/nu7hatch/gouuid"
|
||||||
)
|
)
|
||||||
|
@ -112,7 +113,7 @@ func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !document.CanChangeDocument(ctx, *h.Store, model.DocumentID) {
|
if !permission.HasDocumentAction(ctx, *h.Store, model.DocumentID, pm.DocumentTemplate) {
|
||||||
response.WriteForbiddenError(w)
|
response.WriteForbiddenError(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,7 +378,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all associated roles for this user
|
// remove all associated roles for this user
|
||||||
_, err = h.Store.Space.DeleteAllUserPermissions(ctx, userID)
|
_, err = h.Store.Permission.DeleteUserPermissions(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Transaction.Rollback()
|
ctx.Transaction.Rollback()
|
||||||
response.WriteServerError(w, method, err)
|
response.WriteServerError(w, method, err)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
link "github.com/documize/community/domain/link/mysql"
|
link "github.com/documize/community/domain/link/mysql"
|
||||||
org "github.com/documize/community/domain/organization/mysql"
|
org "github.com/documize/community/domain/organization/mysql"
|
||||||
page "github.com/documize/community/domain/page/mysql"
|
page "github.com/documize/community/domain/page/mysql"
|
||||||
|
permission "github.com/documize/community/domain/permission/mysql"
|
||||||
pin "github.com/documize/community/domain/pin/mysql"
|
pin "github.com/documize/community/domain/pin/mysql"
|
||||||
search "github.com/documize/community/domain/search/mysql"
|
search "github.com/documize/community/domain/search/mysql"
|
||||||
setting "github.com/documize/community/domain/setting/mysql"
|
setting "github.com/documize/community/domain/setting/mysql"
|
||||||
|
@ -43,6 +44,7 @@ func StoreMySQL(r *env.Runtime, s *domain.Store) {
|
||||||
s.Organization = org.Scope{Runtime: r}
|
s.Organization = org.Scope{Runtime: r}
|
||||||
s.Page = page.Scope{Runtime: r}
|
s.Page = page.Scope{Runtime: r}
|
||||||
s.Pin = pin.Scope{Runtime: r}
|
s.Pin = pin.Scope{Runtime: r}
|
||||||
|
s.Permission = permission.Scope{Runtime: r}
|
||||||
s.Search = search.Scope{Runtime: r}
|
s.Search = search.Scope{Runtime: r}
|
||||||
s.Setting = setting.Scope{Runtime: r}
|
s.Setting = setting.Scope{Runtime: r}
|
||||||
s.Space = space.Scope{Runtime: r}
|
s.Space = space.Scope{Runtime: r}
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
<div class="round-button-mono" id="page-menu-{{page.id}}">
|
<div class="round-button-mono" id="page-menu-{{page.id}}">
|
||||||
<i class="material-icons color-gray">more_vert</i>
|
<i class="material-icons color-gray">more_vert</i>
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="round-button-mono" {{action 'toggleExpand'}}>
|
||||||
|
<i class="material-icons color-gray">expand_less</i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if hasMenuPermissions}}
|
||||||
{{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
|
{{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
{{#if permissions.documentCopy}}
|
{{#if permissions.documentCopy}}
|
||||||
|
@ -36,9 +42,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
{{/dropdown-menu}}
|
{{/dropdown-menu}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="round-button-mono" {{action 'toggleExpand'}}>
|
|
||||||
<i class="material-icons color-gray">expand_less</i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if menuOpen}}
|
{{#if menuOpen}}
|
||||||
{{#if permissions.documentDelete}}
|
{{#if permissions.documentDelete}}
|
||||||
|
|
|
@ -9,12 +9,52 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
package space
|
package permission
|
||||||
|
|
||||||
// PermissionRecord represents space permissions for a user on a space.
|
import "time"
|
||||||
|
|
||||||
|
// Permission represents a permission for a space and is persisted to the database.
|
||||||
|
type Permission struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
OrgID string `json:"-"`
|
||||||
|
Who string `json:"who"` // user, role
|
||||||
|
WhoID string `json:"whoId"` // either a user or role ID
|
||||||
|
Action Action `json:"action"` // view, edit, delete
|
||||||
|
Scope string `json:"scope"` // object, table
|
||||||
|
Location string `json:"location"` // table name
|
||||||
|
RefID string `json:"refId"` // id of row in table / blank when scope=table
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action details type of action
|
||||||
|
type Action string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SpaceView action means you can view a space and documents therein
|
||||||
|
SpaceView Action = "view"
|
||||||
|
// SpaceManage action means you can add, remove users, set permissions, but not delete that space
|
||||||
|
SpaceManage Action = "manage"
|
||||||
|
// SpaceOwner action means you can delete a space and do all SpaceManage functions
|
||||||
|
SpaceOwner Action = "own"
|
||||||
|
|
||||||
|
// DocumentAdd action means you can create/upload documents to a space
|
||||||
|
DocumentAdd Action = "doc-add"
|
||||||
|
// DocumentEdit action means you can edit documents in a space
|
||||||
|
DocumentEdit Action = "doc-edit"
|
||||||
|
// DocumentDelete means you can delete documents in a space
|
||||||
|
DocumentDelete Action = "doc-delete"
|
||||||
|
// DocumentMove means you can move documents between spaces
|
||||||
|
DocumentMove Action = "doc-move"
|
||||||
|
// DocumentCopy means you can copy documents within and between spaces
|
||||||
|
DocumentCopy Action = "doc-copy"
|
||||||
|
// DocumentTemplate means you can create, edit and delete document templates and content blocks
|
||||||
|
DocumentTemplate Action = "doc-template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Record represents space permissions for a user on a space.
|
||||||
// This data structure is made from database permission records for the space,
|
// This data structure is made from database permission records for the space,
|
||||||
// and it is designed to be sent to HTTP clients (web, mobile).
|
// and it is designed to be sent to HTTP clients (web, mobile).
|
||||||
type PermissionRecord struct {
|
type Record struct {
|
||||||
OrgID string `json:"orgId"`
|
OrgID string `json:"orgId"`
|
||||||
SpaceID string `json:"folderId"`
|
SpaceID string `json:"folderId"`
|
||||||
UserID string `json:"userId"`
|
UserID string `json:"userId"`
|
||||||
|
@ -31,8 +71,8 @@ type PermissionRecord struct {
|
||||||
|
|
||||||
// DecodeUserPermissions returns a flat, usable permission summary record
|
// DecodeUserPermissions returns a flat, usable permission summary record
|
||||||
// from multiple user permission records for a given space.
|
// from multiple user permission records for a given space.
|
||||||
func DecodeUserPermissions(perm []Permission) (r PermissionRecord) {
|
func DecodeUserPermissions(perm []Permission) (r Record) {
|
||||||
r = PermissionRecord{}
|
r = Record{}
|
||||||
|
|
||||||
if len(perm) > 0 {
|
if len(perm) > 0 {
|
||||||
r.OrgID = perm[0].OrgID
|
r.OrgID = perm[0].OrgID
|
||||||
|
@ -67,9 +107,26 @@ func DecodeUserPermissions(perm []Permission) (r PermissionRecord) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PermissionsModel details which users have what permissions on a given space.
|
||||||
|
type PermissionsModel struct {
|
||||||
|
Message string
|
||||||
|
Permissions []Record
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPermission checks if action matches one of the required actions?
|
||||||
|
func HasPermission(action Action, actions ...Action) bool {
|
||||||
|
for _, a := range actions {
|
||||||
|
if action == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeUserPermissions returns multiple user permission records
|
// EncodeUserPermissions returns multiple user permission records
|
||||||
// for a given space, using flat permission summary record.
|
// for a given space, using flat permission summary record.
|
||||||
func EncodeUserPermissions(r PermissionRecord) (perm []Permission) {
|
func EncodeUserPermissions(r Record) (perm []Permission) {
|
||||||
if r.SpaceView {
|
if r.SpaceView {
|
||||||
perm = append(perm, EncodeRecord(r, SpaceView))
|
perm = append(perm, EncodeRecord(r, SpaceView))
|
||||||
}
|
}
|
||||||
|
@ -103,13 +160,13 @@ func EncodeUserPermissions(r PermissionRecord) (perm []Permission) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAnyPermission returns true if user has at least one permission.
|
// HasAnyPermission returns true if user has at least one permission.
|
||||||
func HasAnyPermission(p PermissionRecord) bool {
|
func HasAnyPermission(p Record) bool {
|
||||||
return p.SpaceView || p.SpaceManage || p.SpaceOwner || p.DocumentAdd || p.DocumentEdit ||
|
return p.SpaceView || p.SpaceManage || p.SpaceOwner || p.DocumentAdd || p.DocumentEdit ||
|
||||||
p.DocumentDelete || p.DocumentMove || p.DocumentCopy || p.DocumentTemplate
|
p.DocumentDelete || p.DocumentMove || p.DocumentCopy || p.DocumentTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRecord creates standard permission record representing user permissions for a space.
|
// EncodeRecord creates standard permission record representing user permissions for a space.
|
||||||
func EncodeRecord(r PermissionRecord, a PermissionAction) (p Permission) {
|
func EncodeRecord(r Record, a Action) (p Permission) {
|
||||||
p = Permission{}
|
p = Permission{}
|
||||||
p.OrgID = r.OrgID
|
p.OrgID = r.OrgID
|
||||||
p.Who = "user"
|
p.Who = "user"
|
|
@ -12,8 +12,6 @@
|
||||||
package space
|
package space
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/documize/community/model"
|
"github.com/documize/community/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,44 +53,6 @@ func (l *Space) IsRestricted() bool {
|
||||||
return l.Type == ScopeRestricted
|
return l.Type == ScopeRestricted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permission represents a permission for a space and is persisted to the database.
|
|
||||||
type Permission struct {
|
|
||||||
ID uint64 `json:"id"`
|
|
||||||
OrgID string `json:"-"`
|
|
||||||
Who string `json:"who"` // user, role
|
|
||||||
WhoID string `json:"whoId"` // either a user or role ID
|
|
||||||
Action PermissionAction `json:"action"` // view, edit, delete
|
|
||||||
Scope string `json:"scope"` // object, table
|
|
||||||
Location string `json:"location"` // table name
|
|
||||||
RefID string `json:"refId"` // id of row in table / blank when scope=table
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PermissionAction details type of action
|
|
||||||
type PermissionAction string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SpaceView action means you can view a space and documents therein
|
|
||||||
SpaceView PermissionAction = "view"
|
|
||||||
// SpaceManage action means you can add, remove users, set permissions, but not delete that space
|
|
||||||
SpaceManage PermissionAction = "manage"
|
|
||||||
// SpaceOwner action means you can delete a space and do all SpaceManage functions
|
|
||||||
SpaceOwner PermissionAction = "own"
|
|
||||||
|
|
||||||
// DocumentAdd action means you can create/upload documents to a space
|
|
||||||
DocumentAdd PermissionAction = "doc-add"
|
|
||||||
// DocumentEdit action means you can edit documents in a space
|
|
||||||
DocumentEdit PermissionAction = "doc-edit"
|
|
||||||
// DocumentDelete means you can delete documents in a space
|
|
||||||
DocumentDelete PermissionAction = "doc-delete"
|
|
||||||
// DocumentMove means you can move documents between spaces
|
|
||||||
DocumentMove PermissionAction = "doc-move"
|
|
||||||
// DocumentCopy means you can copy documents within and between spaces
|
|
||||||
DocumentCopy PermissionAction = "doc-copy"
|
|
||||||
// DocumentTemplate means you can create, edit and delete document templates and content blocks
|
|
||||||
DocumentTemplate PermissionAction = "doc-template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Viewer details who can see a particular space
|
// Viewer details who can see a particular space
|
||||||
type Viewer struct {
|
type Viewer struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -104,12 +64,6 @@ type Viewer struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PermissionsModel details which users have what permissions on a given space.
|
|
||||||
type PermissionsModel struct {
|
|
||||||
Message string
|
|
||||||
Permissions []PermissionRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcceptShareModel is used to setup a user who has accepted a shared space.
|
// AcceptShareModel is used to setup a user who has accepted a shared space.
|
||||||
type AcceptShareModel struct {
|
type AcceptShareModel struct {
|
||||||
Serial string `json:"serial"`
|
Serial string `json:"serial"`
|
||||||
|
@ -132,14 +86,3 @@ type NewSpaceRequest struct {
|
||||||
CopyPermission bool `json:"copyPermission"` // copy uer permissions
|
CopyPermission bool `json:"copyPermission"` // copy uer permissions
|
||||||
CopyDocument bool `json:"copyDocument"` // copy all documents!
|
CopyDocument bool `json:"copyDocument"` // copy all documents!
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPermission checks if action matches one of the required actions?
|
|
||||||
func HasPermission(action PermissionAction, actions ...PermissionAction) bool {
|
|
||||||
for _, a := range actions {
|
|
||||||
if action == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/documize/community/domain/meta"
|
"github.com/documize/community/domain/meta"
|
||||||
"github.com/documize/community/domain/organization"
|
"github.com/documize/community/domain/organization"
|
||||||
"github.com/documize/community/domain/page"
|
"github.com/documize/community/domain/page"
|
||||||
|
"github.com/documize/community/domain/permission"
|
||||||
"github.com/documize/community/domain/pin"
|
"github.com/documize/community/domain/pin"
|
||||||
"github.com/documize/community/domain/search"
|
"github.com/documize/community/domain/search"
|
||||||
"github.com/documize/community/domain/section"
|
"github.com/documize/community/domain/section"
|
||||||
|
@ -58,6 +59,7 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
|
||||||
document := document.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
document := document.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
||||||
attachment := attachment.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
attachment := attachment.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
||||||
conversion := conversion.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
conversion := conversion.Handler{Runtime: rt, Store: s, Indexer: indexer}
|
||||||
|
permission := permission.Handler{Runtime: rt, Store: s}
|
||||||
organization := organization.Handler{Runtime: rt, Store: s}
|
organization := organization.Handler{Runtime: rt, Store: s}
|
||||||
|
|
||||||
//**************************************************
|
//**************************************************
|
||||||
|
@ -113,9 +115,9 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
|
||||||
|
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"PUT", "OPTIONS"}, nil, space.SetPermissions)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetSpacePermissions)
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, space.GetUserPermissions)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserSpacePermissions)
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"GET", "OPTIONS"}, nil, space.GetPermissions)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions", []string{"GET", "OPTIONS"}, nil, permission.GetSpacePermissions)
|
||||||
Add(rt, RoutePrefixPrivate, "space/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
|
Add(rt, RoutePrefixPrivate, "space/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
|
||||||
Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, space.GetSpaceViewers)
|
Add(rt, RoutePrefixPrivate, "space", []string{"GET", "OPTIONS"}, []string{"filter", "viewers"}, space.GetSpaceViewers)
|
||||||
Add(rt, RoutePrefixPrivate, "space", []string{"POST", "OPTIONS"}, nil, space.Add)
|
Add(rt, RoutePrefixPrivate, "space", []string{"POST", "OPTIONS"}, nil, space.Add)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue