/**
* @module flitter-auth/AuthUnit
*/
const Unit = require('libflitter/Unit')
const ncp = require('ncp')
const path = require('path')
const oauth2 = require('node-oauth2-server')
const flapper = require('flitter-flap/FlapHelper')(false)
const FlitterProvider = require('./flitter/FlitterProvider')
const LdapProvider = require('./ldap/LdapProvider')
const Oauth2Provider = require('./oauth2/Oauth2Provider')
const Oauth2BearerToken = require('./model/Oauth2BearerToken')
const Oauth2Client = require('./model/Oauth2Client')
const Oauth2AuthorizationTicket = require('./model/Oauth2AuthorizationTicket')
const SecurityContext = require('./SecurityContext')
/**
* Registers functionality provided by flitter-auth.
* @extends module:libflitter/Unit~Unit
*/
class AuthUnit extends Unit {
/**
* Defines the services required by this unit.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'configs', 'canon', 'models', 'output']
}
/**
* Get the name of the service this unit represents - 'auth'
* @returns {string} - 'auth'
*/
static get name() {
return 'auth'
}
/**
* Instantiate the unit.
*/
constructor() {
super()
/**
* Mapping of provider names to instances of the {module:flitter-auth/Provider~Provider} classes.
* @type {object}
*/
this.providers = {}
/**
* Mapping of provider class names to static provider classes.
* @type {object}
*/
this.provider_classes = {
FlitterProvider,
LdapProvider,
Oauth2Provider,
}
}
/**
* Initialize the unit:
* - Registers the auth and authProvider canonical resources
* - For all configured auth sources, create a new instance of the applicable provider
* and register it with this service.
*
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
* @returns {Promise<void>}
*/
async go(app){
// Register the providers resolver as a canonical resource
this.canon.register_resource('auth', this.get_provider_instance.bind(this))
this.canon.register_resource('authProvider', this.resolve_provider.bind(this))
this.app.di().inject(SecurityContext)
// Initialize Oauth2 Support
// await this.init_oauth()
if ( this.configs.get('auth') ) {
const sources = this.configs.get('auth.sources')
for ( const name in sources ) {
if ( !sources.hasOwnProperty(name) ) continue
const source_config = sources[name]
if ( source_config.enable ) {
source_config.name = name
const ProviderClass = this.resolve_provider(source_config.type)
if ( !ProviderClass ) this.output.error(`Unknown auth provider type: ${source_config.type}`)
else this.output.info(`Registered auth provider: ${name} (${source_config.type})`)
this.providers[name] = new ProviderClass(app, source_config)
}
}
}
await this.init_oauth()
}
/**
* Get the auth source instance with the specified name.
* @param {string} name
* @returns {module:flitter-auth/Provider~Provider}
*/
get_provider_instance(name) {
return this.providers[name]
}
/**
* Get the auth provider class definition with the specified name.
* @param {string} name
* @returns {module:flitter-auth/Provider~Provider} - the static CLASS reference
*/
resolve_provider(name) {
const value = this.provider_classes[name]
const di = this.app.di()
if ( di.__is_injectable(value) ) return di.inject(value)
return value
}
/**
* Deploy the resources provided by flitter-auth. That is, the controllers/views/routes/models/middleware/etc.
* @returns {Promise<void>}
*/
async deploy() {
const package_dir = __dirname
// const base_dir = path.dirname(require.main.filename)
this.output.info("Auth deploy from: " + package_dir)
this.output.info("To: " + path.dirname(require.main.filename))
function do_copy(from, to) {
return new Promise(
(resolve, reject) => {
ncp(from, to, (error) => {
if (error) reject(error)
resolve()
})
}
)
}
// controllers
this.output.message('Deploying controllers...')
await do_copy(path.resolve(package_dir + '/deploy/controllers'), this.app.directories.controllers)
// models
this.output.message('Deploying models...')
await do_copy(path.resolve(package_dir + '/deploy/models'), this.app.directories.models)
// routing - middleware
this.output.message('Deploying middleware...')
await do_copy(path.resolve(package_dir + "/deploy/routing/middleware"), path.resolve(this.app.directories.middlewares, 'auth'))
// routing - routers
this.output.message('Deploying routers...')
await do_copy(path.resolve(package_dir + "/deploy/routing/routers"), this.app.directories.routers)
// views
this.output.message('Deploying views...')
await do_copy(path.resolve(package_dir + "/deploy/views/auth"), path.resolve(this.app.directories.views, 'auth'))
// config
this.output.message('Deploying config...')
await do_copy(path.resolve(package_dir + "/deploy/config"), this.app.directories.configs)
// assets
this.output.message('Deploying assets...')
await do_copy(path.resolve(package_dir + "/deploy/assets"), path.resolve(this.app.directories.assets, 'auth'))
// add global middleware
this.output.message('Adding global middleware...')
await flapper.insert_line(this.app.directories.global_middleware, /\s*=\s*\[/, ' "auth:Utility",')
}
/**
* Get the fully-qualified path to the migrations provided by this unit.
* @returns {string}
*/
migrations(){
return path.resolve(__dirname, 'migrations')
}
/**
* Clean up the resources managed by this unit. Calls the 'cleanup' method
* on all registered providers in this service.
* @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
* @returns {Promise<void>}
*/
async cleanup(app){
for ( let key in this.providers ){
if ( !this.providers.hasOwnProperty(key) ) continue
await this.providers[key].cleanup(app)
}
}
/**
* Get the provider configured with the specified name. If
* no name is provided, the default provider will be returned.
* @param {string} [name]
* @returns {module:flitter-auth/Provider~Provider}
*/
get_provider(name){
if ( !name ) {
name = this.configs.get('auth.default_provider')
}
return this.providers[name]
}
/**
* Initializes the OAuth2 server's resources.
* @returns {Promise<void>}
*/
async init_oauth() {
let config = this.configs.get('auth')
if ( !config ) return
else config = config.servers.oauth2
this.models.external_model(this, 'Oauth2BearerToken', Oauth2BearerToken)
this.models.external_model(this, 'Oauth2Client', Oauth2Client)
this.models.external_model(this, 'Oauth2AuthorizationTicket', Oauth2AuthorizationTicket)
this.app.express.oauth2 = oauth2({
model: this.models.get('auth::Oauth2BearerToken'),
grants: config.grants,
debug: true,
})
this.app.express.use(this.app.express.oauth2.errorHandler())
}
}
module.exports = exports = AuthUnit