const { Controller } = require('libflitter') const zxcvbn = require('zxcvbn') class LDAPController extends Controller { static get services() { return [...super.services, 'models', 'utility'] } 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_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('No client found with that ID.') .api() if ( !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(401) .message('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('No group found with that ID.') .api() if ( !req.user.can(`ldap:group:${group.id}:view`) ) return res.status(401) .message('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('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(`Missing required 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 }) if ( existing_user ) return res.status(400) .message('A user with that uid 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(`Password does not meet the minimum complexity score of ${min_score}.`) .api() // Create the client const Client = this.models.get('ldap:Client') const client = await Client.create({ uid: req.body.uid, password: req.body.password, name: req.body.name, }) return res.api(await client.to_api()) } async create_group(req, res, next) { console.log(req.body) if ( !req.user.can(`ldap:group:create`) ) return res.status(401) .message('Insufficient permissions.') .api() // validate inputs const required_fields = ['role', 'name'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) .message(`Missing required 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('A group already exists with that name.') .api() // Make sure the role exists if ( !this.configs.get('auth.roles')[req.body.role] ) return res.status(400) .message('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(`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('No client found with that ID.') .api() if ( !req.user.can(`ldap:client:${client.id}:update`) ) return res.status(401) .message('Insufficient permissions.') .api() const required_fields = ['name', 'uid'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) .message(`Missing required 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 !== 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 }) if ( existing_user ) return res.status(400) .message('A user already exists with that uid.') .api() user.uid = req.body.uid } // 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(`Password does not meet the minimum complexity score of ${min_score}.`) .api() await user.reset_password(req.body.password) } await user.save() await client.save() return res.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('No group found with that ID.') .api() if ( !req.user.can(`ldap:group:${group.id}:update`) ) return res.status(401) .message('Insufficient permissions.') .api() const required_fields = ['role', 'name'] for ( const field of required_fields ) { if ( !req.body[field] ) return res.status(400) .message(`Missing required 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('A group with that name 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(`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('Client not found with that ID.') .api() if ( !req.user.can(`ldap:client:${client.id}:delete`) ) return res.status(401) .message('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('No group found with that ID.') .api() if ( !req.user.can(`ldap:group:${group.id}:delete`) ) return res.status(401) .message('Insufficient permissions.') .api() group.active = false await group.save() return res.api() } } module.exports = exports = LDAPController