UNPKG

keycloak-connect

Version:
160 lines (134 loc) 4.48 kB
/* * Copyright 2016 Red Hat Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ 'use strict' function handlePermissions (permissions, callback) { for (let i = 0; i < permissions.length; i++) { const expected = permissions[i].split(':') const resource = expected[0] let scope if (expected.length > 1) { scope = expected[1] } const r = callback(resource, scope) if (r === false) { return r } } return true } /** * Construct a policy enforcer. * * @param {Config} config Config object. * * @constructor */ function Enforcer (keycloak, config) { this.keycloak = keycloak this.config = config || {} if (!this.config.response_mode) { this.config.response_mode = 'permissions' } if (!this.config.resource_server_id) { this.config.resource_server_id = this.keycloak.getConfig().clientId } } Enforcer.prototype.enforce = function enforce (expectedPermissions) { const keycloak = this.keycloak const config = this.config if (typeof expectedPermissions === 'string') { expectedPermissions = [expectedPermissions] } return function (request, response, next) { if (!expectedPermissions || expectedPermissions.length === 0) { return next() } const authzRequest = { audience: config.resource_server_id, response_mode: config.response_mode } handlePermissions(expectedPermissions, function (resource, scope) { if (!authzRequest.permissions) { authzRequest.permissions = [] } const permission = { id: resource } if (scope) { permission.scopes = [scope] } authzRequest.permissions.push(permission) }) if (request.kauth && request.kauth.grant) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!request.kauth.grant.access_token.hasPermission(resource, scope)) { return false } })) { return next() } } if (config.claims) { const claims = config.claims(request) if (claims) { authzRequest.claim_token = Buffer.from(JSON.stringify(claims)).toString('base64') authzRequest.claim_token_format = 'urn:ietf:params:oauth:token-type:jwt' } } if (config.response_mode === 'permissions') { return keycloak.checkPermissions(authzRequest, request, function (permissions) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!permissions || permissions.length === 0) { return false } for (let j = 0; j < permissions.length; j++) { const permission = permissions[j] if (permission.rsid === resource || permission.rsname === resource) { if (scope) { if (permission.scopes && permission.scopes.length > 0) { if (!permission.scopes.includes(scope)) { return false } break } return false } } } })) { request.permissions = permissions return next() } return keycloak.accessDenied(request, response, next) }).catch(function () { return keycloak.accessDenied(request, response, next) }) } else if (config.response_mode === 'token') { authzRequest.response_mode = undefined return keycloak.checkPermissions(authzRequest, request).then(function (grant) { if (handlePermissions(expectedPermissions, function (resource, scope) { if (!grant.access_token.hasPermission(resource, scope)) { return false } })) { return next() } return keycloak.accessDenied(request, response, next) }).catch(function () { return keycloak.accessDenied(request, response, next) }) } } } module.exports = Enforcer