/**
* @module flitter-auth/SecurityContext
*/
const { Injectable } = require('flitter-di')
/**
* Request-specific security context that provides helper functions
* with regard to security checks for the relevant request.
* @extends module:flitter-di/src/Injectable~Injectable
*/
class SecurityContext extends Injectable {
/**
* Defines the services required by this unit.
* @returns {Array<string>}
*/
static get services() {
return [...super.services, 'configs', 'auth', 'models']
}
/**
* The relevant request.
* @type {express/request}
*/
#request
/**
* The relevant response.
* @type {express/response}
*/
#response
/**
* Instantiate the security context.
* @param {express/request} req - the relevant request
* @param {express/response} res - the relevant response
*/
constructor(req, res) {
super()
this.#request = req
this.#response = res
}
/**
* Deny the client access to the requested resource.
* Displays the 401 error page and passes along the specified message.
* @param {string} [message = 'Access Denied']
*/
deny(message = 'Access Denied') {
this.#response.error(401, {message})
}
/**
* Deny the client access to the requested resource.
* Displays the 401 error page and passes along the specified message.
* If the request has a user in the session, the user will be forcibly signed out.
* @param {string} [message = 'Access Denied']
*/
kickout(message = 'Access Denied') {
if ( this.#request.user ) {
this.provider().logout(this.#request).then(() => {
this.deny(message)
})
} else {
this.deny(message)
}
}
/**
* Deny the client access to the requested resource.
* Displays the 401 error page and passes along the specified message.
* If the request has a user in the session, the user's block_login flag will
* be set, and they will be forcibly signed out.
*
* WARNING: this flag will prevent the user from signing into the application AT ALL.
* @param {string} [message = 'Access Denied']
*/
ban(message = 'Access Denied') {
if ( this.#request.user ) {
this.#request.user.block_login = true
this.#request.user.save().then(() => {
this.kickout(message)
})
} else {
this.deny(message)
}
}
/**
* Get the name of the auth provider for the request.
* If the request is authenticated, use the user's provider.
* Otherwise, if a provider exists in the route params, use that.
* Otherwise, use the default_provider specified in the config.
* @returns {string}
*/
provider_name() {
let provider_name = this.configs.get('auth.default_provider')
if ( this.#request.is_auth ) provider_name = this.#request.user.provider
else provider_name = this.#request.params.provider ? this.#request.params.provider : provider_name
return provider_name
}
/**
* Get the auth provider for the request.
* @returns {module:flitter-auth/Provider~Provider}
*/
provider() {
return this.auth.get_provider(this.provider_name())
}
/**
* Generate a key action that will resolve to the specified handler.
*
* @example
* const action = await request.security.key_action('controller::Home.password_reset')
* return res.send(`Reset your password at: ${action.url()}`)
* @param {string} handler - canonical name of the handler - e.g. "controller::Home.welcome"
* @returns {Promise<module:flitter-auth/model/KeyAction~KeyAction>}
*/
async key_action(handler) {
const KeyAction = this.models.get('auth:KeyAction')
const ka_data = { handler, used: false }
if ( this.#request.user ) ka_data.user_id = this.#request.user._id
const ka = new KeyAction(ka_data)
await ka.save()
return ka
}
}
module.exports = exports = SecurityContext