From 3a9675eb1419fb151b1df525137c8e744e81d7ab Mon Sep 17 00:00:00 2001 From: Harvey Kandola Date: Thu, 21 Sep 2017 18:59:43 +0100 Subject: [PATCH] refined category permission checks --- domain/category/endpoint.go | 31 +++++++++++++++++++ domain/category/mysql/store.go | 26 ++++++++++++++++ domain/permission/endpoint.go | 29 +++++++++++++++-- domain/permission/mysql/store.go | 8 ++--- domain/storer.go | 1 + gui/app/components/folder/category-admin.js | 29 ++++++++++++++--- gui/app/models/category.js | 6 +++- gui/app/services/category.js | 22 +++++++++++-- .../components/folder/category-admin.hbs | 10 ++---- model/category/category.go | 7 +++++ server/routing/routes.go | 2 ++ 11 files changed, 149 insertions(+), 22 deletions(-) diff --git a/domain/category/endpoint.go b/domain/category/endpoint.go index 3da7fbc4..2cb4ea47 100644 --- a/domain/category/endpoint.go +++ b/domain/category/endpoint.go @@ -278,6 +278,37 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { response.WriteEmpty(w) } +// GetSummary returns number of documents and users for space categories. +func (h *Handler) GetSummary(w http.ResponseWriter, r *http.Request) { + method := "category.GetSummary" + ctx := domain.GetRequestContext(r) + + spaceID := request.Param(r, "spaceID") + if len(spaceID) == 0 { + response.WriteMissingDataError(w, method, "spaceID") + return + } + + ok := permission.HasPermission(ctx, *h.Store, spaceID, pm.SpaceManage, pm.SpaceOwner) + if !ok || !ctx.Authenticated { + response.WriteForbiddenError(w) + return + } + + s, err := h.Store.Category.GetSpaceCategorySummary(ctx, spaceID) + if err != nil { + h.Runtime.Log.Error("get space category summary failed", err) + response.WriteServerError(w, method, err) + return + } + + if len(s) == 0 { + s = []category.SummaryModel{} + } + + response.WriteJSON(w, s) +} + /* - category view permission handling - filter users using new permission diff --git a/domain/category/mysql/store.go b/domain/category/mysql/store.go index 5c8934d5..f86a1127 100644 --- a/domain/category/mysql/store.go +++ b/domain/category/mysql/store.go @@ -199,3 +199,29 @@ func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows in s2 := fmt.Sprintf("DELETE FROM category WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID) return b.DeleteWhere(ctx.Transaction, s2) } + +// 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 + 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) + + if err == sql.ErrNoRows { + err = nil + } + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("unable to execute select category summary for space %s", spaceID)) + return + } + + return +} diff --git a/domain/permission/endpoint.go b/domain/permission/endpoint.go index 154c7f10..21e1e960 100644 --- a/domain/permission/endpoint.go +++ b/domain/permission/endpoint.go @@ -273,9 +273,9 @@ func (h *Handler) GetUserSpacePermissions(w http.ResponseWriter, r *http.Request response.WriteJSON(w, record) } -// GetCategoryPermissions returns user permissions for given category. -func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request) { - method := "space.GetCategoryPermissions" +// GetCategoryViewers returns user permissions for given category. +func (h *Handler) GetCategoryViewers(w http.ResponseWriter, r *http.Request) { + method := "space.GetCategoryViewers" ctx := domain.GetRequestContext(r) categoryID := request.Param(r, "categoryID") @@ -296,6 +296,29 @@ func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request) response.WriteJSON(w, u) } +// GetCategoryPermissions returns user permissions for given category. +func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request) { + method := "space.GetCategoryPermissions" + ctx := domain.GetRequestContext(r) + + categoryID := request.Param(r, "categoryID") + if len(categoryID) == 0 { + response.WriteMissingDataError(w, method, "categoryID") + return + } + + u, err := h.Store.Permission.GetCategoryPermissions(ctx, categoryID) + if err != nil && err != sql.ErrNoRows { + response.WriteServerError(w, method, err) + return + } + if len(u) == 0 { + u = []permission.Permission{} + } + + response.WriteJSON(w, u) +} + // SetCategoryPermissions persists specified category permissions func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request) { method := "permission.SetCategoryPermissions" diff --git a/domain/permission/mysql/store.go b/domain/permission/mysql/store.go index e1da8f4d..10d67704 100644 --- a/domain/permission/mysql/store.go +++ b/domain/permission/mysql/store.go @@ -182,9 +182,9 @@ func (s Scope) GetCategoryPermissions(ctx domain.RequestContext, catID string) ( // GetCategoryUsers returns space permissions for all users. func (s Scope) GetCategoryUsers(ctx domain.RequestContext, catID string) (u []user.User, err error) { err = s.Runtime.Db.Select(&u, ` - SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised - FROM user u, account a - WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN ( + 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=? UNION ALL SELECT r.userid 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=? @@ -197,7 +197,7 @@ func (s Scope) GetCategoryUsers(ctx domain.RequestContext, catID string) (u []us err = nil } if err != nil { - err = errors.Wrap(err, fmt.Sprintf("unable to execute select category user %s", catID)) + err = errors.Wrap(err, fmt.Sprintf("unable to execute select users for category %s", catID)) return } diff --git a/domain/storer.go b/domain/storer.go index 6e3c05bc..6638c547 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -68,6 +68,7 @@ type CategoryStorer interface { Get(ctx RequestContext, id string) (c category.Category, err error) GetBySpace(ctx RequestContext, spaceID string) (c []category.Category, err error) GetAllBySpace(ctx RequestContext, spaceID string) (c []category.Category, err error) + GetSpaceCategorySummary(ctx RequestContext, spaceID string) (c []category.SummaryModel, err error) Delete(ctx RequestContext, id string) (rows int64, err error) AssociateDocument(ctx RequestContext, m category.Member) (err error) DisassociateDocument(ctx RequestContext, categoryID, documentID string) (rows int64, err error) diff --git a/gui/app/components/folder/category-admin.js b/gui/app/components/folder/category-admin.js index 2bfb1927..c99c1ae0 100644 --- a/gui/app/components/folder/category-admin.js +++ b/gui/app/components/folder/category-admin.js @@ -44,6 +44,20 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, DropdownMixin this.get('categoryService').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) => { + c.forEach((cat) => { + 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; + + cat.set('documents', docCount); + cat.set('users', userCount); + }); + }); + // get users that this space admin user can see this.get('userService').getAll().then((users) => { // set up Everyone user @@ -132,12 +146,12 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, DropdownMixin let users = this.get('users'); let category = this.get('category').findBy('id', catId); - this.get('categoryService').getViewers(category.get('id')).then((viewers) => { + 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 selected = viewers.isAny('id', user.get('id')); + let userId = user.get('id') === '0' ? '' : user.get('id'); + let selected = viewers.isAny('whoId', userId); user.set('selected', selected); }); @@ -173,17 +187,22 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, DropdownMixin let viewers = []; users.forEach((user) => { + let userId = user.get('id'); + if (userId === "0") userId = ''; + let v = { orgId: this.get('folder.orgId'), folderId: this.get('folder.id'), categoryId: category.get('id'), - userId: user.get('id') + userId: userId }; viewers.push(v); }); - this.get('categoryService').setViewers(category.get('id'), viewers).then( () => {}); + this.get('categoryService').setViewers(category.get('id'), viewers).then(() => { + this.load(); + }); this.closeDropdown(); } diff --git a/gui/app/models/category.js b/gui/app/models/category.js index 1a2f7620..35665f3e 100644 --- a/gui/app/models/category.js +++ b/gui/app/models/category.js @@ -17,5 +17,9 @@ export default Model.extend({ folderId: attr('string'), category: attr('string'), created: attr(), - revised: attr() + revised: attr(), + + // fields used by UI only + documents: attr(), + users: attr(), }); diff --git a/gui/app/services/category.js b/gui/app/services/category.js index a3eea11c..ccbba2a4 100644 --- a/gui/app/services/category.js +++ b/gui/app/services/category.js @@ -85,10 +85,19 @@ export default BaseService.extend({ }); }, - // Get list of users who can see given category - getViewers(categoryId) { + // Get viewer permission records for given category + getPermissions(categoryId) { return this.get('ajax').request(`category/${categoryId}/permission`, { method: 'GET' + }).then((response) => { + return response; + }); + }, + + // Get list of users who can see given category + getUsers(categoryId) { + return this.get('ajax').request(`category/${categoryId}/user`, { + method: 'GET' }).then((response) => { let data = []; @@ -109,4 +118,13 @@ export default BaseService.extend({ data: JSON.stringify(viewers) }); }, + + // Get count of documents and users associated with each category in given space. + getSummary(spaceId) { + return this.get('ajax').request(`category/space/${spaceId}/summary`, { + method: 'GET' + }).then((response) => { + return response; + }); + } }); diff --git a/gui/app/templates/components/folder/category-admin.hbs b/gui/app/templates/components/folder/category-admin.hbs index 9d7874c0..cc757b07 100644 --- a/gui/app/templates/components/folder/category-admin.hbs +++ b/gui/app/templates/components/folder/category-admin.hbs @@ -16,7 +16,7 @@ {{else}}
{{cat.category}}
-
7 documents, 14 people
+
{{cat.documents}} documents, {{cat.users}} people
{{/if}}
@@ -66,12 +66,8 @@ {{ui/ui-list-picker items=categoryUsers nameField='fullname'}}
-
- cancel -
-
- grant access -
+
cancel
+
grant access
diff --git a/model/category/category.go b/model/category/category.go index 8557350a..97eefdc7 100644 --- a/model/category/category.go +++ b/model/category/category.go @@ -29,3 +29,10 @@ type Member struct { LabelID string `json:"folderId"` DocumentID string `json:"documentId"` } + +// SummaryModel holds number of documents and users for space categories. +type SummaryModel struct { + Type string `json:"type"` // documents or users + CategoryID string `json:"categoryId"` + Count int64 `json:"count"` +} diff --git a/server/routing/routes.go b/server/routing/routes.go index 19c34764..757cad2e 100644 --- a/server/routing/routes.go +++ b/server/routing/routes.go @@ -132,8 +132,10 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "category", []string{"POST", "OPTIONS"}, nil, category.Add) Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update) Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"DELETE", "OPTIONS"}, nil, category.Delete) + Add(rt, RoutePrefixPrivate, "category/space/{spaceID}/summary", []string{"GET", "OPTIONS"}, nil, category.GetSummary) 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/{categoryID}/user", []string{"GET", "OPTIONS"}, nil, permission.GetCategoryViewers) Add(rt, RoutePrefixPrivate, "users/{userID}/password", []string{"POST", "OPTIONS"}, nil, user.ChangePassword) Add(rt, RoutePrefixPrivate, "users", []string{"POST", "OPTIONS"}, nil, user.Add)