Implement OAuth2 server, link oauth:Client and auth::Oauth2Client, implement permission checks
This commit is contained in:
272
app/controllers/api/v1/App.controller.js
Normal file
272
app/controllers/api/v1/App.controller.js
Normal file
@@ -0,0 +1,272 @@
|
||||
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('Application not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`app:${application.id}:view`) )
|
||||
return res.status(401)
|
||||
.message('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('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
const required_fields = ['name', 'identifier']
|
||||
for ( const field of required_fields ) {
|
||||
if ( !req.body[field] )
|
||||
return res.status(400)
|
||||
.message(`Missing required 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('Improperly formatted field: identifier (alphanumeric with underscores only)')
|
||||
.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('An Application with that identifier 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(`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)
|
||||
.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(`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)
|
||||
.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 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(`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)
|
||||
.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('Application not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`app:${application.id}:update`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
const required_fields = ['name', 'identifier']
|
||||
for ( const field of required_fields ) {
|
||||
if ( !req.body[field] )
|
||||
return res.status(400)
|
||||
.message(`Missing required 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('Improperly formatted field: identifier (alphanumeric with underscores only)')
|
||||
.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('An Application with that identifier 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(`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)
|
||||
.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(`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)
|
||||
.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 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(`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)
|
||||
.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('Application not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`app:${application.id}:delete`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
application.active = false
|
||||
await application.save()
|
||||
return res.api()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = AppController
|
||||
Reference in New Issue
Block a user