const Model = require('flitter-orm/src/model/Model')
/**
* @module flitter-auth/model/User
*/
const uuid = require('uuid/v4')
/**
* Base class for the flitter-auth User model.
* @extends module:flitter-orm/src/model/Model~Model
*/
class BaseUser extends Model {
/**
* Defines the services required by this model.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'configs', 'auth']
}
/**
* Defines the schema for this model.
* @returns {object}
*/
static get schema() {
return {
uid: String,
provider: { type: String, default: 'flitter' },
data: { type: String, default: '{}' },
uuid: { type: String, default: uuid },
roles: [String],
permissions: [String],
last_auth: Date,
block_login: Boolean,
// Fields needed by the flitter provider:
password: String,
}
}
static async authFilter() {
const filter = (await this.filter()).field('block_login')
.not().equal(true).end()
.end()
return filter
}
static async lookup(params) {
const filter = await this.authFilter()
filter.absorb(params)
return filter.end().findOne()
}
/**
* Get's a value from the user's serialized JSON.
* @param {string} key
* @return {*}
*/
data_get(key){
return JSON.parse(this.data ? this.data : '{}')[key]
}
/**
* Stores a value in the user's serialized JSON.
* @param {string} key
* @param value
*/
data_set(key, value){
const data = JSON.parse(this.data ? this.data : '{}')
data[key] = value
this.data = JSON.stringify(data)
}
/**
* Checks if an array of permissions contains a permission that
* permits the passed in permission.
* @example
* _array_allow_permission(['foo:bar', 'baz'], 'foo') // false
* @example
* _array_allow_permission(['foo:bar', 'baz'], 'foo:bar') // true
* @example
* _array_allow_permission(['foo:bar', 'baz'], 'foo:bar:view') // true
* @param {Array<string>} array_of_permissions
* @param {string} permission
* @returns {boolean} - true if the permission check passed
*/
_array_allow_permission(array_of_permissions, permission) {
const permission_parts = permission.split(':')
for ( let i = permission_parts.length; i > 0; i-- ) {
const permission_string = permission_parts.slice(0, i).join(':')
if ( array_of_permissions.includes(permission_string) ) return true
}
return false
}
/**
* Checks if the specified role name contains the specified permission.
* Accounts for permissions with arguments. (e.g. view_image:2234)
* @param {string} role - the name of the role
* @param {string} permission - the name of the permission
* @returns {boolean} - true if the role contains the permission
*/
role_has_permission(role, permission){
const role_config = this.configs.get('auth.roles')[role]
if ( !role_config ) return false
return this._array_allow_permission(role_config, permission)
}
/**
* Checks if a user has the specified permission.
* Permissions may include an argument specified like so:
* view_image:3348
* If no argument is provided, it is assumed that the user
* has permission regardless of the argument.
*
* @param {String} permission
* @returns {Boolean} - true if the user has permission
*/
can(permission){
// Check roles first:
for ( let role_key in this.roles ){
if ( !this.roles.hasOwnProperty(role_key) ) continue
if ( this.role_has_permission(this.roles[role_key], permission) ) return true
}
// Check user permissions:
return this._array_allow_permission(this.permissions, permission)
}
/**
* Applies the specified role to the user, if they do not already have it.
* @param {string} role
*/
promote(role){
if ( !this.roles.includes(role) ) this.roles.push(role)
}
/**
* Strips the specified role from the user, if they have it.
* @param {string} role
*/
demote(role){
if ( this.roles.includes(role) ) this.roles = this.roles.filter(r => r !== role)
}
/**
* Applies the specified permission to the user if their current permission set
* doesn't already allow them to access it, or if the force flag is set.
* @param {string} permission
* @param {Boolean} [force = false] - if true, add the permission to the user even if it is covered by a generic permission or role
*/
allow(permission, force = false){
if ( !this.can(permission) || (force && !this.permissions.includes(permission)) ) this.permissions.push(permission)
}
/**
* Strips the permission from the user, if they have it.
* @param {string} permission
*/
disallow(permission){
if ( this.permissions.includes(permission) ) this.permissions = this.permissions.filter(p => p !== permission)
}
/**
* Get the auth provider this user is managed through.
* @returns {module:flitter-auth/Provider~Provider}
*/
get_provider() {
return this.auth.get_provider(this.provider)
}
}
module.exports = exports = BaseUser