@futoin/security
Version:
FutoIn Security Concept reference implementation
161 lines (134 loc) • 4.98 kB
JavaScript
;
/**
* @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 BaseService = require( './lib/BaseService' );
const StatelessAuthFace = require( './StatelessAuthFace' );
const secutil = require( './lib/util' );
const Errors = require( 'futoin-asyncsteps/Errors' );
const {
SVKEY_FACE,
SVDATA_FACE,
} = require( './lib/main' );
const empty_buf = Buffer.from( '' );
/**
* Manage Service
*/
class StatelessAuthService extends BaseService {
static get IFACE_IMPL() {
return StatelessAuthFace;
}
_keyName( reqinfo, user, for_mac ) {
const reqinfo_info = reqinfo.info;
const service = ( reqinfo_info.SECURITY_LEVEL === 'System' )
? this._scope.system_local_id
: reqinfo().userInfo().localID();
const type = for_mac ? 'STLSMAC' : 'STLSPWD';
return `${user}:${service}:${type}`;
}
_checkCommon( as, { reqinfo, for_mac, base, source, user, hash, sigbuf } ) {
const ccm = reqinfo.ccm();
secutil.checkUser( as, ccm, user, source );
as.add(
( as, user_info ) => {
const svkeys = ccm.iface( SVKEY_FACE );
svkeys.extKeyInfo( as, this._keyName( reqinfo, user, for_mac ) );
as.add( ( as, key_info ) => {
const svdata = ccm.iface( SVDATA_FACE );
svdata.verify( as, key_info.id, base, sigbuf, hash );
as.successStep( {
local_id : user_info.local_id,
global_id : user_info.global_id,
} );
} );
},
( as, err ) => {
if ( err === 'InvalidSignature' ) {
as.error( Errors.SecurityError, `Invalid user or password: ${user}` );
}
}
);
}
checkClear( as, reqinfo ) {
if ( !this._scope.config.clear_auth ) {
as.error( Errors.SecurityError, 'Clear text auth is disabled' );
}
const { source, sec : { user, secret } } = reqinfo.params();
this._checkCommon( as, {
reqinfo,
for_mac: false,
base: empty_buf,
source,
user,
hash: 'NA',
sigbuf: Buffer.from( secret, 'utf8' ),
} );
}
checkMAC( as, reqinfo ) {
if ( !this._scope.config.mac_auth ) {
as.error( Errors.SecurityError, 'Stateless MAC auth is disabled' );
}
const { base, source, sec : { user, algo, sig } } = reqinfo.params();
const { hash } = secutil.parseMACAlgo( as, algo );
this._checkCommon( as, {
reqinfo,
for_mac: true,
base,
source,
user,
hash,
sigbuf: Buffer.from( sig, 'base64' ),
} );
}
genMAC( as, reqinfo ) {
const { base, reqsec : { user, algo } } = reqinfo.params();
const { hash } = secutil.parseMACAlgo( as, algo );
const ccm = reqinfo.ccm();
const svkeys = ccm.iface( SVKEY_FACE );
svkeys.extKeyInfo( as, this._keyName( reqinfo, user, true ) );
as.add( ( as, key_info ) => {
const svdata = ccm.iface( SVDATA_FACE );
svdata.sign( as, key_info.id, base, hash );
as.add( ( as, sig ) => {
reqinfo.result( sig.toString( 'base64' ) );
} );
} );
}
getMACSecret( as, reqinfo ) {
const { user } = reqinfo.params();
const ccm = reqinfo.ccm();
const svkeys = ccm.iface( SVKEY_FACE );
svkeys.extKeyInfo( as, this._keyName( reqinfo, user, true ) );
as.add( ( as, key_info ) => {
svkeys.exposeKey( as, key_info.id );
as.add( ( as, sig ) => {
reqinfo.result( sig.toString( 'base64' ) );
} );
} );
}
/**
* Register futoin.auth.stateless interface with Executor
* @alias StatelessAuthService.register
* @param {AsyncSteps} as - steps interface
* @param {Executor} executor - executor instance
* @param {object} options - implementation defined options
* @param {Executor} options.scope=main.globalScope
* @returns {StatelessAuthService} instance
*/
}
module.exports = StatelessAuthService;