You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CoreID/app/controllers/api/v1/Radius.controller.js

191 lines
6.3 KiB

const { Controller } = require('libflitter')
class RadiusController extends Controller {
static get services() {
return [...super.services, 'models', 'output']
}
async attempt(req, res, next) {
const User = this.models.get('auth:User')
const Client = this.models.get('radius:Client')
if ( !req.body.username || !req.body.password ) {
this.output.error('RADIUS error: missing username or password')
return this.fail(res)
}
const parts = String(req.body.username).split('@')
parts.reverse()
const clientId = parts.shift()
parts.reverse()
const username = parts.join('@')
const password = req.body.password
const user = await User.findOne({ uid: username, active: true })
if ( !user ) {
this.output.error(`RADIUS error: invalid username: ${username}`)
return this.fail(res)
}
const client = await Client.findById(clientId)
if ( !client || !client.active ) {
this.output.error(`RADIUS error: invalid client: ${clientId}`)
return this.fail(res)
}
// Check if the credentials are an app_password
const app_password_verified = Array.isArray(user.app_passwords)
&& user.app_passwords.length > 0
&& await user.check_app_password(password)
// Check if the user has MFA enabled.
// If so, split the incoming password to fetch the MFA code
// e.g. normalPassword:123456
if ( !app_password_verified && user.mfa_enabled ) {
const parts = password.split(':')
const mfa_code = parts.pop()
const actual_password = parts.join(':')
// Check the credentials
if ( !(await user.check_password(actual_password)) ) {
this.output.debug(`RADIUS error: user w/ MFA provided invalid credentials`)
return this.fail(res)
}
// Now, check the MFA code
if ( !user.mfa_token.verify(mfa_code) ) {
this.output.debug(`RADIUS error: user w/ MFA provided invalid MFA token`)
return this.fail(res)
}
// If not MFA, just check the credentials
} else if (!app_password_verified && !await user.check_password(password)) {
this.output.debug(`RADIUS error: user w/ simple auth provided invalid credentials`)
return this.fail(res)
}
// Check if the user has any login interrupt traps set
if ( user.trap ) {
this.output.error(`RADIUS error: user has trap: ${user.trap}`)
return this.fail(res)
}
// Apply the appropriate IAM policy if this SAML SP is associated with an App
// If the SAML service provider has no associated application, just allow it
const associated_app = await client.application()
if ( associated_app ) {
const Policy = this.models.get('iam:Policy')
const can_access = await Policy.check_user_access(user, associated_app.id)
if ( !can_access ) {
this.output.error(`RADIUS error: user denied IAM access`)
return this.fail(res)
}
}
this.output.info(`Authenticated RADIUS user: ${user.uid} to IAM ${associated_app.name}`)
return res.api({ success: true })
}
fail(res) {
return res.status(401).api({ success: false })
}
async get_clients(req, res, next) {
const Client = this.models.get('radius:Client')
const clients = await Client.find({ active: true })
const data = []
for ( const client of clients ) {
if ( req.user.can(`radius:client:${client.id}:view`) ) {
data.push(await client.to_api())
}
}
return res.api(data)
}
async get_client(req, res, next) {
const Client = this.models.get('radius:Client')
const client = await Client.findById(req.params.id)
if ( !client || !client.active )
return res.status(404)
.message(req.T('api.client_not_found'))
.api()
if ( !req.user.can(`radius:client:${client.id}:view`) )
return res.status(401)
.message(req.T('api.insufficient_permissions'))
.api()
return res.api(await client.to_api())
}
async create_client(req, res, next) {
if ( !req.user.can('radius:client:create') )
return res.status(401)
.message(req.T('api.insufficient_permissions'))
.api()
if ( !req.body.name )
return res.status(400)
.message(`${req.T('api.missing_field')} name`)
.api()
const Client = this.models.get('radius:Client')
const client = new Client({
name: req.body.name,
})
await client.save()
return res.api(await client.to_api())
}
async update_client(req, res, next) {
const Client = this.models.get('radius:Client')
const client = await Client.findById(req.params.id)
if ( !client || !client.active )
return res.status(404)
.message(req.T('api.client_not_found'))
.api()
if ( !req.user.can(`radius:client:${client.id}:update`) )
return res.status(401)
.message(req.T('api.insufficient_permissions'))
.api()
if ( !req.body.name )
return res.status(400)
.message(`${req.T('api.missing_field')} name`)
.api()
client.name = req.body.name
await client.save()
return res.api()
}
async delete_client(req, res, next) {
const Client = this.models.get('radius:Client')
const client = await Client.findById(req.params.id)
if ( !client || !client.active )
return res.status(404)
.message(req.T('api.client_not_found'))
.api()
if ( !req.user.can(`radius:client:${client.id}:delete`) )
return res.status(401)
.message(req.T('api.insufficient_permissions'))
.api()
client.active = false
await client.save()
return res.api()
}
}
module.exports = exports = RadiusController