1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 21:29:42 +02:00

Set category permissions for both users and groups

This commit is contained in:
sauls8t 2018-03-06 10:39:47 +00:00
parent d1fdb385e9
commit 22679920d0
17 changed files with 335 additions and 124 deletions

View file

@ -35,6 +35,11 @@ func WriteNotFoundError(w http.ResponseWriter, method string, id string) {
w.Write([]byte("{Error: 'Not found'}")) 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. // WriteServerError notifies HTTP client of general application error.
func WriteServerError(w http.ResponseWriter, method string, err error) { func WriteServerError(w http.ResponseWriter, method string, err error) {
writeStatus(w, http.StatusBadRequest) writeStatus(w, http.StatusBadRequest)

View file

@ -155,15 +155,11 @@ func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
} }
cat, err := h.Store.Category.GetAllBySpace(ctx, spaceID) cat, err := h.Store.Category.GetAllBySpace(ctx, spaceID)
if err != nil && err != sql.ErrNoRows { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
return return
} }
if len(cat) == 0 {
cat = []category.Category{}
}
response.WriteJSON(w, cat) response.WriteJSON(w, cat)
} }
@ -320,10 +316,6 @@ func (h *Handler) GetSummary(w http.ResponseWriter, r *http.Request) {
return return
} }
if len(s) == 0 {
s = []category.SummaryModel{}
}
response.WriteJSON(w, s) response.WriteJSON(w, s)
} }

View file

@ -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) 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 err = nil
c = []category.Category{}
} }
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to execute select all categories for space %s", spaceID)) 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. // GetSpaceCategorySummary returns number of documents and users for space categories.
func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string) (c []category.SummaryModel, err error) { func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string) (c []category.SummaryModel, err error) {
err = s.Runtime.Db.Select(&c, ` 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 UNION ALL
SELECT 'users' as type, refid AS categoryid, count(*) AS count FROM permission WHERE orgid=? AND who='user' AND location='category' 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=?) AND refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?)
GROUP BY refid, type GROUP BY refid, type`,
UNION ALL ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID /*, ctx.OrgID, ctx.OrgID, spaceID*/)
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 { if err == sql.ErrNoRows || len(c) == 0 {
err = nil err = nil
c = []category.SummaryModel{}
} }
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID)) err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID))

View file

@ -41,8 +41,8 @@ func (h *Handler) Meta(w http.ResponseWriter, r *http.Request) {
org, err := h.Store.Organization.GetOrganizationByDomain(data.URL) org, err := h.Store.Organization.GetOrganizationByDomain(data.URL)
if err != nil { if err != nil {
h.Runtime.Log.Error("unable to fetch request meta for "+data.URL, err) h.Runtime.Log.Info("unable to fetch request meta for " + data.URL)
response.WriteForbiddenError(w) response.WriteNotFound(w)
return return
} }

View file

@ -79,16 +79,15 @@ func (s Scope) GetOrganizationByDomain(subdomain string) (o org.Organization, er
return 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", // match on given domain name
subdomain) 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 { if err == nil {
return 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") 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 { if err != nil && err != sql.ErrNoRows {
err = errors.Wrap(err, "unable to execute select for empty subdomain") err = errors.Wrap(err, "unable to execute select for empty subdomain")
} }

View file

@ -266,7 +266,7 @@ func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) {
} }
perms, err := h.Store.Permission.GetSpacePermissions(ctx, spaceID) perms, err := h.Store.Permission.GetSpacePermissions(ctx, spaceID)
if err != nil && err != sql.ErrNoRows { 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)
return return
@ -302,7 +302,7 @@ func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) {
if records[i].Who == permission.UserPermission { if records[i].Who == permission.UserPermission {
if records[i].WhoID == user.EveryoneUserID { if records[i].WhoID == user.EveryoneUserID {
records[i].Name = "Everyone" records[i].Name = user.EveryoneUserName
} else { } else {
u, err := h.Store.User.Get(ctx, records[i].WhoID) u, err := h.Store.User.Get(ctx, records[i].WhoID)
if err != nil { if err != nil {
@ -371,13 +371,58 @@ func (h *Handler) GetCategoryPermissions(w http.ResponseWriter, r *http.Request)
return return
} }
u, err := h.Store.Permission.GetCategoryPermissions(ctx, categoryID) perms, err := h.Store.Permission.GetCategoryPermissions(ctx, categoryID)
if err != nil && err != sql.ErrNoRows { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return 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 // SetCategoryPermissions persists specified category permissions
@ -405,7 +450,7 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request)
return return
} }
var model = []permission.CategoryViewRequestModel{} var model = []permission.CategoryRecord{}
err = json.Unmarshal(body, &model) err = json.Unmarshal(body, &model)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) 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) { if !HasPermission(ctx, *h.Store, spaceID, permission.SpaceManage, permission.SpaceOwner) {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
h.Runtime.Log.Info("no permission to set category permissions")
return return
} }
@ -437,8 +483,8 @@ func (h *Handler) SetCategoryPermissions(w http.ResponseWriter, r *http.Request)
for _, m := range model { for _, m := range model {
perm := permission.Permission{} perm := permission.Permission{}
perm.OrgID = ctx.OrgID perm.OrgID = ctx.OrgID
perm.Who = permission.UserPermission perm.Who = m.Who
perm.WhoID = m.UserID perm.WhoID = m.WhoID
perm.Scope = permission.ScopeRow perm.Scope = permission.ScopeRow
perm.Location = permission.LocationCategory perm.Location = permission.LocationCategory
perm.RefID = m.CategoryID perm.RefID = m.CategoryID

View file

@ -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) { func (s Scope) GetCategoryPermissions(ctx domain.RequestContext, catID string) (r []permission.Permission, err error) {
err = s.Runtime.Db.Select(&r, ` err = s.Runtime.Db.Select(&r, `
SELECT id, orgid, who, whoid, action, scope, location, refid 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 UNION ALL
SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid SELECT p.id, p.orgid, p.who, p.whoid, p.action, p.scope, p.location, p.refid
FROM permission p FROM permission p
LEFT JOIN rolemember r ON p.whoid=r.roleid 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) ctx.OrgID, catID, ctx.OrgID, catID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows || len(r) == 0 {
err = nil err = nil
r = []permission.Permission{} 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 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 FROM user u LEFT JOIN account a ON u.refid = a.userid
WHERE a.orgid=? AND a.active=1 AND u.refid IN ( 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 UNION ALL
SELECT r.userid from rolemember r SELECT r.userid from rolemember r
LEFT JOIN permission p ON p.whoid=r.roleid 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`, ORDER BY firstname, lastname`,
ctx.OrgID, ctx.OrgID, catID, ctx.OrgID, catID) ctx.OrgID, ctx.OrgID, catID, ctx.OrgID, catID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows || len(u) == 0 {
err = nil err = nil
u = []user.User{} 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')`, WHERE p.orgid=? AND p.location='category' AND p.who='role' AND (r.userid=? OR r.userid='0')`,
ctx.OrgID, userID, ctx.OrgID, userID) ctx.OrgID, userID, ctx.OrgID, userID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows || len(r) == 0 {
err = nil err = nil
r = []permission.Permission{} r = []permission.Permission{}
} }

View file

@ -30,7 +30,7 @@ func CanViewSpaceDocument(ctx domain.RequestContext, s domain.Store, labelID str
} }
for _, role := range roles { 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) { pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
return true return true
} }
@ -58,7 +58,7 @@ func CanViewDocument(ctx domain.RequestContext, s domain.Store, documentID strin
} }
for _, role := range roles { 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) { pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
return true return true
} }
@ -88,7 +88,7 @@ func CanChangeDocument(ctx domain.RequestContext, s domain.Store, documentID str
} }
for _, role := range roles { 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 return true
} }
} }
@ -136,7 +136,7 @@ func CanUploadDocument(ctx domain.RequestContext, s domain.Store, spaceID string
} }
for _, role := range roles { 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) { pm.ContainsPermission(role.Action, pm.DocumentAdd) {
return true return true
} }
@ -155,7 +155,7 @@ func CanViewSpace(ctx domain.RequestContext, s domain.Store, spaceID string) boo
return false return false
} }
for _, role := range roles { 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) { pm.ContainsPermission(role.Action, pm.SpaceView, pm.SpaceManage, pm.SpaceOwner) {
return true return true
} }
@ -176,7 +176,7 @@ func HasPermission(ctx domain.RequestContext, s domain.Store, spaceID string, ac
} }
for _, role := range roles { 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 { for _, a := range actions {
if role.Action == a { if role.Action == a {
return true return true

View file

@ -10,14 +10,17 @@
// https://documize.com // https://documize.com
import $ from 'jquery'; import $ from 'jquery';
import Component from '@ember/component'; import { A } from "@ember/array"
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
import ModalMixin from '../../mixins/modal'; import ModalMixin from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(ModalMixin, TooltipMixin, { export default Component.extend(ModalMixin, TooltipMixin, {
userService: service('user'), // userService: service('user'),
categoryService: service('category'), spaceSvc: service('folder'),
groupSvc: service('group'),
categorySvc: service('category'),
appMeta: service(), appMeta: service(),
store: service(), store: service(),
newCategory: '', newCategory: '',
@ -42,41 +45,46 @@ export default Component.extend(ModalMixin, TooltipMixin, {
load() { load() {
// get categories // 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); this.set('category', c);
// get summary of documents and users for each category in space // 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) => { c.forEach((cat) => {
let docs = _.findWhere(s, {categoryId: cat.get('id'), type: 'documents'}); // let docs = _.findWhere(s, {categoryId: cat.get('id'), type: 'documents'});
let docCount = is.not.undefined(docs) ? docs.count : 0; // let docCount = is.not.undefined(docs) ? docs.count : 0;
let users = _.findWhere(s, {categoryId: cat.get('id'), type: 'users'}); // let users = _.findWhere(s, {categoryId: cat.get('id'), type: 'users'});
let userCount = is.not.undefined(users) ? users.count : 0; // 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('documents', docCount);
cat.set('users', userCount); cat.set('users', userCount);
}); });
}); });
});
},
// get users that this space admin user can see permissionRecord(who, whoId, name) {
this.get('userService').getSpaceUsers(this.get('folder.id')).then((users) => { let raw = {
// set up Everyone user id: whoId,
let u = {
orgId: this.get('folder.orgId'), orgId: this.get('folder.orgId'),
folderId: this.get('folder.id'), categoryId: this.get('currentCategory.id'),
userId: '', whoId: whoId,
firstname: 'Everyone', who: who,
lastname: '', name: name,
categoryView: false,
}; };
let data = this.get('store').normalize('user', u) let rec = this.get('store').normalize('category-permission', raw);
users.pushObject(this.get('store').push(data)); return this.get('store').push(rec);
users = users.sortBy('firstname', 'lastname');
this.set('users', users);
});
});
}, },
setEdit(id, val) { setEdit(id, val) {
@ -109,7 +117,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
folderId: this.get('folder.id') folderId: this.get('folder.id')
}; };
this.get('categoryService').add(c).then(() => { this.get('categorySvc').add(c).then(() => {
this.load(); this.load();
}); });
}, },
@ -124,7 +132,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
onDelete() { onDelete() {
this.modalClose('#category-delete-modal'); this.modalClose('#category-delete-modal');
this.get('categoryService').delete(this.get('deleteId')).then(() => { this.get('categorySvc').delete(this.get('deleteId')).then(() => {
this.load(); this.load();
}); });
}, },
@ -150,7 +158,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
cat = this.setEdit(id, false); cat = this.setEdit(id, false);
$('#edit-category-' + cat.get('id')).removeClass('is-invalid'); $('#edit-category-' + cat.get('id')).removeClass('is-invalid');
this.get('categoryService').save(cat).then(() => { this.get('categorySvc').save(cat).then(() => {
this.load(); this.load();
}); });
@ -160,21 +168,37 @@ export default Component.extend(ModalMixin, TooltipMixin, {
onShowAccessPicker(catId) { onShowAccessPicker(catId) {
this.set('showCategoryAccess', true); this.set('showCategoryAccess', true);
let users = this.get('users'); let categoryPermissions = A([]);
let category = this.get('category').findBy('id', catId); let category = this.get('category').findBy('id', catId);
this.get('categoryService').getPermissions(category.get('id')).then((viewers) => { this.set('currentCategory', category);
// mark those users as selected that have already been given permission this.set('categoryPermissions', categoryPermissions);
// to see the current category;
users.forEach((user) => { // get space permissions
let userId = user.get('id'); this.get('spaceSvc').getPermissions(this.get('folder.id')).then((spacePermissions) => {
let selected = viewers.isAny('whoId', userId); spacePermissions.forEach((sp) => {
user.set('selected', selected); 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.get('categorySvc').getPermissions(category.get('id')).then((perms) => {
this.set('currentCategory', category); // 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() { onGrantAccess() {
@ -182,23 +206,9 @@ export default Component.extend(ModalMixin, TooltipMixin, {
let folder = this.get('folder'); let folder = this.get('folder');
let category = this.get('currentCategory'); let category = this.get('currentCategory');
let users = this.get('categoryUsers').filterBy('selected', true); let perms = this.get('categoryPermissions').filterBy('selected', true);
let viewers = [];
users.forEach((user) => { this.get('categorySvc').setViewers(folder.get('id'), category.get('id'), perms).then(() => {
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.load(); this.load();
}); });
} }

View file

@ -0,0 +1,23 @@
// 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
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
});

View file

@ -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
}
};
}
});

View file

@ -87,7 +87,15 @@ export default BaseService.extend({
return this.get('ajax').request(`category/${categoryId}/permission`, { return this.get('ajax').request(`category/${categoryId}/permission`, {
method: 'GET' method: 'GET'
}).then((response) => { }).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;
}); });
}, },

View file

@ -6,7 +6,7 @@
<p class="sub-title">Sub-divide spaces into categories which can contain documents with restricted access.</p> <p class="sub-title">Sub-divide spaces into categories which can contain documents with restricted access.</p>
<form class="form-inline" onsubmit={{action 'onAdd'}}> <form class="form-inline" onsubmit={{action 'onAdd'}}>
<div class="form-group mr-3"> <div class="form-group mr-3">
{{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}}
</div> </div>
<button type="button" class="btn btn-success" onclick={{action 'onAdd'}}>Add</button> <button type="button" class="btn btn-success" onclick={{action 'onAdd'}}>Add</button>
</form> </form>
@ -27,7 +27,7 @@
{{else}} {{else}}
<div class="category col-8"> <div class="category col-8">
<div class="name">{{cat.category}}</div> <div class="name">{{cat.category}}</div>
<div class="info">{{cat.documents}} {{if (eq cat.documents 1) 'document' 'documents' }}, {{cat.users}} {{if (eq cat.users 1) 'person' 'people' }}</div> <div class="info">{{cat.documents}} {{if (eq cat.documents 1) 'document' 'documents' }} &middot; {{cat.users}} users/groups</div>
</div> </div>
{{/if}} {{/if}}
<div class="col-4 buttons text-right"> <div class="col-4 buttons text-right">
@ -72,8 +72,40 @@
</div> </div>
</div> </div>
{{#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')}}
<p>Select who can view documents within category</p> <p>Select who can view documents within category</p>
{{ui/ui-list-picker items=categoryUsers nameField='fullname' singleSelect=false}}
<div class="widget-list-picker">
<ul class="options">
{{#each categoryPermissions as |permission|}}
<li class="option {{if permission.selected 'selected'}}" {{action 'onToggle' permission}}>
<div class="text text-truncate">
{{#if (eq permission.who "role")}}
<span class="button-icon-gray button-icon-small align-middle">
<i class="material-icons">people</i>
</span>
{{else}}
{{#if (eq permission.whoId constants.EveryoneUserId)}}
<span class="button-icon-gray button-icon-small align-middle">
<i class="material-icons">language</i>
</span>
{{else}}
<span class="button-icon-gray button-icon-small align-middle">
<i class="material-icons">person</i>
</span>
{{/if}}
{{/if}}
&nbsp;{{permission.name}}
{{#if (eq permission.whoId session.user.id)}}
<small class="form-text text-muted d-inline-block">(you)</small>
{{/if}}
</div>
{{#if permission.selected}}
<i class="material-icons">check</i>
{{/if}}
</li>
{{/each}}
</ul>
</div>
{{/ui/ui-dialog}} {{/ui/ui-dialog}}
</div> </div>

View file

@ -0,0 +1,75 @@
// 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
// 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
}

View file

@ -136,7 +136,9 @@ type CategoryViewRequestModel struct {
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
SpaceID string `json:"folderId"` SpaceID string `json:"folderId"`
CategoryID string `json:"categoryID"` CategoryID string `json:"categoryID"`
UserID string `json:"userId"` WhoID string `json:"whoId"`
Who WhoType `json:"who"`
// UserID string `json:"userId"`
} }
// SpaceRequestModel details which users have what permissions on a given space. // SpaceRequestModel details which users have what permissions on a given space.

View file

@ -75,4 +75,7 @@ func Exists(users []User, userID string) bool {
const ( const (
// EveryoneUserID provides a shortcut to state "all authenticated users". // EveryoneUserID provides a shortcut to state "all authenticated users".
EveryoneUserID string = "0" EveryoneUserID string = "0"
// EveryoneUserName provides the descriptor for this type of user/group.
EveryoneUserName string = "Everyone"
) )

View file

@ -67,9 +67,18 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) {
organization := organization.Handler{Runtime: rt, Store: s} 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, "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/keycloak", []string{"POST", "OPTIONS"}, nil, keycloak.Authenticate)
Add(rt, RoutePrefixPublic, "authenticate", []string{"POST", "OPTIONS"}, nil, auth.Login) Add(rt, RoutePrefixPublic, "authenticate", []string{"POST", "OPTIONS"}, nil, auth.Login)
Add(rt, RoutePrefixPublic, "validate", []string{"GET", "OPTIONS"}, nil, auth.ValidateToken) 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, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword)
Add(rt, RoutePrefixPublic, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation) 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, "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) 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}", []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, 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/{spaceID}/invitation", []string{"POST", "OPTIONS"}, nil, space.Invite)
Add(rt, RoutePrefixPrivate, "space/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll) Add(rt, RoutePrefixPrivate, "space/manage", []string{"GET", "OPTIONS"}, nil, space.GetAll)
Add(rt, RoutePrefixPrivate, "space/{spaceID}", []string{"GET", "OPTIONS"}, nil, space.Get) 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/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/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"}, []string{"filter", "all"}, category.GetAll)
Add(rt, RoutePrefixPrivate, "category/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.Get) 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/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, category.GetSpaceCategoryMembers)
Add(rt, RoutePrefixPrivate, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership) Add(rt, RoutePrefixPrivate, "category/member", []string{"POST", "OPTIONS"}, nil, category.SetDocumentCategoryMembership)
Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update) Add(rt, RoutePrefixPrivate, "category/{categoryID}", []string{"PUT", "OPTIONS"}, nil, category.Update)