Permissions refactor; create sharing API; update dependencies
This commit is contained in:
@@ -31,14 +31,24 @@ class Page extends Model {
|
||||
UpdateUserId: {type: String},
|
||||
ChildPageIds: [String],
|
||||
|
||||
// Menu flags
|
||||
noDelete: { type: Boolean, default: false },
|
||||
virtual: { type: Boolean, default: false },
|
||||
|
||||
// Sharing properties
|
||||
shared_users_view: [ObjectId],
|
||||
shared_users_update: [ObjectId],
|
||||
shared_users_manage: [ObjectId],
|
||||
};
|
||||
}
|
||||
|
||||
static scopes = [new ActiveScope]
|
||||
|
||||
// Static and instance methods can go here
|
||||
is_shared() {
|
||||
return this.shared_users_view.length > 0 || this.shared_users_update.length > 0 || this.shared_users_manage.length > 0
|
||||
}
|
||||
|
||||
// ================= RELATIONSHIPS =================
|
||||
get user() {
|
||||
const User = this.models.get("auth:User")
|
||||
return this.belongs_to_one(User, "OrgUserId", "_id")
|
||||
@@ -58,8 +68,99 @@ class Page extends Model {
|
||||
return this.belongs_to_one(Parent, "ParentId", "UUID")
|
||||
}
|
||||
|
||||
get view_users() {
|
||||
const User = this.models.get('auth:User')
|
||||
return this.has_many(User, 'shared_users_view', '_id')
|
||||
}
|
||||
|
||||
get update_users() {
|
||||
const User = this.models.get('auth:User')
|
||||
return this.has_many(User, 'shared_users_update', '_id')
|
||||
}
|
||||
|
||||
get manage_users() {
|
||||
const User = this.models.get('auth:User')
|
||||
return this.has_many(User, 'shared_users_manage', '_id')
|
||||
}
|
||||
|
||||
// ================= SECURITY =================
|
||||
accessible_by(user, mode = 'view') {
|
||||
return user.can(`page:${this.UUID}:${mode}`)
|
||||
const base_access = user.can(`page:${this.UUID}:${mode}`)
|
||||
}
|
||||
|
||||
async is_accessible_by(user, mode = 'view') {
|
||||
const can_manage = user.can(`page:${this.UUID}:manage`)
|
||||
const can_update = user.can(`page:${this.UUID}:update`)
|
||||
const can_view = user.can(`page:${this.UUID}:view`)
|
||||
const can_all = user.can(`page:${this.UUID}`)
|
||||
|
||||
// Allow universal access
|
||||
if ( can_all ) return true
|
||||
// deny if blocked
|
||||
else if ( user.can(`page:${this.UUID}:block`) ) return false
|
||||
// manage, update, view can view
|
||||
else if ( mode === 'view' && (can_manage || can_update || can_view) ) return true
|
||||
// manage, update can update
|
||||
else if ( mode === 'update' ) {
|
||||
if ( can_manage || can_update ) return true
|
||||
// If other permissions are explicitly set for this page, use those
|
||||
else if ( can_view ) return false
|
||||
}
|
||||
// manage can manage
|
||||
else if ( mode === 'manage' ) {
|
||||
if ( can_manage ) return true
|
||||
// If other permissions are explicitly set for this page, use those
|
||||
else if ( can_update || can_view ) return false
|
||||
}
|
||||
|
||||
// allow universal access
|
||||
// deny blocked users
|
||||
|
||||
// If there are no explicit permissions set for this node, check the parent
|
||||
const parent = await this.parent
|
||||
if ( parent ) return (await this.parent).is_accessible_by(user, mode)
|
||||
else return false
|
||||
}
|
||||
|
||||
async access_level_for(user) {
|
||||
if ( await this.is_accessible_by(user, 'manage') ) return 'manage'
|
||||
else if ( await this.is_accessible_by(user, 'update') ) return 'update'
|
||||
else if ( await this.is_accessible_by(user, 'view') ) return 'view'
|
||||
else return false
|
||||
}
|
||||
|
||||
async share_with(user, level = 'view') {
|
||||
if ( !['view', 'update', 'manage'].includes(level) ) {
|
||||
throw new Error(`Invalid share level: ${level}`)
|
||||
}
|
||||
|
||||
// Remove existing sharing info
|
||||
await this.unshare_with(user)
|
||||
|
||||
// Add the page to the user's permissions:
|
||||
user.allow(`page:${this.UUID}:${level}`)
|
||||
|
||||
// Add the user to the appropriate access list
|
||||
this[`shared_users_${level}`].push(user._id)
|
||||
|
||||
await this.save()
|
||||
await user.save()
|
||||
}
|
||||
|
||||
async unshare_with(user) {
|
||||
// Remove this page from the user's permissions
|
||||
if ( user.can(`page:${this.UUID}`) ) user.disallow(`page:${this.UUID}`)
|
||||
for ( const level of ['view', 'update', 'manage'] ) {
|
||||
if ( user.can(`page:${this.UUID}:${level}`) ) user.disallow(`page:${this.UUID}:${level}`)
|
||||
}
|
||||
|
||||
// Remove the user from this page's access lists
|
||||
this.shared_users_view = this.shared_users_view.filter(x => String(x) !== user.id)
|
||||
this.shared_users_update = this.shared_users_update.filter(x => String(x) !== user.id)
|
||||
this.shared_users_manage = this.shared_users_manage.filter(x => String(x) !== user.id)
|
||||
|
||||
await this.save()
|
||||
await user.save()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ const Model = require('flitter-auth/model/KeyAction')
|
||||
*/
|
||||
class KeyAction extends Model {
|
||||
|
||||
auth_url() {
|
||||
return this.url().replace('auth/action/', 'auth/action/login/')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = KeyAction
|
||||
|
||||
Reference in New Issue
Block a user