import * as fs from 'fs' ; import * as path from 'path' ; import { IPermission } from '@materia/interfaces' ; import { App } from '../app' ; import { MateriaError } from '../error' ; import { Permission } from './permission' ; class classdesc export class Permissions { permissions : Permission [ ] ; constructor ( private app ) { this . app = app ; this . clear ( ) ; } param { Array < string > } returns { function } check ( permissionsName ) { return ( reqOrig , resOrig , nextOrig ) => { let chain = ( req , res , next ) => { next ( ) ; } ; const rev_permissions = [ ... permissionsName ] . reverse ( ) ; rev_permissions . every ( permissionName => { const permission = this . permissions . find ( p => p . name == permissionName ) ; if ( ! permission ) { const err = new MateriaError ( 'Could not find permission "' + permissionName + '"' ) ; resOrig . status ( 500 ) . json ( err . message ) ; nextOrig ( err . message ) ; return false ; } const nextchain = chain ; chain = ( req , res , next ) => { const _next = e => { if ( e ) { return res . status ( 401 ) . json ( JSON . stringify ( e . message ) ) ; } nextchain ( req , res , next ) ; } ; const cl = permission . middleware . default ? permission . middleware . default : permission . middleware ; try { if ( this . isClass ( cl ) ) { const obj = new cl ( this . app ) ; obj . check ( req , res , _next ) ; } else { req . app = this . app ; cl ( req , res , _next ) ; } } catch ( e ) { _next ( e ) ; } } ; return true ; } ) ; chain ( reqOrig , resOrig , nextOrig ) ; } ; } /**
     * Load all the registered permissions
     */ load ( ) { this . clear ( ) ; let permissionsPath , resolvedPath , permissionsRaw ; try { permissionsPath = path . join ( this . app . path , 'server' , 'permissions.json' ) ; resolvedPath = require . resolve ( permissionsPath ) ; if ( require . cache [ resolvedPath ] ) { delete require . cache [ resolvedPath ] ; } permissionsRaw = require ( permissionsPath ) ; } catch ( e ) { return Promise . resolve ( ) ; } if ( permissionsRaw && permissionsRaw . length ) { const results = [ ] ; permissionsRaw . forEach ( ( permissionRaw ) => { if ( permissionRaw . file && permissionRaw . name ) { results . push ( this . add ( permissionRaw ) ) ; } } ) ; return Promise . all ( results ) . then ( ( ) => { return ; } ) ; } else { return Promise . resolve ( ) ; } } /**
     * Remove all permissions
    */ clear ( ) { this . permissions = [ ] ; this . add ( { name : 'Anyone' , description : 'Anyone are allowed without restriction' , middleware : ( req , res , next ) => { return next ( ) ; } , readOnly : true } ) ; } returns { Array < Permission > } findAll ( ) { return this . permissions ; } param { string } returns { function } get ( name ) { return this . permissions . find ( permission => { return permission . name == name ; } ) ; } param { IPermission } param { object } returns { Promise < Permission > } add ( perm , opts ? ) { if ( ! perm ) { return Promise . reject ( new MateriaError ( `The information of the permission to be added not found` ) ) ; } if ( this . permissions . find ( permission => permission . name == perm . name ) ) { return Promise . reject ( new MateriaError ( `The permission ${ perm . name } already exists` ) ) ; } try { this . permissions . push ( new Permission ( this . app , perm ) ) ; } catch ( e ) { return Promise . reject ( e ) ; } let p = Promise . resolve ( ) ; if ( ! opts ) { opts = { } ; } if ( opts . save && perm . file ) { p = p . then ( ( ) => this . app . saveFile ( path . join ( this . app . path , 'server' , 'permissions' , `${ perm . file }.js` ) , `module.exports = ${ perm . middleware . toString ( ) }` , { mkdir : true } ) . then ( ( ) => this . save ( ) ) ) ; } return p . then ( ( ) => this . get ( perm . name ) ) ; } param { string } param { IPermission } param { object } returns { Promise < Permission > } update ( name , permission , opts ? ) { return new Promise ( ( resolve , reject ) => { this . permissions . forEach ( p => { if ( p . file && ! p . readOnly && name == p . name ) { p . name = permission . name ; p . description = permission . description ; let filepath = p . file ; filepath = filepath . indexOf ( path . join ( this . app . path , 'server' , 'permissions' ) ) == - 1 ? path . join ( this . app . path , 'server' , 'permissions' , p . file ) : p . file ; filepath = filepath . indexOf ( '.js' ) !== - 1 ? filepath : filepath + '.js' ; if ( path . join ( this . app . path , 'server' , 'permissions' , permission . file + '.js' ) != filepath ) { fs . rename ( filepath , path . join ( this . app . path , 'server' , 'permissions' , permission . file + '.js' ) , err => { if ( err ) { return reject ( err ) ; } p . file = path . join ( this . app . path , 'server' , 'permissions' , permission . file ) ; p . reload ( ) ; if ( opts && opts . save ) { return this . save ( ) . then ( ( ) => { return resolve ( p ) ; } ) ; } else { return resolve ( p ) ; } } ) ; } else { p . reload ( ) ; if ( opts && opts . save ) { this . save ( ) . then ( ( ) => { return resolve ( p ) ; } ) ; } else { return resolve ( p ) ; } } } } ) ; } ) ; } param { string } returns { Promise } remove ( name , opts ? ) { const permission = this . permissions . find ( p => { return p . name === name ; } ) ; const index = this . permissions . indexOf ( permission ) ; let filepath = permission . file . indexOf ( path . join ( this . app . path , 'server' , 'permissions' ) ) == - 1 ? path . join ( this . app . path , 'server' , 'permissions' , permission . file ) : permission . file ; filepath = filepath . indexOf ( '.js' ) !== - 1 ? filepath : filepath + '.js' ; if ( index != - 1 ) { if ( opts && opts . removeSource ) { fs . unlinkSync ( filepath ) ; } this . permissions . splice ( index , 1 ) ; if ( opts && opts . save ) { return this . save ( ) ; } return Promise . resolve ( ) ; } } /**
     * Save all current permissions as JSON in APP_PATH/server/permissions.json
    */ save ( ) { const permissionsToSave = this . toJson ( ) . filter ( ( perm ) => ! perm . readOnly || ! perm . fromAddon ) . map ( perm => ( { name : perm . name , file : perm . file , description : perm . description } ) ) ; return this . app . saveFile ( path . join ( this . app . path , 'server' , 'permissions.json' ) , JSON . stringify ( permissionsToSave , null , 2 ) , { mkdir : true } ) ; } returns { Array < IPermission > } toJson ( ) { const result = [ ] ; this . permissions . forEach ( permission => { result . push ( permission . toJson ( ) ) ; } ) ; return result ; } private isClass ( fn ) { return ( typeof fn !== 'function' || fn . toString ( ) . startsWith ( 'class' ) || Boolean ( fn . prototype && ! Object . getOwnPropertyDescriptor ( fn , 'prototype' ) . writable ) ) ; } }