Implement OAuth2 server, link oauth:Client and auth::Oauth2Client, implement permission checks
This commit is contained in:
@@ -2,6 +2,16 @@ const { Middleware } = require('libflitter')
|
||||
|
||||
class PermissionMiddleware extends Middleware {
|
||||
async test(req, res, next, { check }) {
|
||||
// If the request was authorized using an OAuth2 bearer token,
|
||||
// make sure the associated client has permission to access this endpoint.
|
||||
if ( req?.oauth?.client ) {
|
||||
if ( !req.oauth.client.can(check) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions (OAuth2 Client).')
|
||||
.api()
|
||||
}
|
||||
|
||||
// Make sure the user has permission
|
||||
if ( !req.user.can(check) )
|
||||
return res.status(401)
|
||||
.message('Insufficient permissions.')
|
||||
|
||||
60
app/routing/middleware/auth/APIRoute.middleware.js
Normal file
60
app/routing/middleware/auth/APIRoute.middleware.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const { Middleware } = require('libflitter')
|
||||
|
||||
class APIRouteMiddleware extends Middleware {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
async test(req, res, next, { allow_token = true, allow_user = true }) {
|
||||
// First, check if there is a user in the session.
|
||||
if ( allow_user && req.is_auth ) {
|
||||
return next()
|
||||
} else if ( allow_token ) {
|
||||
return req.app.oauth2.authorise()(req, res, async e => {
|
||||
if ( e ) return next(e)
|
||||
// Look up the OAuth2 client an inject it into the route
|
||||
if ( req.user && req.user.id ) {
|
||||
const User = this.models.get('auth:User')
|
||||
const user = await User.findById(req.user.id)
|
||||
if ( !user )
|
||||
return res.status(401)
|
||||
.message('The user this token is associated with no longer exists.')
|
||||
.api()
|
||||
|
||||
req.user = user
|
||||
req.is_auth = true
|
||||
|
||||
// Look up the token and the associated client
|
||||
const Oauth2BearerToken = this.models.get('auth::Oauth2BearerToken')
|
||||
const Client = this.models.get('oauth:Client')
|
||||
|
||||
// e.g. "Bearer XYZ".split(' ')[1] -> "XYZ"
|
||||
const bearer = req.headers.authorization.split(' ')[1]
|
||||
const token = await Oauth2BearerToken.findOne({ accessToken: bearer })
|
||||
if ( !token )
|
||||
return res.status(401)
|
||||
.message('Unable to lookup OAuth2 token.')
|
||||
.api()
|
||||
|
||||
const client = await Client.findOne({uuid: token.clientID})
|
||||
if ( !client )
|
||||
return res.status(401)
|
||||
.message('This OAuth2 client is no longer authorized.')
|
||||
.api()
|
||||
|
||||
req.oauth.token = token
|
||||
req.oauth.client = client
|
||||
} else
|
||||
return res.status(401)
|
||||
.message('Unable to lookup user associated with that token.')
|
||||
.api()
|
||||
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(401).api()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = APIRouteMiddleware
|
||||
41
app/routing/routers/api/v1/app.routes.js
Normal file
41
app/routing/routers/api/v1/app.routes.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const app_routes = {
|
||||
prefix: '/api/v1/applications',
|
||||
|
||||
middleware: [
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/': [
|
||||
['middleware::api:Permission', { check: 'v1:applications:list' }],
|
||||
'controller::api:v1:App.get_applications',
|
||||
],
|
||||
'/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:applications:get' }],
|
||||
'controller::api:v1:App.get_application',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/': [
|
||||
['middleware::api:Permission', { check: 'v1:applications:create' }],
|
||||
'controller::api:v1:App.create_application',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:applications:update' }],
|
||||
'controller::api:v1:App.update_application',
|
||||
],
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:applications:delete' }],
|
||||
'controller::api:v1:App.delete_application',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = exports = app_routes
|
||||
@@ -9,30 +9,100 @@ const auth_routes = {
|
||||
'/mfa/enable/date': ['middleware::auth:UserOnly', 'controller::api:v1:Auth.get_mfa_enable_date'],
|
||||
|
||||
'/roles': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:roles:list' }],
|
||||
'controller::api:v1:Auth.get_roles',
|
||||
],
|
||||
'/users': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:users:list' }],
|
||||
'controller::api:v1:Auth.get_users',
|
||||
],
|
||||
'/groups': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:groups:list' }],
|
||||
'controller::api:v1:Auth.get_groups',
|
||||
],
|
||||
'/users/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:users:get' }],
|
||||
'controller::api:v1:Auth.get_user',
|
||||
],
|
||||
'/groups/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:groups:get' }],
|
||||
'controller::api:v1:Auth.get_group',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/validate/username': ['controller::api:v1:Auth.validate_username'],
|
||||
'/attempt': [ 'controller::api:v1:Auth.attempt' ],
|
||||
'/mfa/generate': ['middleware::auth:UserOnly', 'controller::api:v1:Auth.generate_mfa_key'],
|
||||
'/mfa/attempt': ['middleware::auth:DMZOnly', 'controller::api:v1:Auth.attempt_mfa'],
|
||||
'/validate/username': [
|
||||
'controller::api:v1:Auth.validate_username'
|
||||
],
|
||||
|
||||
'/attempt': [
|
||||
'controller::api:v1:Auth.attempt'
|
||||
],
|
||||
|
||||
'/mfa/generate': [
|
||||
'middleware::auth:UserOnly',
|
||||
'controller::api:v1:Auth.generate_mfa_key'
|
||||
],
|
||||
|
||||
'/mfa/attempt': [
|
||||
'middleware::auth:DMZOnly',
|
||||
'controller::api:v1:Auth.attempt_mfa'
|
||||
],
|
||||
|
||||
'/mfa/enable': [
|
||||
'middleware::auth:UserOnly',
|
||||
['middleware::auth:RequireTrust', { scope: 'mfa.enable', deplete: true }],
|
||||
'controller::api:v1:Auth.enable_mfa'
|
||||
],
|
||||
|
||||
'/mfa/disable': [
|
||||
'middleware::auth:UserOnly',
|
||||
['middleware::auth:RequireTrust', { scope: 'mfa.disable', deplete: true }],
|
||||
'controller::api:v1:Auth.disable_mfa',
|
||||
],
|
||||
|
||||
'/groups': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:groups:create' }],
|
||||
'controller::api:v1:Auth.create_group',
|
||||
],
|
||||
|
||||
'/users': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:users:create' }],
|
||||
'controller::api:v1:Auth.create_user',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/groups/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:groups:update' }],
|
||||
'controller::api:v1:Auth.update_group',
|
||||
],
|
||||
'/users/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:users:update' }],
|
||||
'controller::api:v1:Auth.update_user',
|
||||
],
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/groups/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:groups:delete' }],
|
||||
'controller::api:v1:Auth.delete_group',
|
||||
],
|
||||
'/users/:id': [
|
||||
'middleware::auth:APIRoute',
|
||||
['middleware::api:Permission', { check: 'v1:auth:users:delete' }],
|
||||
'controller::api:v1:Auth.delete_user',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
49
app/routing/routers/api/v1/iam.routes.js
Normal file
49
app/routing/routers/api/v1/iam.routes.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const iam_routes = {
|
||||
prefix: '/api/v1/iam',
|
||||
|
||||
middleware: [
|
||||
'auth:APIRoute'
|
||||
],
|
||||
|
||||
get: {
|
||||
'/policy': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:policy:list' }],
|
||||
'controller::api:v1:IAM.get_policies',
|
||||
],
|
||||
'/policy/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:policy:get' }],
|
||||
'controller::api:v1:IAM.get_policy',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/policy': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:policy:create' }],
|
||||
'controller::api:v1:IAM.create_policy',
|
||||
],
|
||||
'/check_entity_access': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:check_entity_access' }],
|
||||
'controller::api:v1:IAM.check_entity_access',
|
||||
],
|
||||
'/check_user_access': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:check_user_access' }],
|
||||
'controller::api:v1:IAM.check_user_access',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/policy/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:policy:update' }],
|
||||
'controller::api:v1:IAM.update_policy',
|
||||
],
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/policy/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:iam:policy:delete' }],
|
||||
'controller::api:v1:IAM.delete_policy',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = exports = iam_routes
|
||||
@@ -2,7 +2,7 @@ const ldap_routes = {
|
||||
prefix: '/api/v1/ldap',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
|
||||
@@ -2,15 +2,21 @@ const message_routes = {
|
||||
prefix: '/api/v1/message',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/banners': ['controller::api:v1:Message.get_banners'],
|
||||
'/banners': [
|
||||
['middleware::api:Permission', { check: 'v1:message:banners:get' }],
|
||||
'controller::api:v1:Message.get_banners',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/banners/read/:banner_id': ['controller::api:v1:Message.read_banner'],
|
||||
'/banners/read/:banner_id': [
|
||||
['middleware::api:Permission', { check: 'v1:message:banners:update' }],
|
||||
'controller::api:v1:Message.read_banner',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
41
app/routing/routers/api/v1/oauth.routes.js
Normal file
41
app/routing/routers/api/v1/oauth.routes.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const oauth_routes = {
|
||||
prefix: '/api/v1/oauth',
|
||||
|
||||
middleware: [
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/clients': [
|
||||
['middleware::api:Permission', { check: 'v1:oauth:clients:list' }],
|
||||
'controller::api:v1:OAuth.get_clients',
|
||||
],
|
||||
'/clients/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:oauth:clients:get' }],
|
||||
'controller::api:v1:OAuth.get_client',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/clients': [
|
||||
['middleware::api:Permission', { check: 'v1:oauth:clients:create' }],
|
||||
'controller::api:v1:OAuth.create_client',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/clients/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:oauth:clients:update' }],
|
||||
'controller::api:v1:OAuth.update_client',
|
||||
],
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/clients/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:oauth:clients:delete' }],
|
||||
'controller::api:v1:OAuth.delete_client',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = exports = oauth_routes
|
||||
@@ -2,16 +2,25 @@ const password_routes = {
|
||||
prefix: '/api/v1/password',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/resets': ['controller::api:v1:Password.get_resets'],
|
||||
'/app_passwords': ['controller::api:v1:Password.get_app_passwords'],
|
||||
'/resets': [
|
||||
['middleware::api:Permission', { check: 'v1:password:resets:get' }],
|
||||
'controller::api:v1:Password.get_resets',
|
||||
],
|
||||
'/app_passwords': [
|
||||
['middleware::api:Permission', { check: 'v1:password:app_passwords:get' }],
|
||||
'controller::api:v1:Password.get_app_passwords',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/app_passwords': ['controller::api:v1:Password.create_app_password'],
|
||||
'/app_passwords': [
|
||||
['middleware::api:Permission', { check: 'v1:password:app_passwords:create' }],
|
||||
'controller::api:v1:Password.create_app_password',
|
||||
],
|
||||
'/resets': [
|
||||
['middleware::auth:RequireTrust', { scope: 'password.reset' }],
|
||||
'controller::api:v1:Password.reset_password',
|
||||
@@ -19,7 +28,10 @@ const password_routes = {
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/app_passwords/:uuid': ['controller::api:v1:Password.delete_app_password'],
|
||||
'/app_passwords/:uuid': [
|
||||
['middleware::api:Permission', { check: 'v1:password:app_passwords:delete' }],
|
||||
'controller::api:v1:Password.delete_app_password',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,19 @@ const profile_routes = {
|
||||
prefix: '/api/v1/profile',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/:user_id': [ // user_id | 'me'
|
||||
['middleware::api:Permission', { check: 'v1:profile:get' }],
|
||||
'controller::api:v1:Profile.fetch',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/:user_id': [ // user_id | 'me'
|
||||
['middleware::api:Permission', { check: 'v1:profile:update' }],
|
||||
'controller::api:v1:Profile.update',
|
||||
],
|
||||
},
|
||||
|
||||
50
app/routing/routers/api/v1/reflect.routes.js
Normal file
50
app/routing/routers/api/v1/reflect.routes.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const reflect_routes = {
|
||||
prefix: '/api/v1/reflect',
|
||||
|
||||
middleware: [
|
||||
'auth:APIRoute'
|
||||
],
|
||||
|
||||
get: {
|
||||
'/scopes': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:scopes' }],
|
||||
'controller::api:v1:Reflect.get_scopes',
|
||||
],
|
||||
'/tokens': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:tokens:list' }],
|
||||
'controller::api:v1:Reflect.get_tokens',
|
||||
],
|
||||
'/tokens/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:tokens:get' }],
|
||||
'controller::api:v1:Reflect.get_token',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
'/tokens': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:tokens:create'}],
|
||||
'controller::api:v1:Reflect.create_token',
|
||||
],
|
||||
|
||||
'/check_permissions': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:check_permissions' }],
|
||||
'controller::api:v1:Reflect.check_permissions',
|
||||
],
|
||||
},
|
||||
|
||||
patch: {
|
||||
'/tokens/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:tokens:update' }],
|
||||
'controller::api:v1:Reflect.update_token',
|
||||
],
|
||||
},
|
||||
|
||||
delete: {
|
||||
'/tokens/:id': [
|
||||
['middleware::api:Permission', { check: 'v1:reflect:tokens:delete' }],
|
||||
'controller::api:v1:Reflect.delete_token',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = exports = reflect_routes
|
||||
@@ -2,7 +2,7 @@ const saml_routes = {
|
||||
prefix: '/api/v1/saml',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
'auth:APIRoute',
|
||||
],
|
||||
|
||||
get: {
|
||||
|
||||
Reference in New Issue
Block a user