garrettmills
d6e4ea2e56
All checks were successful
continuous-integration/drone/push Build is passing
617 lines
20 KiB
JavaScript
617 lines
20 KiB
JavaScript
const { Controller } = require('libflitter')
|
|
const zxcvbn = require('zxcvbn')
|
|
|
|
class LDAPController extends Controller {
|
|
static get services() {
|
|
return [...super.services, 'models', 'utility', 'configs']
|
|
}
|
|
|
|
async get_config(req, res, next) {
|
|
// ldap port
|
|
// user base dn
|
|
// group base dn
|
|
const config = this.configs.get('ldap:server')
|
|
return res.api({
|
|
port: config.port,
|
|
base_dc: config.schema.base_dc,
|
|
authentication_base: config.schema.authentication_base,
|
|
group_base: config.schema.group_base,
|
|
login_field: config.schema.auth.user_id,
|
|
})
|
|
}
|
|
|
|
async get_clients(req, res, next) {
|
|
const Client = this.models.get('ldap:Client')
|
|
const clients = await Client.find({active: true})
|
|
const data = []
|
|
|
|
for ( const client of clients ) {
|
|
if ( !req.user.can(`ldap:client:${client.id}:view`) ) continue
|
|
data.push(await client.to_api())
|
|
}
|
|
|
|
return res.api(data)
|
|
}
|
|
|
|
async get_groups(req, res, next) {
|
|
const Group = this.models.get('ldap:Group')
|
|
const groups = await Group.find({active: true})
|
|
const data = []
|
|
|
|
for ( const group of groups ) {
|
|
if ( !req.user.can(`ldap:group:${group.id}:view`) ) continue
|
|
data.push(await group.to_api())
|
|
}
|
|
|
|
return res.api(data)
|
|
}
|
|
|
|
async get_machines(req, res, next) {
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const machines = await Machine.find({active: true})
|
|
const data = []
|
|
|
|
for ( const machine of machines ) {
|
|
if ( !req.user.can(`ldap:machine:${machine.id}:view`) ) continue
|
|
data.push(await machine.to_api())
|
|
}
|
|
|
|
return res.api(data)
|
|
}
|
|
|
|
async get_machine_groups(req, res, next) {
|
|
const MachineGroup = this.models.get('ldap:MachineGroup')
|
|
const groups = await MachineGroup.find({active: true})
|
|
const data = []
|
|
|
|
for ( const group of groups ) {
|
|
if ( !req.user.can(`ldap:machine_group:${group.id}:view`) ) continue
|
|
data.push(await group.to_api())
|
|
}
|
|
|
|
return res.api(data)
|
|
}
|
|
|
|
async get_client(req, res, next) {
|
|
const Client = this.models.get('ldap: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(`ldap:client:${client.id}:view`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
return res.api(await client.to_api())
|
|
}
|
|
|
|
async get_group(req, res, next) {
|
|
const Group = this.models.get('ldap:Group')
|
|
const group = await Group.findById(req.params.id)
|
|
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:group:${group.id}:view`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
return res.api(await group.to_api())
|
|
}
|
|
|
|
async get_machine(req, res, next) {
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const machine = await Machine.findById(req.params.id)
|
|
|
|
if ( !machine || !machine.active )
|
|
return res.status(404)
|
|
.message(req.T('api.machine_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine:${machine.id}:view`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
return res.api(await machine.to_api())
|
|
}
|
|
|
|
async get_machine_group(req, res, next) {
|
|
const MachineGroup = this.models.get('ldap:MachineGroup')
|
|
const group = await MachineGroup.findById(req.params.id)
|
|
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine_group:${group.id}:view`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
return res.api(await group.to_api())
|
|
}
|
|
|
|
async create_client(req, res, next) {
|
|
if ( !req.user.can('ldap:client:create') )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
// validate inputs
|
|
const required_fields = ['name', 'uid', 'password']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
}
|
|
|
|
// Make sure the uid is free
|
|
const User = this.models.get('auth:User')
|
|
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
|
if ( existing_user )
|
|
return res.status(400)
|
|
.message(req.T('api.user_already_exists'))
|
|
.api()
|
|
|
|
// Verify password complexity
|
|
const min_score = 3
|
|
const result = zxcvbn(req.body.password)
|
|
if ( result.score < min_score )
|
|
return res.status(400)
|
|
.message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score))
|
|
.api()
|
|
|
|
// Create the client
|
|
const Client = this.models.get('ldap:Client')
|
|
const client = await Client.create({
|
|
uid: req.body.uid.toLowerCase(),
|
|
password: req.body.password,
|
|
name: req.body.name,
|
|
})
|
|
|
|
return res.api(await client.to_api())
|
|
}
|
|
|
|
async create_machine(req, res, next) {
|
|
// validate inputs
|
|
const required_fields = ['name', 'description']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the machine name is free
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const existing_machine = await Machine.findOne({ name: req.body.name })
|
|
if ( existing_machine )
|
|
return res.status(400)
|
|
.message(req.T('api.machine_already_exists'))
|
|
.api()
|
|
|
|
const machine = new Machine({
|
|
name: req.body.name,
|
|
description: req.body.description,
|
|
host_name: req.body.host_name,
|
|
location: req.body.location,
|
|
})
|
|
|
|
if ( req.body.bind_password ) {
|
|
await machine.set_bind_password(req.body.bind_password)
|
|
}
|
|
|
|
if ( 'ldap_visible' in req.body ) {
|
|
machine.ldap_visible = !!req.body.ldap_visible
|
|
}
|
|
|
|
await machine.save()
|
|
return res.api(await machine.to_api())
|
|
}
|
|
|
|
async create_machine_group(req, res, next) {
|
|
// validate inputs
|
|
const required_fields = ['name']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the machine name is free
|
|
const MachineGroup = this.models.get('ldap:MachineGroup')
|
|
const existing_group = await MachineGroup.findOne({ name: req.body.name })
|
|
if ( existing_group )
|
|
return res.status(400)
|
|
.message(req.T('api.group_already_exists'))
|
|
.api()
|
|
|
|
const group = new MachineGroup({
|
|
name: req.body.name,
|
|
description: req.body.description,
|
|
})
|
|
|
|
if ( 'ldap_visible' in req.body ) {
|
|
group.ldap_visible = !!req.body.ldap_visible
|
|
}
|
|
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const machine_ids = Array.isArray(req.body.machine_ids) ? req.body.machine_ids : []
|
|
group.machine_ids = []
|
|
for ( const potential of machine_ids ) {
|
|
const machine = await Machine.findOne({
|
|
_id: Machine.to_object_id(potential),
|
|
active: true,
|
|
})
|
|
|
|
if ( machine ) {
|
|
group.machine_ids.push(potential)
|
|
}
|
|
}
|
|
|
|
await group.save()
|
|
return res.api(await group.to_api())
|
|
}
|
|
|
|
async create_group(req, res, next) {
|
|
// validate inputs
|
|
const required_fields = ['role', 'name']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the group name is free
|
|
const Group = this.models.get('ldap:Group')
|
|
const User = this.models.get('auth:User')
|
|
const existing_group = await Group.findOne({ name: req.body.name })
|
|
if ( existing_group )
|
|
return res.status(400)
|
|
.message(req.T('api.group_already_exists'))
|
|
.api()
|
|
|
|
// Make sure the role exists
|
|
if ( !this.configs.get('auth.roles')[req.body.role] )
|
|
return res.status(400)
|
|
.message(`${req.T('common.invalid')} role.`)
|
|
.api()
|
|
|
|
const group = new Group({
|
|
name: req.body.name,
|
|
role: req.body.role,
|
|
})
|
|
|
|
if ( 'ldap_visible' in req.body ) group.ldap_visible = !!req.body.ldap_visible
|
|
if ( 'user_ids' in req.body ) {
|
|
// Attempt to parse the user_ids
|
|
const parsed = typeof req.body.user_ids === 'string' ? this.utility.infer(req.body.user_ids) : req.body.user_ids
|
|
const user_ids = Array.isArray(parsed) ? parsed : [parsed]
|
|
|
|
// Make sure all the user IDs are valid
|
|
for ( const user_id of user_ids ) {
|
|
const user = await User.findById(user_id)
|
|
if ( !user )
|
|
return res.status(400)
|
|
.message(`${req.T('common.invalid')} user_id: ${user_id}`)
|
|
.api()
|
|
}
|
|
|
|
group.user_ids = user_ids
|
|
}
|
|
|
|
await group.save()
|
|
return res.api(await group.to_api())
|
|
}
|
|
|
|
async update_client(req, res, next) {
|
|
const Client = this.models.get('ldap: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(`ldap:client:${client.id}:update`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
const required_fields = ['name', 'uid']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
const user = await client.user()
|
|
|
|
// Update the name
|
|
if ( req.body.name !== client.name ) {
|
|
client.name = req.body.name
|
|
user.first_name = req.body.name
|
|
}
|
|
|
|
// Update the uid
|
|
if ( req.body.uid.toLowerCase() !== user.uid ) {
|
|
// Make sure the UID is free
|
|
const User = this.models.get('auth:User')
|
|
const existing_user = await User.findOne({ uid: req.body.uid.toLowerCase() })
|
|
if ( existing_user )
|
|
return res.status(400)
|
|
.message(req.T('api.user_already_exists'))
|
|
.api()
|
|
|
|
user.uid = req.body.uid.toLowerCase()
|
|
}
|
|
|
|
// Update the password
|
|
if ( req.body.password && !(await user.check_password(req.body.password)) ) {
|
|
// Verify the password's complexity
|
|
const min_score = 3
|
|
const result = zxcvbn(req.body.password)
|
|
if ( result.score < min_score )
|
|
return res.status(400)
|
|
.message(req.T('auth.password_complexity_fail').replace('MIN_SCORE', min_score))
|
|
.api()
|
|
|
|
await user.reset_password(req.body.password)
|
|
}
|
|
|
|
await user.save()
|
|
await client.save()
|
|
return res.api()
|
|
}
|
|
|
|
async update_machine(req, res, next) {
|
|
const Machine = this.models.get('ldap:Machine')
|
|
|
|
const machine = await Machine.findById(req.params.id)
|
|
if ( !machine || !machine.active )
|
|
return res.status(404)
|
|
.message(req.T('api.machine_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine:${machine.id}:update`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
const required_fields = ['name', 'description']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the machine name is free
|
|
const existing_machine = await Machine.findOne({ name: req.body.name })
|
|
if ( existing_machine && existing_machine.id !== machine.id )
|
|
return res.status(400)
|
|
.message(req.T('api.machine_already_exists'))
|
|
.api()
|
|
|
|
machine.name = req.body.name
|
|
machine.description = req.body.description
|
|
machine.host_name = req.body.host_name
|
|
machine.location = req.body.location
|
|
|
|
if ( req.body.bind_password ) {
|
|
await machine.set_bind_password(req.body.bind_password)
|
|
}
|
|
|
|
if ( 'ldap_visible' in req.body ) {
|
|
machine.ldap_visible = !!req.body.ldap_visible
|
|
}
|
|
|
|
await machine.save()
|
|
return res.api(await machine.to_api())
|
|
}
|
|
|
|
async update_machine_group(req, res, next) {
|
|
const MachineGroup = this.models.get('ldap:MachineGroup')
|
|
|
|
const group = await MachineGroup.findById(req.params.id)
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine_group:${group.id}:update`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
const required_fields = ['name']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the machine name is free
|
|
const existing_group = await MachineGroup.findOne({ name: req.body.name })
|
|
if ( existing_group && existing_group.id !== group.id )
|
|
return res.status(400)
|
|
.message(req.T('api.group_already_exists'))
|
|
.api()
|
|
|
|
group.name = req.body.name
|
|
group.description = req.body.description
|
|
|
|
if ( 'ldap_visible' in req.body ) {
|
|
group.ldap_visible = !!req.body.ldap_visible
|
|
}
|
|
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const machine_ids = Array.isArray(req.body.machine_ids) ? req.body.machine_ids : []
|
|
group.machine_ids = []
|
|
for ( const potential of machine_ids ) {
|
|
const machine = await Machine.findOne({
|
|
_id: Machine.to_object_id(potential),
|
|
active: true,
|
|
})
|
|
|
|
if ( machine ) {
|
|
group.machine_ids.push(potential)
|
|
}
|
|
}
|
|
|
|
await group.save()
|
|
return res.api(await group.to_api())
|
|
}
|
|
|
|
async update_group(req, res, next) {
|
|
const User = await this.models.get('auth:User')
|
|
const Group = await this.models.get('ldap:Group')
|
|
|
|
const group = await Group.findById(req.params.id)
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:group:${group.id}:update`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
const required_fields = ['role', 'name']
|
|
for ( const field of required_fields ) {
|
|
if ( !req.body[field] )
|
|
return res.status(400)
|
|
.message(`${req.T('api.missing_field')} ${field}`)
|
|
.api()
|
|
}
|
|
|
|
// Make sure the name is free
|
|
const existing_group = await Group.findOne({ name: req.body.name })
|
|
if ( existing_group && existing_group.id !== group.id )
|
|
return res.status(400)
|
|
.message(req.T('api.group_already_exists'))
|
|
.api()
|
|
|
|
group.name = req.body.name
|
|
group.role = req.body.name
|
|
group.ldap_visible = !!req.body.ldap_visible
|
|
|
|
if ( req.body.user_ids ) {
|
|
const parsed = typeof req.body.user_ids === 'string' ? this.utility.infer(req.body.user_ids) : req.body.user_ids
|
|
const user_ids = Array.isArray(parsed) ? parsed : [parsed]
|
|
|
|
for ( const user_id of user_ids ) {
|
|
const user = await User.findById(user_id)
|
|
if ( !user )
|
|
return res.status(400)
|
|
.message(`${req.T('common.invalid')} user_id: ${user_id}`)
|
|
.api()
|
|
}
|
|
|
|
group.user_ids = user_ids
|
|
} else {
|
|
group.user_ids = []
|
|
}
|
|
|
|
await group.save()
|
|
return res.api()
|
|
}
|
|
|
|
async delete_client(req, res, next) {
|
|
const Client = this.models.get('ldap: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(`ldap:client:${client.id}:delete`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
const user = await client.user()
|
|
client.active = false
|
|
user.active = false
|
|
user.block_login = true
|
|
|
|
await user.save()
|
|
await client.save()
|
|
return res.api()
|
|
}
|
|
|
|
async delete_group(req, res, next) {
|
|
const Group = this.models.get('ldap:Group')
|
|
const group = await Group.findById(req.params.id)
|
|
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:group:${group.id}:delete`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
group.active = false
|
|
await group.save()
|
|
return res.api()
|
|
}
|
|
|
|
async delete_machine(req, res, next) {
|
|
const Machine = this.models.get('ldap:Machine')
|
|
const machine = await Machine.findById(req.params.id)
|
|
|
|
if ( !machine || !machine.active )
|
|
return res.status(404)
|
|
.message(req.T('api.machine_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine:${machine.id}:delete`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
machine.active = false
|
|
await machine.save()
|
|
return res.api()
|
|
}
|
|
|
|
async delete_machine_group(req, res, next) {
|
|
const MachineGroup = this.models.get('ldap:MachineGroup')
|
|
const group = await MachineGroup.findById(req.params.id)
|
|
|
|
if ( !group || !group.active )
|
|
return res.status(404)
|
|
.message(req.T('api.group_not_found'))
|
|
.api()
|
|
|
|
if ( !req.user.can(`ldap:machine_group:${group.id}:delete`) )
|
|
return res.status(401)
|
|
.message(req.T('api.insufficient_permissions'))
|
|
.api()
|
|
|
|
group.active = false
|
|
await group.save()
|
|
return res.api()
|
|
}
|
|
}
|
|
|
|
module.exports = exports = LDAPController
|