diff --git a/core/response/write.go b/core/response/write.go index 41a06f89..82c8205c 100644 --- a/core/response/write.go +++ b/core/response/write.go @@ -35,6 +35,11 @@ func WriteNotFoundError(w http.ResponseWriter, method string, id string) { w.Write([]byte("{Error: 'Not found'}")) } +// WriteNotFound notifies HTTP client of 'record not found' error. +func WriteNotFound(w http.ResponseWriter) { + w.WriteHeader(http.StatusNotFound) +} + // WriteServerError notifies HTTP client of general application error. func WriteServerError(w http.ResponseWriter, method string, err error) { writeStatus(w, http.StatusBadRequest) diff --git a/domain/category/endpoint.go b/domain/category/endpoint.go index e7001305..74b9b187 100644 --- a/domain/category/endpoint.go +++ b/domain/category/endpoint.go @@ -155,15 +155,11 @@ func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) { } cat, err := h.Store.Category.GetAllBySpace(ctx, spaceID) - if err != nil && err != sql.ErrNoRows { + if err != nil { response.WriteServerError(w, method, err) return } - if len(cat) == 0 { - cat = []category.Category{} - } - response.WriteJSON(w, cat) } @@ -320,10 +316,6 @@ func (h *Handler) GetSummary(w http.ResponseWriter, r *http.Request) { return } - if len(s) == 0 { - s = []category.SummaryModel{} - } - response.WriteJSON(w, s) } diff --git a/domain/category/mysql/store.go b/domain/category/mysql/store.go index 9d1584ad..39dafe0c 100644 --- a/domain/category/mysql/store.go +++ b/domain/category/mysql/store.go @@ -80,8 +80,9 @@ func (s Scope) GetAllBySpace(ctx domain.RequestContext, spaceID string) (c []cat )) ORDER BY category`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID) - if err == sql.ErrNoRows { + if err == sql.ErrNoRows || len(c) == 0 { err = nil + c = []category.Category{} } if err != nil { err = errors.Wrap(err, fmt.Sprintf("unable to execute select all categories for space %s", spaceID)) @@ -190,20 +191,20 @@ func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows in // GetSpaceCategorySummary returns number of documents and users for space categories. func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string) (c []category.SummaryModel, err error) { err = s.Runtime.Db.Select(&c, ` - SELECT 'documents' as type, categoryid, COUNT(*) as count FROM categorymember WHERE orgid=? AND labelid=? GROUP BY categoryid, type + SELECT 'documents' as type, categoryid, COUNT(*) as count + FROM categorymember + WHERE orgid=? AND labelid=? GROUP BY categoryid, type UNION ALL - SELECT 'users' as type, refid AS categoryid, count(*) AS count FROM permission WHERE orgid=? AND who='user' AND location='category' - AND refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?) - GROUP BY refid, type - UNION ALL - SELECT 'users' as type, p.refid AS categoryid, count(*) AS count FROM rolemember r LEFT JOIN permission p ON p.whoid=r.roleid - WHERE p.orgid=? AND p.who='role' AND p.location='category' - AND p.refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?) - GROUP BY p.refid, type`, - ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID) + SELECT 'users' as type, refid AS categoryid, count(*) AS count + FROM permission + WHERE orgid=? AND location='category' + AND refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?) + GROUP BY refid, type`, + ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID /*, ctx.OrgID, ctx.OrgID, spaceID*/) - if err == sql.ErrNoRows { + if err == sql.ErrNoRows || len(c) == 0 { err = nil + c = []category.SummaryModel{} } if err != nil { err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID)) diff --git a/domain/meta/endpoint.go b/domain/meta/endpoint.go index 80d6818b..a0effd69 100644 --- a/domain/meta/endpoint.go +++ b/domain/meta/endpoint.go @@ -41,8 +41,8 @@ func (h *Handler) Meta(w http.ResponseWriter, r *http.Request) { org, err := h.Store.Organization.GetOrganizationByDomain(data.URL) if err != nil { - h.Runtime.Log.Error("unable to fetch request meta for "+data.URL, err) - response.WriteForbiddenError(w) + h.Runtime.Log.Info("unable to fetch request meta for " + data.URL) + response.WriteNotFound(w) return } diff --git a/domain/organization/mysql/store.go b/domain/organization/mysql/store.go index 4b56d569..0d0b0702 100644 --- a/domain/organization/mysql/store.go +++ b/domain/organization/mysql/store.go @@ -79,16 +79,15 @@ func (s Scope) GetOrganizationByDomain(subdomain string) (o org.Organization, er return } - err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE domain=? AND active=1", - subdomain) - + // match on given domain name + err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE domain=? AND active=1", subdomain) if err == nil { return } + err = nil - // we try to match on empty domain as last resort + // match on empty domain as last resort err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE domain='' AND active=1") - if err != nil && err != sql.ErrNoRows { err = errors.Wrap(err, "unable to execute select for empty subdomain") } diff --git a/domain/permission/endpoint.go b/domain/permission/endpoint.go index 015f2186..e6697a4c 100644 --- a/domain/permission/endpoint.go +++ b/domain/permission/endpoint.go @@ -266,7 +266,7 @@ func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) { } perms, err := h.Store.Permission.GetSpacePermissions(ctx, spaceID) - if err != nil && err != sql.ErrNoRows { + if err != nil { response.WriteServerError(w, method, err) h.Runtime.Log.Error(method, err) return @@ -302,7 +302,7 @@ func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) { if records[i].Who == permission.UserPermission { if records[i].WhoID == user.EveryoneUserID { - records[i].Name = "Everyone" + records[i].Name = user.EveryoneUserName } else { u, err := h.Store.User.Get(ctx, records[i].WhoID) if err != nil { @@ -371,13 +371,58 @@ func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request) return } - u, err := h.Store.Permission.GetCategoryPermissions(ctx, categoryID) - if err != nil && err != sql.ErrNoRows { + perms, err := h.Store.Permission.GetCategoryPermissions(ctx, categoryID) + if err != nil { response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) return } - response.WriteJSON(w, u) + userPerms := make(map[string][]permission.Permission) + for _, p := range perms { + userPerms[p.WhoID] = append(userPerms[p.WhoID], p) + } + + records := []permission.CategoryRecord{} + for _, up := range userPerms { + records = append(records, permission.DecodeUserCategoryPermissions(up)) + } + + // populate user/group name for thing that has permission record + groups, err := h.Store.Group.GetAll(ctx) + if err != nil && err != sql.ErrNoRows { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + for i := range records { + if records[i].Who == permission.GroupPermission { + for j := range groups { + if records[i].WhoID == groups[j].RefID { + records[i].Name = groups[j].Name + break + } + } + } + + if records[i].Who == permission.UserPermission { + if records[i].WhoID == user.EveryoneUserID { + records[i].Name = user.EveryoneUserName + } else { + u, err := h.Store.User.Get(ctx, records[i].WhoID) + if err != nil { + h.Runtime.Log.Info(fmt.Sprintf("user not found %s", records[i].WhoID)) + h.Runtime.Log.Error(method, err) + continue + } + + records[i].Name = u.Fullname() + } + } + } + + response.WriteJSON(w, records) } // SetCategoryPermissions persists specified category permissions @@ -405,7 +450,7 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request) return } - var model = []permission.CategoryViewRequestModel{} + var model = []permission.CategoryRecord{} err = json.Unmarshal(body, &model) if err != nil { response.WriteServerError(w, method, err) @@ -415,6 +460,7 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request) if !HasPermission(ctx, *h.Store, spaceID, permission.SpaceManage, permission.SpaceOwner) { response.WriteForbiddenError(w) + h.Runtime.Log.Info("no permission to set category permissions") return } @@ -437,8 +483,8 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request) for _, m := range model { perm := permission.Permission{} perm.OrgID = ctx.OrgID - perm.Who = permission.UserPermission - perm.WhoID = m.UserID + perm.Who = m.Who + perm.WhoID = m.WhoID perm.Scope = permission.ScopeRow perm.Location = permission.LocationCategory perm.RefID = m.CategoryID diff --git a/domain/permission/mysql/store.go b/domain/permission/mysql/store.go index 2c2a3554..90658517 100644 --- a/domain/permission/mysql/store.go +++ b/domain/permission/mysql/store.go @@ -112,15 +112,16 @@ func (s Scope) GetSpacePermissions(ctx domain.RequestContext, spaceID string) (r func (s Scope) GetCategoryPermissions(ctx domain.RequestContext, catID 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='category' AND refid=? AND who='user' + FROM permission + WHERE orgid=? AND location='category' AND who='user' AND (refid=? OR refid='0') 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'`, + WHERE p.orgid=? AND p.location='category' AND p.who='role' AND (p.refid=? OR p.refid='0')`, ctx.OrgID, catID, ctx.OrgID, catID) - if err == sql.ErrNoRows { + if err == sql.ErrNoRows || len(r) == 0 { err = nil r = []permission.Permission{} } @@ -137,7 +138,8 @@ func (s Scope) GetCategoryUsers(ctx domain.RequestContext, catID string) (u []us SELECT u.id, IFNULL(u.refid, '') AS refid, IFNULL(u.firstname, '') AS firstname, IFNULL(u.lastname, '') as lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised FROM user u LEFT JOIN account a ON u.refid = a.userid WHERE a.orgid=? AND a.active=1 AND u.refid IN ( - SELECT whoid from permission WHERE orgid=? AND who='user' AND location='category' AND refid=? + SELECT whoid from permission + WHERE orgid=? AND who='user' AND location='category' AND refid=? UNION ALL SELECT r.userid from rolemember r LEFT JOIN permission p ON p.whoid=r.roleid @@ -147,7 +149,7 @@ func (s Scope) GetCategoryUsers(ctx domain.RequestContext, catID string) (u []us ORDER BY firstname, lastname`, ctx.OrgID, ctx.OrgID, catID, ctx.OrgID, catID) - if err == sql.ErrNoRows { + if err == sql.ErrNoRows || len(u) == 0 { err = nil u = []user.User{} } @@ -170,7 +172,7 @@ func (s Scope) GetUserCategoryPermissions(ctx domain.RequestContext, userID stri WHERE p.orgid=? AND p.location='category' AND p.who='role' AND (r.userid=? OR r.userid='0')`, ctx.OrgID, userID, ctx.OrgID, userID) - if err == sql.ErrNoRows { + if err == sql.ErrNoRows || len(r) == 0 { err = nil r = []permission.Permission{} } diff --git a/domain/permission/permission.go b/domain/permission/permission.go index a23e9118..17cc86f1 100644 --- a/domain/permission/permission.go +++ b/domain/permission/permission.go @@ -30,7 +30,7 @@ func CanViewSpaceDocument(ctx domain.RequestContext, s domain.Store, labelID str } for _, role := range roles { - if role.RefID == labelID && role.Location == "space" && role.Scope == "object" && + if role.RefID == labelID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow && pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) { return true } @@ -58,7 +58,7 @@ func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID strin } for _, role := range roles { - if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" && + if role.RefID == document.LabelID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow && pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) { return true } @@ -88,7 +88,7 @@ func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID str } for _, role := range roles { - if role.RefID == document.LabelID && role.Location == "space" && role.Scope == "object" && role.Action == pm.DocumentEdit { + if role.RefID == document.LabelID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow && role.Action == pm.DocumentEdit { return true } } @@ -136,7 +136,7 @@ func CanUploadDocument(ctx domain.RequestContext, s domain.Store, spaceID string } for _, role := range roles { - if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" && + if role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow && pm.ContainsPermission(role.Action, pm.DocumentAdd) { return true } @@ -155,7 +155,7 @@ func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) boo return false } for _, role := range roles { - if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" && + if role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow && pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) { return true } @@ -176,7 +176,7 @@ func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, ac } for _, role := range roles { - if role.RefID == spaceID && role.Location == "space" && role.Scope == "object" { + if role.RefID == spaceID && role.Location == pm.LocationSpace && role.Scope == pm.ScopeRow { for _, a := range actions { if role.Action == a { return true diff --git a/gui/app/components/folder/category-admin.js b/gui/app/components/folder/category-admin.js index c7028338..e89f037c 100644 --- a/gui/app/components/folder/category-admin.js +++ b/gui/app/components/folder/category-admin.js @@ -10,14 +10,17 @@ // https://documize.com import $ from 'jquery'; -import Component from '@ember/component'; +import { A } from "@ember/array" import { inject as service } from '@ember/service'; import TooltipMixin from '../../mixins/tooltip'; import ModalMixin from '../../mixins/modal'; +import Component from '@ember/component'; export default Component.extend(ModalMixin, TooltipMixin, { - userService: service('user'), - categoryService: service('category'), + // userService: service('user'), + spaceSvc: service('folder'), + groupSvc: service('group'), + categorySvc: service('category'), appMeta: service(), store: service(), newCategory: '', @@ -42,43 +45,48 @@ export default Component.extend(ModalMixin, TooltipMixin, { load() { // get categories - this.get('categoryService').getAll(this.get('folder.id')).then((c) => { + this.get('categorySvc').getAll(this.get('folder.id')).then((c) => { this.set('category', c); // get summary of documents and users for each category in space - this.get('categoryService').getSummary(this.get('folder.id')).then((s) => { + this.get('categorySvc').getSummary(this.get('folder.id')).then((s) => { c.forEach((cat) => { - let docs = _.findWhere(s, {categoryId: cat.get('id'), type: 'documents'}); - let docCount = is.not.undefined(docs) ? docs.count : 0; + // let docs = _.findWhere(s, {categoryId: cat.get('id'), type: 'documents'}); + // let docCount = is.not.undefined(docs) ? docs.count : 0; - let users = _.findWhere(s, {categoryId: cat.get('id'), type: 'users'}); - let userCount = is.not.undefined(users) ? users.count : 0; + // let users = _.findWhere(s, {categoryId: cat.get('id'), type: 'users'}); + // let userCount = is.not.undefined(users) ? users.count : 0; + let docs = _.where(s, {categoryId: cat.get('id'), type: 'documents'}); + let docCount = 0; + docs.forEach((d) => { docCount = docCount + d.count }); + + let users = _.where(s, {categoryId: cat.get('id'), type: 'users'}); + let userCount = 0; + users.forEach((u) => { userCount = userCount + u.count }); + cat.set('documents', docCount); cat.set('users', userCount); }); }); - - // get users that this space admin user can see - this.get('userService').getSpaceUsers(this.get('folder.id')).then((users) => { - // set up Everyone user - let u = { - orgId: this.get('folder.orgId'), - folderId: this.get('folder.id'), - userId: '', - firstname: 'Everyone', - lastname: '', - }; - - let data = this.get('store').normalize('user', u) - users.pushObject(this.get('store').push(data)); - - users = users.sortBy('firstname', 'lastname'); - this.set('users', users); - }); }); }, + permissionRecord(who, whoId, name) { + let raw = { + id: whoId, + orgId: this.get('folder.orgId'), + categoryId: this.get('currentCategory.id'), + whoId: whoId, + who: who, + name: name, + categoryView: false, + }; + + let rec = this.get('store').normalize('category-permission', raw); + return this.get('store').push(rec); + }, + setEdit(id, val) { let cats = this.get('category'); let cat = cats.findBy('id', id); @@ -109,7 +117,7 @@ export default Component.extend(ModalMixin, TooltipMixin, { folderId: this.get('folder.id') }; - this.get('categoryService').add(c).then(() => { + this.get('categorySvc').add(c).then(() => { this.load(); }); }, @@ -124,7 +132,7 @@ export default Component.extend(ModalMixin, TooltipMixin, { onDelete() { this.modalClose('#category-delete-modal'); - this.get('categoryService').delete(this.get('deleteId')).then(() => { + this.get('categorySvc').delete(this.get('deleteId')).then(() => { this.load(); }); }, @@ -150,7 +158,7 @@ export default Component.extend(ModalMixin, TooltipMixin, { cat = this.setEdit(id, false); $('#edit-category-' + cat.get('id')).removeClass('is-invalid'); - this.get('categoryService').save(cat).then(() => { + this.get('categorySvc').save(cat).then(() => { this.load(); }); @@ -160,45 +168,47 @@ export default Component.extend(ModalMixin, TooltipMixin, { onShowAccessPicker(catId) { this.set('showCategoryAccess', true); - let users = this.get('users'); + let categoryPermissions = A([]); let category = this.get('category').findBy('id', catId); - this.get('categoryService').getPermissions(category.get('id')).then((viewers) => { - // mark those users as selected that have already been given permission - // to see the current category; - users.forEach((user) => { - let userId = user.get('id'); - let selected = viewers.isAny('whoId', userId); - user.set('selected', selected); + this.set('currentCategory', category); + this.set('categoryPermissions', categoryPermissions); + + // get space permissions + this.get('spaceSvc').getPermissions(this.get('folder.id')).then((spacePermissions) => { + spacePermissions.forEach((sp) => { + let cp = this.permissionRecord(sp.get('who'), sp.get('whoId'), sp.get('name')); + cp.set('selected', false); + categoryPermissions.pushObject(cp); }); - this.set('categoryUsers', users); - this.set('currentCategory', category); + this.get('categorySvc').getPermissions(category.get('id')).then((perms) => { + // mark those users as selected that have permission to see the current category + perms.forEach((perm) => { + let c = categoryPermissions.findBy('whoId', perm.get('whoId')); + if (is.not.undefined(c)) { + c.set('selected', true); + } + console.log(perm.get('whoId')); + }); + + this.set('categoryPermissions', categoryPermissions.sortBy('who', 'name')); + }); }); }, + onToggle(item) { + item.set('selected', !item.get('selected')); + }, + onGrantAccess() { this.set('showCategoryAccess', false); let folder = this.get('folder'); let category = this.get('currentCategory'); - let users = this.get('categoryUsers').filterBy('selected', true); - let viewers = []; + let perms = this.get('categoryPermissions').filterBy('selected', true); - users.forEach((user) => { - let userId = user.get('id'); - - let v = { - orgId: this.get('folder.orgId'), - folderId: this.get('folder.id'), - categoryId: category.get('id'), - userId: userId - }; - - viewers.push(v); - }); - - this.get('categoryService').setViewers(folder.get('id'), category.get('id'), viewers).then(() => { + this.get('categorySvc').setViewers(folder.get('id'), category.get('id'), perms).then(() => { this.load(); }); } diff --git a/gui/app/models/category-permission.js b/gui/app/models/category-permission.js new file mode 100644 index 00000000..bc984548 --- /dev/null +++ b/gui/app/models/category-permission.js @@ -0,0 +1,23 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; + +export default Model.extend({ + orgId: attr('string'), + categoryId: attr('string'), + whoId: attr('string'), + who: attr('string'), + categoryView: attr('boolean'), + name: attr('string'), // read-only + selected: attr('boolean') // client-side only +}); diff --git a/gui/app/serializers/category-permission.js b/gui/app/serializers/category-permission.js new file mode 100644 index 00000000..c6542cf8 --- /dev/null +++ b/gui/app/serializers/category-permission.js @@ -0,0 +1,13 @@ +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + normalize(modelClass, resourceHash) { + return { + data: { + id: resourceHash.whoId ? resourceHash.whoId : 0, + type: modelClass.modelName, + attributes: resourceHash + } + }; + } +}); diff --git a/gui/app/services/category.js b/gui/app/services/category.js index 75aae84e..386f5656 100644 --- a/gui/app/services/category.js +++ b/gui/app/services/category.js @@ -87,7 +87,15 @@ export default BaseService.extend({ return this.get('ajax').request(`category/${categoryId}/permission`, { method: 'GET' }).then((response) => { - return response; + // return response; + let data = []; + + data = response.map((obj) => { + let data = this.get('store').normalize('category-permission', obj); + return this.get('store').push(data); + }); + + return data; }); }, diff --git a/gui/app/templates/components/folder/category-admin.hbs b/gui/app/templates/components/folder/category-admin.hbs index 9c1d1680..a055dd67 100644 --- a/gui/app/templates/components/folder/category-admin.hbs +++ b/gui/app/templates/components/folder/category-admin.hbs @@ -6,7 +6,7 @@

Sub-divide spaces into categories which can contain documents with restricted access.

- {{input id="new-category-name" type='text' class="form-control mousetrap" placeholder="Category name" value=newCategory}} + {{focus-input id="new-category-name" type='text' class="form-control mousetrap" placeholder="Category name" value=newCategory}}
@@ -27,7 +27,7 @@ {{else}}
{{cat.category}}
-
{{cat.documents}} {{if (eq cat.documents 1) 'document' 'documents' }}, {{cat.users}} {{if (eq cat.users 1) 'person' 'people' }}
+
{{cat.documents}} {{if (eq cat.documents 1) 'document' 'documents' }} · {{cat.users}} users/groups
{{/if}}
@@ -72,8 +72,40 @@
- {{#ui/ui-dialog title="Set Cateogory Access" confirmCaption="Save" buttonType="btn-success" show=showCategoryAccess onAction=(action 'onGrantAccess')}} + {{#ui/ui-dialog title="Set Category Access" confirmCaption="Save" buttonType="btn-success" show=showCategoryAccess onAction=(action 'onGrantAccess')}}

Select who can view documents within category

- {{ui/ui-list-picker items=categoryUsers nameField='fullname' singleSelect=false}} + +
+
    + {{#each categoryPermissions as |permission|}} +
  • +
    + {{#if (eq permission.who "role")}} + + people + + {{else}} + {{#if (eq permission.whoId constants.EveryoneUserId)}} + + language + + {{else}} + + person + + {{/if}} + {{/if}} +  {{permission.name}} + {{#if (eq permission.whoId session.user.id)}} + (you) + {{/if}} +
    + {{#if permission.selected}} + check + {{/if}} +
  • + {{/each}} +
+
{{/ui/ui-dialog}} diff --git a/model/permission/category.go b/model/permission/category.go new file mode 100644 index 00000000..a257bbcf --- /dev/null +++ b/model/permission/category.go @@ -0,0 +1,75 @@ +// Copyright 2016 Documize Inc. . 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 . +// +// https://documize.com + +package permission + +// CategoryRecord represents space permissions for a user on a category. +// This data structure is made from database permission records for the category, +// and it is designed to be sent to HTTP clients (web, mobile). +type CategoryRecord struct { + OrgID string `json:"orgId"` + CategoryID string `json:"categoryId"` + WhoID string `json:"whoId"` + Who WhoType `json:"who"` + CategoryView bool `json:"categoryView"` + Name string `json:"name"` // read-only, user or group name +} + +// DecodeUserCategoryPermissions returns a flat, usable permission summary record +// from multiple user permission records for a given category. +func DecodeUserCategoryPermissions(perm []Permission) (r CategoryRecord) { + r = CategoryRecord{} + + if len(perm) > 0 { + r.OrgID = perm[0].OrgID + r.WhoID = perm[0].WhoID + r.Who = perm[0].Who + r.CategoryID = perm[0].RefID + } + + for _, p := range perm { + switch p.Action { + case CategoryView: + r.CategoryView = true + } + } + + return +} + +// EncodeUserCategoryPermissions returns multiple user permission records +// for a given document, using flat permission summary record. +func EncodeUserCategoryPermissions(r CategoryRecord) (perm []Permission) { + if r.CategoryView { + perm = append(perm, EncodeCategoryRecord(r, CategoryView)) + } + + return +} + +// HasAnyCategoryPermission returns true if user has at least one permission. +func HasAnyCategoryPermission(p CategoryRecord) bool { + return p.CategoryView +} + +// EncodeCategoryRecord creates standard permission record representing user permissions for a category. +func EncodeCategoryRecord(r CategoryRecord, a Action) (p Permission) { + p = Permission{} + p.OrgID = r.OrgID + p.WhoID = r.WhoID + p.Who = r.Who + p.Location = LocationDocument + p.RefID = r.CategoryID + p.Action = a + p.Scope = ScopeRow + + return +} diff --git a/model/permission/space.go b/model/permission/space.go index 51ee931a..144e4989 100644 --- a/model/permission/space.go +++ b/model/permission/space.go @@ -133,10 +133,12 @@ func EncodeRecord(r Record, a Action) (p Permission) { // CategoryViewRequestModel represents who should be allowed to see a category. type CategoryViewRequestModel struct { - OrgID string `json:"orgId"` - SpaceID string `json:"folderId"` - CategoryID string `json:"categoryID"` - UserID string `json:"userId"` + OrgID string `json:"orgId"` + SpaceID string `json:"folderId"` + CategoryID string `json:"categoryID"` + WhoID string `json:"whoId"` + Who WhoType `json:"who"` + // UserID string `json:"userId"` } // SpaceRequestModel details which users have what permissions on a given space. diff --git a/model/user/user.go b/model/user/user.go index b33f612d..15406cf5 100644 --- a/model/user/user.go +++ b/model/user/user.go @@ -75,4 +75,7 @@ func Exists(users []User, userID string) bool { const ( // EveryoneUserID provides a shortcut to state "all authenticated users". EveryoneUserID string = "0" + + // EveryoneUserName provides the descriptor for this type of user/group. + EveryoneUserName string = "Everyone" ) diff --git a/server/routing/routes.go b/server/routing/routes.go index 009a66fb..be93dbe4 100644 --- a/server/routing/routes.go +++ b/server/routing/routes.go @@ -67,9 +67,18 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { organization := organization.Handler{Runtime: rt, Store: s} //************************************************** - // Non-secure routes + // Non-secure public info routes //************************************************** + Add(rt, RoutePrefixPublic, "meta", []string{"GET", "OPTIONS"}, nil, meta.Meta) + Add(rt, RoutePrefixPublic, "version", []string{"GET", "OPTIONS"}, nil, func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(rt.Product.Version)) + }) + + //************************************************** + // Non-secure public service routes + //************************************************** + Add(rt, RoutePrefixPublic, "authenticate/keycloak", []string{"POST", "OPTIONS"}, nil, keycloak.Authenticate) Add(rt, RoutePrefixPublic, "authenticate", []string{"POST", "OPTIONS"}, nil, auth.Login) Add(rt, RoutePrefixPublic, "validate", []string{"GET", "OPTIONS"}, nil, auth.ValidateToken) @@ -77,12 +86,9 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPublic, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword) Add(rt, RoutePrefixPublic, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation) Add(rt, RoutePrefixPublic, "attachments/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, attachment.Download) - Add(rt, RoutePrefixPublic, "version", []string{"GET", "OPTIONS"}, nil, func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(rt.Product.Version)) - }) //************************************************** - // Secure routes + // Secured private routes (require authentication) //************************************************** Add(rt, RoutePrefixPrivate, "import/folder/{folderID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert) @@ -119,9 +125,6 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { 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}/permissions", []string{"PUT", "OPTIONS"}, nil, permission.SetSpacePermissions) - // Add(rt, RoutePrefixPrivate, "space/{spaceID}/permissions/user", []string{"GET", "OPTIONS"}, nil, permission.GetUserSpacePermissions) - // 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/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll) Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"GET", "OPTIONS"}, nil, space.Get) @@ -131,11 +134,8 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "category/space/{spaceID}/summary", []string{"GET", "OPTIONS"}, nil, category.GetSummary) Add(rt, RoutePrefixPrivate, "category/document/{documentID}", []string{"GET", "OPTIONS"}, nil, category.GetDocumentCategoryMembership) - // Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"PUT", "OPTIONS"}, nil, permission.SetCategoryPermissions) - // Add(rt, RoutePrefixPrivate, "category/{categoryID}/permission", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryPermissions) Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, []string{"filter", "all"}, category.GetAll) Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.Get) - // Add(rt, RoutePrefixPrivate, "category/{categoryID}/user", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryViewers) Add(rt, RoutePrefixPrivate, "category/member/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.GetSpaceCategoryMembers) Add(rt, RoutePrefixPrivate, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership) Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update)