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

New schema for permissions and roles management

This commit is contained in:
Harvey Kandola 2017-09-12 09:59:43 +01:00
parent fbf2222eda
commit c51ba65b1d
17 changed files with 810 additions and 665 deletions

View file

@ -8,7 +8,7 @@ The mission is to bring software dev inspired features (refactoring, testing, li
## Latest version ## Latest version
v1.53.5 v1.54.0
## OS Support ## OS Support

View file

@ -120,8 +120,8 @@ func Check(runtime *env.Runtime) bool {
{ // check all the required tables exist { // check all the required tables exist
var tables = []string{`account`, var tables = []string{`account`,
`attachment`, `audit`, `document`, `attachment`, `document`,
`label`, `labelrole`, `organization`, `label`, `organization`,
`page`, `revision`, `search`, `user`} `page`, `revision`, `search`, `user`}
for _, table := range tables { for _, table := range tables {

View file

@ -0,0 +1,123 @@
/* community edition */
-- permission records space and document level privelges, making existing labelrole table obsolete
-- who column can be user or role
-- whoid column contains eitehr user or role ID
-- action column records permission type (view, edit, delete...)
-- scope column details if action applies to object or table
-- location column details name of table
-- refid column details ID of item that the action applies to (only if scope=object)
DROP TABLE IF EXISTS `permission`;
CREATE TABLE IF NOT EXISTS `permission` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`who` VARCHAR(30) NOT NULL,
`whoid` CHAR(16) DEFAULT '' NOT NULL COLLATE utf8_bin,
`action` VARCHAR(30) NOT NULL,
`scope` VARCHAR(30) NOT NULL,
`location` VARCHAR(100) NOT NULL,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX `idx_permission_id` (`id` ASC),
INDEX `idx_permission_orgid` (`orgid` ASC))
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ENGINE = MyISAM;
-- category represents "folder/label" assignment to document (1:M)
DROP TABLE IF EXISTS `category`;
CREATE TABLE IF NOT EXISTS `category` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`labelid` CHAR(16) NOT NULL COLLATE utf8_bin,
`label` VARCHAR(30) NOT NULL,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX `idx_category_id` (`id` ASC),
INDEX `idx_category_refid` (`refid` ASC),
INDEX `idx_category_orgid` (`orgid` ASC))
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ENGINE = MyISAM;
-- category member records who can see a category and the documents within
DROP TABLE IF EXISTS `categorymember`;
CREATE TABLE IF NOT EXISTS `categorymember` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`categoryid` CHAR(16) NOT NULL COLLATE utf8_bin,
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
UNIQUE INDEX `idx_categorymember_id` (`id` ASC),
INDEX `idx_category_documentid` (`documentid`))
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ENGINE = MyISAM;
-- rolee represent user groups
DROP TABLE IF EXISTS `role`;
CREATE TABLE IF NOT EXISTS `role` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`role` VARCHAR(30) NOT NULL,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX `idx_category_id` (`id` ASC),
INDEX `idx_category_refid` (`refid` ASC),
INDEX `idx_category_orgid` (`orgid` ASC))
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ENGINE = MyISAM;
-- role member records user role membership
DROP TABLE IF EXISTS `rolemember`;
CREATE TABLE IF NOT EXISTS `rolemember` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
`roleid` CHAR(16) NOT NULL COLLATE utf8_bin,
`userid` CHAR(16) NOT NULL COLLATE utf8_bin,
UNIQUE INDEX `idx_category_id` (`id` ASC))
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
ENGINE = MyISAM;
-- user account can have global permssion to state if user can see all other users
-- provides granular control for external users
ALTER TABLE account ADD COLUMN `users` BOOL NOT NULL DEFAULT 1 AFTER `admin`;
-- migrate space/document permissions
-- space own
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'own' as `action`, 'object' as scope, 'space' as location, refid
FROM label;
-- space manage (same as owner)
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'manage' as `action`, 'object' as scope, 'space' as location, refid
FROM label;
-- view space
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'view' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canview=1;
-- edit space => add/edit/delete/move/copy/template documents
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-add' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-edit' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-delete' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-move' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-copy' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;
INSERT INTO permission (orgid, who, whoid, `action`, scope, location, refid)
SELECT orgid, 'user' as who, userid as whois, 'doc-template' as `action`, 'object' as scope, 'space' as location, labelid as refid
FROM labelrole WHERE canedit=1;

View file

@ -34,7 +34,7 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro
account.Created = time.Now().UTC() account.Created = time.Now().UTC()
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := ctx.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") stmt, err := ctx.Transaction.Preparex("INSERT INTO account (refid, orgid, userid, admin, editor, users, active, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {
@ -42,7 +42,7 @@ func (s Scope) Add(ctx domain.RequestContext, account account.Account) (err erro
return return
} }
_, err = stmt.Exec(account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Active, account.Created, account.Revised) _, err = stmt.Exec(account.RefID, account.OrgID, account.UserID, account.Admin, account.Editor, account.Users, account.Active, account.Created, account.Revised)
if err != nil { if err != nil {
err = errors.Wrap(err, "unable to execute insert for account") err = errors.Wrap(err, "unable to execute insert for account")
@ -115,7 +115,7 @@ func (s Scope) CountOrgAccounts(ctx domain.RequestContext) (c int) {
func (s Scope) UpdateAccount(ctx domain.RequestContext, account account.Account) (err error) { func (s Scope) UpdateAccount(ctx domain.RequestContext, account account.Account) (err error) {
account.Revised = time.Now().UTC() account.Revised = time.Now().UTC()
stmt, err := ctx.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid") stmt, err := ctx.Transaction.PrepareNamed("UPDATE account SET userid=:userid, admin=:admin, editor=:editor, users=:users, active=:active, revised=:revised WHERE orgid=:orgid AND refid=:refid")
defer streamutil.Close(stmt) defer streamutil.Close(stmt)
if err != nil { if err != nil {

View file

@ -467,6 +467,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
a.Editor = u.Editor a.Editor = u.Editor
a.Admin = u.Admin a.Admin = u.Admin
a.Active = u.Active a.Active = u.Active
a.Users = u.ViewUsers
err = h.Store.Account.UpdateAccount(ctx, a) err = h.Store.Account.UpdateAccount(ctx, a)
if err != nil { if err != nil {

View file

@ -240,29 +240,6 @@ func (s Scope) GetVisibleUsers(ctx domain.RequestContext) (u []user.User, err er
return return
} }
/*
`SELECT
id, refid, firstname, lastname, email, initials, password, salt, reset, created, revised
FROM
user
WHERE
refid IN (SELECT userid FROM account where orgid = '4Tec34w8')
AND refid IN
(SELECT userid FROM labelrole where userid != '' AND orgid='4Tec34w8'
AND labelid IN (
SELECT refid FROM label WHERE orgid='4Tec34w8' AND type=2 AND userid='iJdf6qUW'
UNION ALL
SELECT refid FROM label a WHERE orgid='4Tec34w8' AND type=1 AND refid IN (SELECT labelid FROM labelrole WHERE orgid='4Tec34w8' AND userid='' AND (canedit=1 OR canview=1))
UNION ALL
SELECT refid FROM label a WHERE orgid='4Tec34w8' AND type=3 AND refid IN (SELECT labelid FROM labelrole WHERE orgid='4Tec34w8' AND userid='iJdf6qUW' AND (canedit=1 OR canview=1))
)
GROUP BY userid)
ORDER BY
firstname, lastname`
*/
// UpdateUser updates the user table using the given replacement user record. // UpdateUser updates the user table using the given replacement user record.
func (s Scope) UpdateUser(ctx domain.RequestContext, u user.User) (err error) { func (s Scope) UpdateUser(ctx domain.RequestContext, u user.User) (err error) {
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()

View file

@ -39,12 +39,14 @@ func AttachUserAccounts(ctx domain.RequestContext, s domain.Store, orgID string,
u.Editor = false u.Editor = false
u.Admin = false u.Admin = false
u.Active = false u.Active = false
u.ViewUsers = false
for _, account := range u.Accounts { for _, account := range u.Accounts {
if account.OrgID == orgID { if account.OrgID == orgID {
u.Admin = account.Admin u.Admin = account.Admin
u.Editor = account.Editor u.Editor = account.Editor
u.Active = account.Active u.Active = account.Active
u.ViewUsers = account.Users
break break
} }
} }

View file

@ -41,8 +41,8 @@ func main() {
// product details // product details
rt.Product = env.ProdInfo{} rt.Product = env.ProdInfo{}
rt.Product.Major = "1" rt.Product.Major = "1"
rt.Product.Minor = "53" rt.Product.Minor = "54"
rt.Product.Patch = "5" rt.Product.Patch = "0"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = "Community" rt.Product.Edition = "Community"
rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition) rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)

File diff suppressed because one or more lines are too long

View file

@ -71,7 +71,7 @@ export default Ember.Component.extend(AuthProvider, {
this.set('selectedUsers', su); this.set('selectedUsers', su);
this.set('hasSelectedUsers', su.length > 0); this.set('hasSelectedUsers', su.length > 0);
}, },
toggleActive(id) { toggleActive(id) {
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
user.set('active', !user.get('active')); user.set('active', !user.get('active'));
@ -90,11 +90,17 @@ export default Ember.Component.extend(AuthProvider, {
this.attrs.onSave(user); this.attrs.onSave(user);
}, },
toggleUsers(id) {
let user = this.users.findBy("id", id);
user.set('viewUsers', !user.get('viewUsers'));
this.attrs.onSave(user);
},
edit(id) { edit(id) {
let self = this; let self = this;
let user = this.users.findBy("id", id); let user = this.users.findBy("id", id);
let userCopy = user.getProperties('id', 'created', 'revised', 'firstname', 'lastname', 'email', 'initials', 'active', 'editor', 'admin', 'accounts'); let userCopy = user.getProperties('id', 'created', 'revised', 'firstname', 'lastname', 'email', 'initials', 'active', 'editor', 'admin', 'viewUsers', 'accounts');
this.set('editUser', userCopy); this.set('editUser', userCopy);
this.set('password', { this.set('password', {
password: "", password: "",
@ -188,14 +194,14 @@ export default Ember.Component.extend(AuthProvider, {
onBulkDelete() { onBulkDelete() {
let su = this.get('selectedUsers'); let su = this.get('selectedUsers');
su.forEach(userId => { su.forEach(userId => {
this.attrs.onDelete(userId); this.attrs.onDelete(userId);
}); });
this.set('selectedUsers', []); this.set('selectedUsers', []);
this.set('hasSelectedUsers', false); this.set('hasSelectedUsers', false);
return true; return true;
} }
} }

View file

@ -22,6 +22,7 @@ export default Model.extend({
active: attr('boolean', { defaultValue: false }), active: attr('boolean', { defaultValue: false }),
editor: attr('boolean', { defaultValue: false }), editor: attr('boolean', { defaultValue: false }),
admin: attr('boolean', { defaultValue: false }), admin: attr('boolean', { defaultValue: false }),
viewUsers: attr('boolean', { defaultValue: false }),
global: attr('boolean', { defaultValue: false }), global: attr('boolean', { defaultValue: false }),
accounts: attr(), accounts: attr(),
created: attr(), created: attr(),

View file

@ -17,7 +17,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
documentService: Ember.inject.service('document'), documentService: Ember.inject.service('document'),
folderService: Ember.inject.service('folder'), folderService: Ember.inject.service('folder'),
linkService: Ember.inject.service('link'), linkService: Ember.inject.service('link'),
beforeModel(transition) { beforeModel(transition) {
this.set('pageId', is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : ""); this.set('pageId', is.not.undefined(transition.queryParams.page) ? transition.queryParams.page : "");
this.set('folderId', this.paramsFor('document').folder_id); this.set('folderId', this.paramsFor('document').folder_id);

View file

@ -12,9 +12,10 @@
{{focus-input type="text" placeholder="< type here to filter users >" value=filter}} {{focus-input type="text" placeholder="< type here to filter users >" value=filter}}
</div> </div>
</th> </th>
<th class="no-width">Create spaces</th> <th class="no-width">Add Space</th>
<th class="no-width">Is administrator</th> <th class="no-width">View Users</th>
<th class="no-width">Is active</th> <th class="no-width">Admin</th>
<th class="no-width">Active</th>
<th class="no-width"> <th class="no-width">
{{#if hasSelectedUsers}} {{#if hasSelectedUsers}}
<div class="round-button round-button-small button-red" id="bulk-delete-users"> <div class="round-button round-button-small button-red" id="bulk-delete-users">
@ -52,6 +53,15 @@
<i class="material-icons checkbox" {{action 'toggleEditor' user.id}}>check_box_outline_blank</i> <i class="material-icons checkbox" {{action 'toggleEditor' user.id}}>check_box_outline_blank</i>
{{/if}} {{/if}}
</td> </td>
<td class="no-width text-center">
{{#if user.me}}
<i class="material-icons color-gray">check_box</i>
{{else if user.viewUsers}}
<i class="material-icons checkbox" {{action 'toggleUsers' user.id}}>check_box</i>
{{else}}
<i class="material-icons checkbox" {{action 'toggleUsers' user.id}}>check_box_outline_blank</i>
{{/if}}
</td>
<td class="no-width text-center"> <td class="no-width text-center">
{{#if user.me}} {{#if user.me}}
<i class="material-icons color-gray">check_box</i> <i class="material-icons color-gray">check_box</i>

View file

@ -1,6 +1,6 @@
{ {
"name": "documize", "name": "documize",
"version": "1.53.5", "version": "1.54.0",
"description": "The Document IDE", "description": "The Document IDE",
"private": true, "private": true,
"repository": "", "repository": "",

View file

@ -1,16 +1,16 @@
{ {
"community": "community":
{ {
"version": "1.53.5", "version": "1.54.0",
"major": 1, "major": 1,
"minor": 53, "minor": 54,
"patch": 5 "patch": 0
}, },
"enterprise": "enterprise":
{ {
"version": "1.55.5", "version": "1.56.0",
"major": 1, "major": 1,
"minor": 55, "minor": 56,
"patch": 5 "patch": 0
} }
} }

View file

@ -18,6 +18,7 @@ type Account struct {
model.BaseEntity model.BaseEntity
Admin bool `json:"admin"` Admin bool `json:"admin"`
Editor bool `json:"editor"` Editor bool `json:"editor"`
Users bool `json:"viewUsers"`
UserID string `json:"userId"` UserID string `json:"userId"`
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
Company string `json:"company"` Company string `json:"company"`

View file

@ -28,6 +28,7 @@ type User struct {
Active bool `json:"active"` Active bool `json:"active"`
Editor bool `json:"editor"` Editor bool `json:"editor"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
ViewUsers bool `json:"viewUsers"`
Global bool `json:"global"` Global bool `json:"global"`
Password string `json:"-"` Password string `json:"-"`
Salt string `json:"-"` Salt string `json:"-"`