|
|
|
@ -1,14 +1,15 @@ |
|
|
|
const CouchDB = require("../../db") |
|
|
|
const { cloneDeep } = require("lodash/fp") |
|
|
|
|
|
|
|
const BUILTIN_IDS = { |
|
|
|
ADMIN: "ADMIN", |
|
|
|
POWER: "POWER_USER", |
|
|
|
BASIC: "BASIC", |
|
|
|
ANON: "ANON", |
|
|
|
PUBLIC: "PUBLIC", |
|
|
|
BUILDER: "BUILDER", |
|
|
|
} |
|
|
|
|
|
|
|
function AccessLevel(id, name, inherits = null) { |
|
|
|
function AccessLevel(id, name, inherits) { |
|
|
|
this._id = id |
|
|
|
this.name = name |
|
|
|
if (inherits) { |
|
|
|
@ -19,8 +20,8 @@ function AccessLevel(id, name, inherits = null) { |
|
|
|
exports.BUILTIN_LEVELS = { |
|
|
|
ADMIN: new AccessLevel(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER), |
|
|
|
POWER: new AccessLevel(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC), |
|
|
|
BASIC: new AccessLevel(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.ANON), |
|
|
|
ANON: new AccessLevel(BUILTIN_IDS.ANON, "Anonymous"), |
|
|
|
BASIC: new AccessLevel(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC), |
|
|
|
ANON: new AccessLevel(BUILTIN_IDS.PUBLIC, "Public"), |
|
|
|
BUILDER: new AccessLevel(BUILTIN_IDS.BUILDER, "Builder"), |
|
|
|
} |
|
|
|
|
|
|
|
@ -36,27 +37,64 @@ function isBuiltin(accessLevel) { |
|
|
|
return exports.BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevel) !== -1 |
|
|
|
} |
|
|
|
|
|
|
|
class AccessController { |
|
|
|
constructor(appId) { |
|
|
|
this.appId = appId |
|
|
|
this.accessLevels = {} |
|
|
|
/** |
|
|
|
* Gets the access level object, this is mainly useful for two purposes, to check if the level exists and |
|
|
|
* to check if the access level inherits any others. |
|
|
|
* @param {string} appId The app in which to look for the access level. |
|
|
|
* @param {string|null} accessLevelId The level ID to lookup. |
|
|
|
* @returns {Promise<AccessLevel|object|null>} The access level object, which may contain an "inherits" property. |
|
|
|
*/ |
|
|
|
exports.getAccessLevel = async (appId, accessLevelId) => { |
|
|
|
if (!accessLevelId) { |
|
|
|
return null |
|
|
|
} |
|
|
|
|
|
|
|
async getAccessLevel(accessLevelId) { |
|
|
|
if (this.accessLevels[accessLevelId]) { |
|
|
|
return this.accessLevels[accessLevelId] |
|
|
|
} |
|
|
|
let accessLevel |
|
|
|
if (isBuiltin(accessLevelId)) { |
|
|
|
accessLevel = Object.values(exports.BUILTIN_LEVELS).find( |
|
|
|
let accessLevel |
|
|
|
if (isBuiltin(accessLevelId)) { |
|
|
|
accessLevel = cloneDeep( |
|
|
|
Object.values(exports.BUILTIN_LEVELS).find( |
|
|
|
level => level._id === accessLevelId |
|
|
|
) |
|
|
|
} else { |
|
|
|
const db = new CouchDB(this.appId) |
|
|
|
accessLevel = await db.get(accessLevelId) |
|
|
|
} |
|
|
|
this.accessLevels[accessLevelId] = accessLevel |
|
|
|
return accessLevel |
|
|
|
) |
|
|
|
} else { |
|
|
|
const db = new CouchDB(appId) |
|
|
|
accessLevel = await db.get(accessLevelId) |
|
|
|
} |
|
|
|
return accessLevel |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Returns an ordered array of the user's inherited access level IDs, this can be used |
|
|
|
* to determine if a user can access something that requires a specific access level. |
|
|
|
* @param {string} appId The ID of the application from which access levels should be obtained. |
|
|
|
* @param {string} userAccessLevelId The user's access level, this can be found in their access token. |
|
|
|
* @returns {Promise<string[]>} returns an ordered array of the access levels, with the first being their |
|
|
|
* highest level of access and the last being the lowest level. |
|
|
|
*/ |
|
|
|
exports.getUserAccessLevelHierarchy = async (appId, userAccessLevelId) => { |
|
|
|
// special case, if they don't have a level then they are a public user
|
|
|
|
if (!userAccessLevelId) { |
|
|
|
return [BUILTIN_IDS.PUBLIC] |
|
|
|
} |
|
|
|
let accessLevelIds = [userAccessLevelId] |
|
|
|
let userAccess = await exports.getAccessLevel(appId, userAccessLevelId) |
|
|
|
// check if inherited makes it possible
|
|
|
|
while ( |
|
|
|
userAccess && |
|
|
|
userAccess.inherits && |
|
|
|
accessLevelIds.indexOf(userAccess.inherits) === -1 |
|
|
|
) { |
|
|
|
accessLevelIds.push(userAccess.inherits) |
|
|
|
// go to get the inherited incase it inherits anything
|
|
|
|
userAccess = await exports.getAccessLevel(appId, userAccess.inherits) |
|
|
|
} |
|
|
|
// add the user's actual level at the end (not at start as that stops iteration
|
|
|
|
return accessLevelIds |
|
|
|
} |
|
|
|
|
|
|
|
class AccessController { |
|
|
|
constructor(appId) { |
|
|
|
this.appId = appId |
|
|
|
this.userHierarchies = {} |
|
|
|
} |
|
|
|
|
|
|
|
async hasAccess(tryingAccessLevelId, userAccessLevelId) { |
|
|
|
@ -70,16 +108,16 @@ class AccessController { |
|
|
|
) { |
|
|
|
return true |
|
|
|
} |
|
|
|
let userAccess = await this.getAccessLevel(userAccessLevelId) |
|
|
|
// check if inherited makes it possible
|
|
|
|
while (userAccess.inherits) { |
|
|
|
if (tryingAccessLevelId === userAccess.inherits) { |
|
|
|
return true |
|
|
|
} |
|
|
|
// go to get the inherited incase it inherits anything
|
|
|
|
userAccess = await this.getAccessLevel(userAccess.inherits) |
|
|
|
let accessLevelIds = this.userHierarchies[userAccessLevelId] |
|
|
|
if (!accessLevelIds) { |
|
|
|
accessLevelIds = await exports.getUserAccessLevelHierarchy( |
|
|
|
this.appId, |
|
|
|
userAccessLevelId |
|
|
|
) |
|
|
|
this.userHierarchies[userAccessLevelId] = userAccessLevelId |
|
|
|
} |
|
|
|
return false |
|
|
|
|
|
|
|
return accessLevelIds.indexOf(tryingAccessLevelId) !== -1 |
|
|
|
} |
|
|
|
|
|
|
|
async checkScreensAccess(screens, userAccessLevelId) { |
|
|
|
|