const { Controller } = require('libflitter') class AppController extends Controller { static get services() { return [...super.services, 'models', 'utility'] } async get_applications(req, res, next) { const Application = this.models.get('Application') const applications = await Application.find({ active: true }) const data = [] for ( const app of applications ) { if ( req.user.can(`app:${app.id}:view`) ) { data.push(await app.to_api()) } } return res.api(data) } async get_application(req, res, next) { const Application = this.models.get('Application') const application = await Application.findById(req.params.id) if ( !application || !application.active ) return res.status(404) .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:view`) ) return res.status(401) .message(req.T('api.insufficient_permissions')) .api() return res.api(await application.to_api()) } async create_application(req, res, next) { const Application = this.models.get('Application') if ( !req.user.can('app:create') ) return res.status(401) .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'identifier'] 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 identifier is properly formatted if ( !(new RegExp('^[a-zA-Z0-9_]*$')).test(req.body.identifier) ) return res.status(400) .message(`${req.T('api.improper_field')} identifier ${req.T('api.alphanum_underscores')}`) .api() // Make sure the identifier is unique const existing_app = await Application.findOne({ identifier: req.body.identifier }) if ( existing_app ) return res.status(400) .message(req.T('api.application_already_exists')) .api() const application = new Application({ name: req.body.name, identifier: req.body.identifier, description: req.body.description, }) // Verify LDAP client IDs const LDAPClient = this.models.get('ldap:Client') if ( req.body.ldap_client_ids ) { const parsed = typeof req.body.ldap_client_ids === 'string' ? this.utility.infer(req.body.ldap_client_ids) : req.body.ldap_client_ids const ldap_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of ldap_client_ids ) { const client = await LDAPClient.findById(id) if ( !client || !client.active || !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_ldap_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ ldap_client_ids: client.id }) if ( other_assoc_app ) return res.status(400) // TODO translate this .message(`The LDAP client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.ldap_client_ids = ldap_client_ids } // Verify OAuth client IDs const OAuthClient = this.models.get('oauth:Client') if ( req.body.oauth_client_ids ) { const parsed = typeof req.body.oauth_client_ids === 'string' ? this.utility.infer(req.body.oauth_client_ids) : req.body.oauth_client_ids const oauth_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of oauth_client_ids ) { const client = await OAuthClient.findById(id) if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ oauth_client_ids: client.id }) if ( other_assoc_app ) return res.status(400) // TODO translate this .message(`The OAuth2 client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.oauth_client_ids = oauth_client_ids } // Verify RADIUS client IDs const RadiusClient = this.models.get('radius:Client') if ( req.body.radius_client_ids ) { const parsed = typeof req.body.radius_client_ids === 'string' ? this.utility.infer(req.body.radius_client_ids) : req.body.radius_client_ids const radius_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of radius_client_ids ) { const client = await RadiusClient.findById(id) if ( !client || !client.active || !req.user.can(`radius:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_radius_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ radius_client_ids: client.id }) if ( other_assoc_app ) return res.status(400) // TODO translate this .message(`The RADIUS client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.radius_client_ids = radius_client_ids } // Verify OpenID client IDs const OpenIDClient = this.models.get('openid:Client') if ( req.body.openid_client_ids ) { const parsed = typeof req.body.openid_client_ids === 'string' ? this.utility.infer(req.body.openid_client_ids) : req.body.openid_client_ids const openid_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of openid_client_ids ) { const client = await OpenIDClient.findById(id) if ( !client ) return res.status(400) .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ openid_client_ids: client.id }) if ( other_assoc_app ) return res.status(400) // TODO translate this .message(`The OpenID Connect client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.openid_client_ids = openid_client_ids } // Verify SAML service provider IDs const ServiceProvider = this.models.get('saml:ServiceProvider') if ( req.body.saml_service_provider_ids ) { const parsed = typeof req.body.saml_service_provider_ids === 'string' ? this.utility.infer(req.body.saml_service_provider_ids) : req.body.saml_service_provider_ids const saml_service_provider_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of saml_service_provider_ids ) { const provider = await ServiceProvider.findById(id) if ( !provider || !provider.active || !req.user.can(`saml:provider:${provider.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_saml_service_provider_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ saml_service_provider_ids: provider.id }) if ( other_assoc_app ) return res.status(400) // TODO translate this .message(`The SAML service provider ${provider.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.saml_service_provider_ids = saml_service_provider_ids } await application.save() return res.api(await application.to_api()) } async update_application(req, res, next) { const Application = this.models.get('Application') const application = await Application.findById(req.params.id) if ( !application || !application.active ) return res.status(404) .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:update`) ) return res.status(401) .message(req.T('api.insufficient_permissions')) .api() const required_fields = ['name', 'identifier'] 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 identifier is properly formatted if ( !(new RegExp('^[a-zA-Z0-9_]*$')).test(req.body.identifier) ) return res.status(400) .message(`${req.T('api.improper_field')} identifier ${req.T('api.alphanum_underscores')}`) .api() // Make sure the identifier is unique const existing_app = await Application.findOne({ identifier: req.body.identifier }) if ( existing_app && existing_app.id !== application.id ) return res.status(400) .message(req.T('api.application_already_exists')) .api() // Verify LDAP client IDs const LDAPClient = this.models.get('ldap:Client') if ( req.body.ldap_client_ids ) { const parsed = typeof req.body.ldap_client_ids === 'string' ? this.utility.infer(req.body.ldap_client_ids) : req.body.ldap_client_ids const ldap_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of ldap_client_ids ) { const client = await LDAPClient.findById(id) if ( !client || !client.active || !req.user.can(`ldap:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_ldap_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ ldap_client_ids: client.id }) if ( other_assoc_app && other_assoc_app.id !== application.id ) return res.status(400) // TODO translate this .message(`The LDAP client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.ldap_client_ids = ldap_client_ids } else application.ldap_client_ids = [] // Verify OAuth client IDs const OAuthClient = this.models.get('oauth:Client') if ( req.body.oauth_client_ids ) { const parsed = typeof req.body.oauth_client_ids === 'string' ? this.utility.infer(req.body.oauth_client_ids) : req.body.oauth_client_ids const oauth_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of oauth_client_ids ) { const client = await OAuthClient.findById(id) if ( !client || !client.active || !req.user.can(`oauth:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ oauth_client_ids: client.id }) if ( other_assoc_app && other_assoc_app.id !== application.id ) return res.status(400) // TODO translate this .message(`The OAuth2 client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.oauth_client_ids = oauth_client_ids } else application.oauth_client_ids = [] // Verify OAuth client IDs const RadiusClient = this.models.get('radius:Client') if ( req.body.radius_client_ids ) { const parsed = typeof req.body.radius_client_ids === 'string' ? this.utility.infer(req.body.radius_client_ids) : req.body.radius_client_ids const radius_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of radius_client_ids ) { const client = await RadiusClient.findById(id) if ( !client || !client.active || !req.user.can(`radius:client:${client.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_radius_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ radius_client_ids: client.id }) if ( other_assoc_app && other_assoc_app.id !== application.id ) return res.status(400) // TODO translate this .message(`The RADIUS client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.radius_client_ids = radius_client_ids } else application.radius_client_ids = [] // Verify OpenID client IDs const OpenIDClient = this.models.get('openid:Client') if ( req.body.openid_client_ids ) { const parsed = typeof req.body.openid_client_ids === 'string' ? this.utility.infer(req.body.openid_client_ids) : req.body.openid_client_ids const openid_client_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of openid_client_ids ) { const client = await OpenIDClient.findById(id) if ( !client ) return res.status(400) .message(`${req.T('api.invalid_oauth_client_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ openid_client_ids: client.id }) if ( other_assoc_app && other_assoc_app.id !== application.id ) return res.status(400) // TODO translate this .message(`The OpenID Connect client ${client.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.openid_client_ids = openid_client_ids } else application.openid_client_ids = [] // Verify SAML service provider IDs const ServiceProvider = this.models.get('saml:ServiceProvider') if ( req.body.saml_service_provider_ids ) { const parsed = typeof req.body.saml_service_provider_ids === 'string' ? this.utility.infer(req.body.saml_service_provider_ids) : req.body.saml_service_provider_ids const saml_service_provider_ids = Array.isArray(parsed) ? parsed : [parsed] for ( const id of saml_service_provider_ids ) { const provider = await ServiceProvider.findById(id) if ( !provider || !provider.active || !req.user.can(`saml:provider:${provider.id}:view`) ) return res.status(400) .message(`${req.T('api.invalid_saml_service_provider_id')} ${id}`) .api() const other_assoc_app = await Application.findOne({ saml_service_provider_ids: provider.id }) if ( other_assoc_app && other_assoc_app.id !== application.id ) return res.status(400) // TODO translate this .message(`The SAML service provider ${provider.name} is already associated with an existing application (${other_assoc_app.name}).`) .api() } application.saml_service_provider_ids = saml_service_provider_ids } else application.saml_service_provider_ids = [] application.name = req.body.name application.identifier = req.body.identifier application.description = req.body.description await application.save() return res.api(await application.to_api()) } async delete_application(req, res, next) { const Application = this.models.get('Application') const application = await Application.findById(req.params.id) if ( !application || !application.active ) return res.status(404) .message(req.T('api.application_not_found')) .api() if ( !req.user.can(`app:${application.id}:delete`) ) return res.status(401) .message(req.T('api.insufficient_permissions')) .api() application.active = false await application.save() return res.api() } } module.exports = exports = AppController