You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CoreID/app/controllers/api/v1/IAM.controller.js

249 lines
9.5 KiB

const { Controller } = require('libflitter')
class IAMController extends Controller {
static get services() {
return [...super.services, 'models', 'canon']
}
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(`${req.T('api.missing_field', true)} 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(`${req.T('api.missing_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(req.T('api.user_not_found'))
.api()
if ( !req.user.can(`auth:user:${user.id}:view`) )
return res.status(401)
.message(req.T('api.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(req.T('iam.policy_not_found'))
.api()
if ( !req.user.can(`iam:policy:${policy.id}:view`) )
return res.status(401)
.message(req.T('api.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(`${req.T('api.missing_field')} ${field}`)
.api()
}
if ( !['user', 'group'].includes(req.body.entity_type) )
return res.status(400)
.message(`${req.T('iam.invalid_entity')} 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(`${req.T('common.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(`${req.T('common.invalid')} entity_id.`)
.api()
}
if ( !['allow', 'deny'].includes(req.body.access_type) )
return res.status(400)
.message(`${req.T('common.invalid')} access_type. ${req.T('api:must_one')} allow, deny.`)
.api()
if ( !['application', 'api_scope'].includes(req.body.target_type) )
return res.status(400)
.message(`${req.T('common.invalid')} target_type. ${req.T('api:must_one')} application, api_scope.`)
.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(`${req.T('common.invalid')} target_id.`)
.api()
} else if ( req.body.target_type === 'api_scope' ) {
const api_scopes = this.canon.get('controller::api:v1:Reflect.api_scopes')()
if ( !api_scopes.includes(req.body.target_id) )
return res.status(400)
.message(`${req.T('common.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(req.T('iam.policy_not_found'))
.api()
if ( !req.user.can(`iam:policy:${policy.id}:update`) )
return res.status(401)
.message(req.T('api.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(`${req.T('api.missing_field')} ${field}`)
.api()
}
if ( !['user', 'group'].includes(req.body.entity_type) )
return res.status(400)
.message(`${req.T('common.invalid')} entity_type. ${req.T('api.must_one')} 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(`${req.T('common.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(`${req.T('common.invalid')} entity_id.`)
.api()
}
if ( !['allow', 'deny'].includes(req.body.access_type) )
return res.status(400)
.message(`${req.T('common.invalid')} access_type. ${req.T('api.must_one')} allow, deny.`)
.api()
if ( !['application', 'api_scope'].includes(req.body.target_type) )
return res.status(400)
.message(`${req.T('common.invalid')} target_type. ${req.T('api.must_one')} application, api_scope.`)
.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(`${req.T('common.invalid')} target_id.`)
.api()
} else if ( req.body.target_type === 'api_scope' ) {
const api_scopes = this.canon.get('controller::api:v1:Reflect.api_scopes')()
if ( !api_scopes.includes(req.body.target_id) )
return res.status(400)
.message(`${req.T('common.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(req.T('iam.policy_not_found'))
.api()
if ( !req.user.can(`iam:policy:${policy.id}:delete`) )
return res.status(401)
.message(req.T('api.insufficient_permissions'))
.api()
policy.active = false
await policy.save()
return res.api()
}
}
module.exports = exports = IAMController