UNPKG

@backstage-community/plugin-rbac-backend

Version:
229 lines (225 loc) 8.05 kB
'use strict'; var pluginPermissionCommon = require('@backstage/plugin-permission-common'); var pluginRbacCommon = require('@backstage-community/plugin-rbac-common'); var adminCreation = require('../admin-permissions/admin-creation.cjs.js'); var auditor = require('../auditor/auditor.cjs.js'); var aliasResolver = require('../conditional-aliases/alias-resolver.cjs.js'); var csvFileWatcher = require('../file-permissions/csv-file-watcher.cjs.js'); var yamlConditionalFileWatcher = require('../file-permissions/yaml-conditional-file-watcher.cjs.js'); class RBACPermissionPolicy { constructor(enforcer, auditor, conditionStorage, superUserList) { this.enforcer = enforcer; this.auditor = auditor; this.conditionStorage = conditionStorage; this.superUserList = superUserList; } superUserList; static async build(logger, auditor, configApi, conditionalStorage, enforcerDelegate, roleMetadataStorage, knex, pluginMetadataCollector, auth) { const superUserList = []; const adminUsers = configApi.getOptionalConfigArray( "permission.rbac.admin.users" ); const superUsers = configApi.getOptionalConfigArray( "permission.rbac.admin.superUsers" ); const policiesFile = configApi.getOptionalString( "permission.rbac.policies-csv-file" ); const allowReload = configApi.getOptionalBoolean("permission.rbac.policyFileReload") || false; const conditionalPoliciesFile = configApi.getOptionalString( "permission.rbac.conditionalPoliciesFile" ); if (superUsers && superUsers.length > 0) { for (const user of superUsers) { const userName = user.getString("name"); superUserList.push(userName); } } await adminCreation.useAdminsFromConfig( adminUsers || [], enforcerDelegate, auditor, roleMetadataStorage, knex ); await adminCreation.setAdminPermissions(enforcerDelegate, auditor); if ((!adminUsers || adminUsers.length === 0) && (!superUsers || superUsers.length === 0)) { logger.warn( "There are no admins or super admins configured for the RBAC-backend plugin." ); } const csvFile = new csvFileWatcher.CSVFileWatcher( policiesFile, allowReload, logger, enforcerDelegate, roleMetadataStorage, auditor ); await csvFile.initialize(); const conditionalFile = new yamlConditionalFileWatcher.YamlConditinalPoliciesFileWatcher( conditionalPoliciesFile, allowReload, logger, conditionalStorage, auditor, auth, pluginMetadataCollector, roleMetadataStorage, enforcerDelegate ); await conditionalFile.initialize(); if (!conditionalPoliciesFile) { logger.info("conditional policies file feature was disabled"); await conditionalFile.cleanUpConditionalPolicies(); } if (!policiesFile) { logger.info("csv policies file feature was disabled"); await csvFile.cleanUpRolesAndPolicies(); } return new RBACPermissionPolicy( enforcerDelegate, auditor, conditionalStorage, superUserList ); } async handle(request, user) { const userEntityRef = user?.info.userEntityRef ?? `user without entity`; const auditorEvent = await auditor.createPermissionEvaluationAuditorEvent( this.auditor, userEntityRef, request ); try { let status = false; const action = pluginRbacCommon.toPermissionAction(request.permission.attributes); if (!user) { await auditorEvent.success({ meta: { result: pluginPermissionCommon.AuthorizeResult.DENY } }); return { result: pluginPermissionCommon.AuthorizeResult.DENY }; } if (this.superUserList.includes(userEntityRef)) { await auditorEvent.success({ meta: { result: pluginPermissionCommon.AuthorizeResult.ALLOW } }); return { result: pluginPermissionCommon.AuthorizeResult.ALLOW }; } const permissionName = request.permission.name; const roles = await this.enforcer.getRolesForUser(userEntityRef); const hasNamedPermission = await this.hasImplicitPermissionSpecifiedByName( permissionName, action, roles ); if (request.permission.name === "policy.entity.create" && !hasNamedPermission) { request.permission = { attributes: { action: "create" }, type: "resource", resourceType: "policy-entity", name: "policy.entity.create" }; } if (pluginPermissionCommon.isResourcePermission(request.permission)) { const resourceType = request.permission.resourceType; if (user) { const conditionResult = await this.handleConditions( auditorEvent, userEntityRef, request, roles, user.info ); if (conditionResult) { return conditionResult; } } const obj = hasNamedPermission ? permissionName : resourceType; status = await this.isAuthorized(userEntityRef, obj, action, roles); } else { status = await this.isAuthorized( userEntityRef, permissionName, action, roles ); } const result = status ? pluginPermissionCommon.AuthorizeResult.ALLOW : pluginPermissionCommon.AuthorizeResult.DENY; await auditorEvent.success({ meta: { result } }); return { result }; } catch (error) { await auditorEvent.fail({ error, meta: { result: pluginPermissionCommon.AuthorizeResult.DENY } }); return { result: pluginPermissionCommon.AuthorizeResult.DENY }; } } async hasImplicitPermissionSpecifiedByName(permissionName, action, roles) { for (const role of roles) { const perms = await this.enforcer.getFilteredPolicy( 0, role, permissionName, action ); if (perms.length > 0) { return true; } } return false; } isAuthorized = async (userIdentity, permission, action, roles) => { return await this.enforcer.enforce(userIdentity, permission, action, roles); }; async handleConditions(auditorEvent, userEntityRef, request, roles, userInfo) { const permissionName = request.permission.name; const resourceType = request.permission.resourceType; const action = pluginRbacCommon.toPermissionAction(request.permission.attributes); const conditions = []; let pluginId = ""; for (const role of roles) { const conditionalDecisions = await this.conditionStorage.filterConditions( role, undefined, resourceType, [action], [permissionName] ); if (conditionalDecisions.length === 1) { pluginId = conditionalDecisions[0].pluginId; conditions.push(conditionalDecisions[0].conditions); } if (conditionalDecisions.length > 1) { await auditorEvent.fail({ error: new Error( `Detected ${JSON.stringify( conditionalDecisions )} collisions for conditional policies. Expected to find a stored single condition for permission with name ${permissionName}, resource type ${resourceType}, action ${action} for user ${userEntityRef}` ), meta: { result: pluginPermissionCommon.AuthorizeResult.DENY } }); return { result: pluginPermissionCommon.AuthorizeResult.DENY }; } } if (conditions.length > 0) { const result = { pluginId, result: pluginPermissionCommon.AuthorizeResult.CONDITIONAL, resourceType, conditions: { anyOf: conditions } }; aliasResolver.replaceAliases(result.conditions, userInfo); await auditorEvent.success({ meta: { ...result } }); return result; } return undefined; } } exports.RBACPermissionPolicy = RBACPermissionPolicy; //# sourceMappingURL=permission-policy.cjs.js.map