UNPKG

@futoin/security

Version:

FutoIn Security Concept reference implementation

249 lines (214 loc) 9.3 kB
'use strict'; /** * @file * * Copyright 2018 FutoIn Project (https://futoin.org) * Copyright 2018 Andrey Galkin <andrey@futoin.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const AdvancedCCM = require( 'futoin-invoker/AdvancedCCM' ); const NodeExecutor = require( 'futoin-executor/NodeExecutor' ); const Executor = require( 'futoin-executor/Executor' ); const DBAutoConfig = require( 'futoin-database/AutoConfig' ); const _merge = require( 'lodash/merge' ); const SQLStorage = require( 'futoin-secvault/lib/storage/SQLStorage' ); const CachedStorageWrapper = require( 'futoin-secvault/lib/storage/CachedStorageWrapper' ); const KeyFace = require( 'futoin-secvault/KeyFace' ); const KeyService = require( 'futoin-secvault/KeyService' ); const DataFace = require( 'futoin-secvault/DataFace' ); const DataService = require( 'futoin-secvault/DataService' ); const DBGenFace = require( 'futoin-eventstream/DBGenFace' ); const DBGenService = require( 'futoin-eventstream/DBGenService' ); const PushFace = require( 'futoin-eventstream/PushFace' ); const DBPushService = require( 'futoin-eventstream/DBPushService' ); const { MANAGE_FACE, STLS_AUTH_FACE, STLS_MANAGE_FACE, MASTER_AUTH_FACE, MASTER_AUTOREG_FACE, MASTER_MANAGE_FACE, SVKEY_FACE, SVDATA_FACE, EVTGEN_FACE, EVTPUSH_FACE, scopeTemplate, } = require( './lib/main' ); const ManageFace = require( './ManageFace' ); const CachedManageService = require( './CachedManageService' ); const StatelessManageFace = require( './StatelessManageFace' ); const StatelessManageService = require( './StatelessManageService' ); const StatelessAuthFace = require( './StatelessAuthFace' ); const StatelessAuthService = require( './StatelessAuthService' ); const MasterManageFace = require( './MasterManageFace' ); const MasterManageService = require( './MasterManageService' ); const MasterAuthFace = require( './MasterAuthFace' ); const MasterAuthService = require( './MasterAuthService' ); const MasterAutoregFace = require( './MasterAutoregFace' ); const MasterAutoregService = require( './MasterAutoregService' ); const SimpleSecurityProvider = require( './SimpleSecurityProvider' ); /** * All-in-one AuthService initialization */ class ServiceApp { /** * C-tor * * @param {AsyncSteps} as - AsyncSteps interface * @param {object} options={} - options * @param {AdvancedCCM} [options.ccm] - external CCM instance * @param {Executor} [options.publicExecutor] - external public executor instance * @param {Executor} [options.privateExecutor] - external private executor instance * @param {string} [options.storagePassword] - Base64 encoded KEK for storage * @param {object} [options.config] - config overrides for MasterService * @param {object} [options.ccmOptions] - auto-CCM options * @param {callable} [options.notExpectedHandler] - 'notExpected' error handler * @param {object} [options.privateExecutorOptions] - private auto-Executor options * @param {object} [options.publicExecutorOptions] - public auto-Executor options * @param {object} [options.evtOptions] - eventstream options * @param {object} [options.secVaultOptions] - secure vault options * @param {object} [options.securityOptions] - security interface options */ constructor( as, options = {} ) { let { ccm, publicExecutor, privateExecutor, notExpectedHandler, storagePassword, secVaultOptions, securityOptions, } = options; // minimize exposure options.storagePassword = null; // Scope setup const scope = _merge( {}, scopeTemplate ); Object.seal( scope ); Object.seal( scope.config ); // Config from template const config = Object.assign( {}, scopeTemplate.config ); Object.seal( config ); _merge( config, options.config || {} ); // Init of standard FutoIn components if ( !ccm ) { ccm = new AdvancedCCM( options.ccmOptions ); } ccm.once( 'close', () => { this._ccm = null; this.close(); } ); if ( !privateExecutor ) { privateExecutor = new Executor( ccm, options.privateExecutorOptions ); } if ( !publicExecutor ) { const securityProvider = new SimpleSecurityProvider(); const opts = Object.assign( { securityProvider }, options.publicExecutorOptions ); publicExecutor = new NodeExecutor( ccm, opts ); } if ( !notExpectedHandler ) { notExpectedHandler = function() { // eslint-disable-next-line no-console console.log( arguments ); }; } privateExecutor.on( 'notExpected', notExpectedHandler ); publicExecutor.on( 'notExpected', notExpectedHandler ); this._scope = scope; this._ccm = ccm; this._private_executor = privateExecutor; this._public_executor = publicExecutor; // Common database DBAutoConfig( as, ccm, { ftnsec: {}, }, options.databaseConfig ); ccm.alias( '#db.ftnsec', '#db.secvault' ); ccm.alias( '#db.ftnsec', '#db.evt' ); as.add( ( as ) => { // EvtStream DBGenService.register( as, privateExecutor, options.evtOptions ); DBGenFace.register( as, ccm, EVTGEN_FACE, privateExecutor ); DBPushService.register( as, privateExecutor, options.evtOptions ); PushFace.register( as, ccm, EVTPUSH_FACE, privateExecutor ); } ); as.add( ( as ) => { // SecVault ccm.alias( EVTGEN_FACE, '#secvault.evtgen' ); ccm.alias( EVTPUSH_FACE, '#secvault.evtpush' ); const sv_storage = new SQLStorage( ccm, secVaultOptions ); const cached_sv_storage = new CachedStorageWrapper( ccm, sv_storage, Object.assign( { evtpushExecutor: privateExecutor }, secVaultOptions ) ); KeyService.register( as, privateExecutor, cached_sv_storage, secVaultOptions ); KeyFace.register( as, ccm, SVKEY_FACE, privateExecutor ); DataService.register( as, privateExecutor, cached_sv_storage, secVaultOptions ); DataFace.register( as, ccm, SVDATA_FACE, privateExecutor ); } ); as.add( ( as ) => { // Init of FutoIn Security services CachedManageService.register( as, privateExecutor, scope, Object.assign( { ccm, evtpushExecutor: privateExecutor, }, securityOptions ) ); ManageFace.register( as, ccm, MANAGE_FACE, privateExecutor, securityOptions ); StatelessManageService.register( as, privateExecutor, scope, securityOptions ); StatelessManageFace.register( as, ccm, STLS_MANAGE_FACE, privateExecutor, securityOptions ); StatelessAuthService.register( as, publicExecutor, scope, securityOptions ); StatelessAuthFace.register( as, ccm, STLS_AUTH_FACE, publicExecutor, securityOptions ); MasterManageService.register( as, privateExecutor, scope, securityOptions ); MasterManageFace.register( as, ccm, MASTER_MANAGE_FACE, privateExecutor, securityOptions ); MasterAuthService.register( as, publicExecutor, scope, securityOptions ); MasterAuthFace.register( as, ccm, MASTER_AUTH_FACE, publicExecutor, securityOptions ); MasterAutoregService.register( as, publicExecutor, scope, securityOptions ); MasterAutoregFace.register( as, ccm, MASTER_AUTOREG_FACE, publicExecutor, securityOptions ); } ); as.add( ( as ) => { ccm.iface( MANAGE_FACE ).call( as, 'setup', config ); if ( storagePassword ) { ccm.iface( SVKEY_FACE ).unlock( as, Buffer.from( storagePassword, 'hex' ) ); } } ); } /** * CCM instance accessor * @returns {AdvancedCCM} instance */ ccm() { return this._ccm; } /** * Executor instance accessor * @returns {Executor} instance */ executor() { return this._public_executor; } /** * Shutdown of app and related instances * @param {callable} [done] - done callback */ close( done=null ) { if ( this._ccm ) { this._ccm.close(); this._ccm = null; this._public_executor.close( done ); } this._private_executor = null; this._public_executor = null; } } module.exports = ServiceApp;