Implement OAuth2 server, link oauth:Client and auth::Oauth2Client, implement permission checks
This commit is contained in:
236
app/controllers/api/v1/IAM.controller.js
Normal file
236
app/controllers/api/v1/IAM.controller.js
Normal file
@@ -0,0 +1,236 @@
|
||||
const { Controller } = require('libflitter')
|
||||
|
||||
class IAMController extends Controller {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
async check_entity_access(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
|
||||
if ( !req.body.entity_id && !req.body.target_id )
|
||||
return res.status(400)
|
||||
.message('Missing one or more required fields: entity_id, target_id')
|
||||
.api()
|
||||
|
||||
return res.api(await Policy.check_entity_access(req.body.entity_id, req.body.target_id))
|
||||
}
|
||||
|
||||
async check_user_access(req, res, next) {
|
||||
const User = this.models.get('auth:User')
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
|
||||
if ( !req.body.target_id )
|
||||
return res.status(400)
|
||||
.message('Missing required field: target_id')
|
||||
.api()
|
||||
|
||||
let user = req.user
|
||||
if ( req.body.user_id && req.body.user_id !== 'me' )
|
||||
user = await User.findById(req.body.user_id)
|
||||
|
||||
if ( !user )
|
||||
return res.status(404)
|
||||
.message('User not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`auth:user:${user.id}:view`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
return res.api(await Policy.check_user_access(user, req.body.target_id))
|
||||
}
|
||||
|
||||
async get_policies(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const policies = await Policy.find({ active: true })
|
||||
const data = []
|
||||
|
||||
for ( const policy of policies ) {
|
||||
if ( req.user.can(`iam:policy:${policy.id}:view`) ) {
|
||||
data.push(await policy.to_api())
|
||||
}
|
||||
}
|
||||
|
||||
return res.api(data)
|
||||
}
|
||||
|
||||
async get_policy(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const policy = await Policy.findById(req.params.id)
|
||||
|
||||
if ( !policy )
|
||||
return res.status(404)
|
||||
.message('Policy not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`iam:policy:${policy.id}:view`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
return res.api(await policy.to_api())
|
||||
}
|
||||
|
||||
async create_policy(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
|
||||
const required_fields = ['entity_type', 'entity_id', 'access_type', 'target_type', 'target_id']
|
||||
for ( const field of required_fields ) {
|
||||
if ( !req.body[field] )
|
||||
return res.status(400)
|
||||
.message(`Missing required field: ${field}`)
|
||||
.api()
|
||||
}
|
||||
|
||||
if ( !['user', 'group'].includes(req.body.entity_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_type. Must be one of: user, group.')
|
||||
.api()
|
||||
|
||||
// Make sure the entity_id is valid
|
||||
if ( req.body.entity_type === 'user' ) {
|
||||
const User = this.models.get('auth:User')
|
||||
const user = await User.findById(req.body.entity_id)
|
||||
if ( !user || !req.user.can(`auth:user:${user.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_id.')
|
||||
.api()
|
||||
} else if ( req.body.entity_type === 'group' ) {
|
||||
const Group = this.models.get('auth:Group')
|
||||
const group = await Group.findById(req.body.entity_id)
|
||||
if ( !group || !group.active || !req.user.can(`auth:group:${group.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_id.')
|
||||
.api()
|
||||
}
|
||||
|
||||
if ( !['allow', 'deny'].includes(req.body.access_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid access_type. Must be one of: allow, deny.')
|
||||
.api()
|
||||
|
||||
if ( !['application'].includes(req.body.target_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid target_type. Must be one of: application.')
|
||||
.api()
|
||||
|
||||
// Make sure the target_id is valid
|
||||
if ( req.body.target_type === 'application' ) {
|
||||
const Application = this.models.get('Application')
|
||||
const app = await Application.findById(req.body.target_id)
|
||||
if ( !app || !app.active || !req.user.can(`app:${app.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid target_id.')
|
||||
.api()
|
||||
}
|
||||
|
||||
const policy = new Policy({
|
||||
entity_type: req.body.entity_type,
|
||||
entity_id: req.body.entity_id,
|
||||
access_type: req.body.access_type,
|
||||
target_type: req.body.target_type,
|
||||
target_id: req.body.target_id,
|
||||
})
|
||||
|
||||
await policy.save()
|
||||
req.user.allow(`iam:policy:${policy.id}`)
|
||||
await req.user.save()
|
||||
return res.api(await policy.to_api())
|
||||
}
|
||||
|
||||
async update_policy(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const policy = await Policy.findById(req.params.id)
|
||||
|
||||
if ( !policy || !policy.active )
|
||||
return res.status(404)
|
||||
.message('Policy not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`iam:policy:${policy.id}:update`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
const required_fields = ['entity_type', 'entity_id', 'access_type', 'target_type', 'target_id']
|
||||
for ( const field of required_fields ) {
|
||||
if ( !req.body[field] )
|
||||
return res.status(400)
|
||||
.message(`Missing required field: ${field}`)
|
||||
.api()
|
||||
}
|
||||
|
||||
if ( !['user', 'group'].includes(req.body.entity_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_type. Must be one of: user, group.')
|
||||
.api()
|
||||
|
||||
// Make sure the entity_id is valid
|
||||
if ( req.body.entity_type === 'user' ) {
|
||||
const User = this.models.get('auth:User')
|
||||
const user = await User.findById(req.body.entity_id)
|
||||
if ( !user || !req.user.can(`auth:user:${user.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_id.')
|
||||
.api()
|
||||
} else if ( req.body.entity_type === 'group' ) {
|
||||
const Group = this.models.get('auth:Group')
|
||||
const group = await Group.findById(req.body.entity_id)
|
||||
if ( !group || !group.active || !req.user.can(`auth:group:${group.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid entity_id.')
|
||||
.api()
|
||||
}
|
||||
|
||||
if ( !['allow', 'deny'].includes(req.body.access_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid access_type. Must be one of: allow, deny.')
|
||||
.api()
|
||||
|
||||
if ( !['application'].includes(req.body.target_type) )
|
||||
return res.status(400)
|
||||
.message('Invalid target_type. Must be one of: application.')
|
||||
.api()
|
||||
|
||||
// Make sure the target_id is valid
|
||||
if ( req.body.target_type === 'application' ) {
|
||||
const Application = this.models.get('Application')
|
||||
const app = await Application.findById(req.body.target_id)
|
||||
if ( !app || !app.active || !req.user.can(`app:${app.id}:view`) )
|
||||
return res.status(400)
|
||||
.message('Invalid target_id.')
|
||||
.api()
|
||||
}
|
||||
|
||||
policy.entity_type = req.body.entity_type
|
||||
policy.entity_id = req.body.entity_id
|
||||
policy.access_type = req.body.access_type
|
||||
policy.target_type = req.body.target_type
|
||||
policy.target_id = req.body.target_id
|
||||
await policy.save()
|
||||
return res.api()
|
||||
}
|
||||
|
||||
async delete_policy(req, res, next) {
|
||||
const Policy = this.models.get('iam:Policy')
|
||||
const policy = await Policy.findById(req.params.id)
|
||||
|
||||
if ( !policy || !policy.active )
|
||||
return res.status(404)
|
||||
.message('Policy not found with that ID.')
|
||||
.api()
|
||||
|
||||
if ( !req.user.can(`iam:policy:${policy.id}:delete`) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
.api()
|
||||
|
||||
policy.active = false
|
||||
await policy.save()
|
||||
return res.api()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = IAMController
|
||||
Reference in New Issue
Block a user