UNPKG

@backstage-community/plugin-rbac-backend

Version:
949 lines (945 loc) 35.8 kB
'use strict'; var errors = require('@backstage/errors'); var pluginPermissionBackend = require('@backstage/plugin-permission-backend'); var pluginPermissionCommon = require('@backstage/plugin-permission-common'); var lodash = require('lodash'); var pluginRbacCommon = require('@backstage-community/plugin-rbac-common'); var restInterceptor = require('../auditor/rest-interceptor.cjs.js'); var roleMetadata = require('../database/role-metadata.cjs.js'); var helper = require('../helper.cjs.js'); var conditionValidation = require('../validation/condition-validation.cjs.js'); var policiesValidation = require('../validation/policies-validation.cjs.js'); var conditions = require('../permissions/conditions.cjs.js'); require('../permissions/rules.cjs.js'); var permissionDefinitionRoutes = require('./permission-definition-routes.cjs.js'); async function authorizeConditional(request, permission, deps) { const { auth, httpAuth, permissions } = deps; const credentials = await httpAuth.credentials(request, { allow: ["user", "service"] }); if (auth.isPrincipal(credentials, "service") && permission !== pluginRbacCommon.policyEntityReadPermission) { throw new errors.NotAllowedError( `Only credential principal with type 'user' permitted to modify permissions` ); } let decision; if (permission.type === "resource") { decision = (await permissions.authorizeConditional([{ permission }], { credentials }))[0]; } else { decision = (await permissions.authorize([{ permission }], { credentials }))[0]; } if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) { throw new errors.NotAllowedError(); } return { decision, credentials }; } class PoliciesServer { constructor(options, enforcer, conditionalStorage, pluginPermMetaData, roleMetadata, extraPluginsIdStorage, pluginIdProvider, rbacProviders) { this.options = options; this.enforcer = enforcer; this.conditionalStorage = conditionalStorage; this.pluginPermMetaData = pluginPermMetaData; this.roleMetadata = roleMetadata; this.extraPluginsIdStorage = extraPluginsIdStorage; this.pluginIdProvider = pluginIdProvider; this.rbacProviders = rbacProviders; } async serve() { const router = await pluginPermissionBackend.createRouter(this.options); const { logger, auditor, auth, permissionsRegistry } = this.options; const isPluginEnabled = this.options.config.getOptionalBoolean("permission.enabled"); if (!isPluginEnabled) { return router; } const transformConditions = conditions.conditionTransformerFunc(permissionsRegistry); router.get("/", async (request, response) => { await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); response.send({ status: "Authorized" }); }); router.get( "/policies", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter); let policies = []; if (this.isPolicyFilterEnabled(request)) { const entityRef = this.getFirstQuery(request.query.entityRef); const permission = this.getFirstQuery(request.query.permission); const policy = this.getFirstQuery(request.query.policy); const effect = this.getFirstQuery(request.query.effect); const matchedRoleName = roleMetadata.flatMap( (role) => role.roleEntityRef ); const filter = [entityRef, permission, policy, effect]; policies = matchedRoleName.includes(entityRef) ? await this.enforcer.getFilteredPolicy(0, ...filter) : []; } else { for (const role of roleMetadata) { policies.push( ...await this.enforcer.getFilteredPolicy( 0, ...[role.roleEntityRef] ) ); } } const body = await this.transformPolicyArray(...policies); body.map((policy) => { if (policy.permission === "policy-entity" && policy.policy === "create") { policy.permission = "policy.entity.create"; logger.warn( `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[policy.entityReference, "policy-entity", policy.policy, policy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${policy.metadata?.source}` ); } }); response.json(body); } ); router.get( "/policies/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter); const matchedRoleName = roleMetadata.flatMap((role) => { return role.roleEntityRef; }); const entityRef = this.getEntityReference(request); const policy = matchedRoleName.includes(entityRef) ? await this.enforcer.getFilteredPolicy(0, entityRef) : []; if (policy.length !== 0) { const body = await this.transformPolicyArray(...policy); body.map((bodyPolicy) => { if (bodyPolicy.permission === "policy-entity" && bodyPolicy.policy === "create") { bodyPolicy.permission = "policy.entity.create"; logger.warn( `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[bodyPolicy.entityReference, "policy-entity", bodyPolicy.policy, bodyPolicy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${bodyPolicy.metadata?.source}` ); } }); response.json(body); } else { throw new errors.NotFoundError(); } } ); router.delete( "/policies/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityDeletePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const entityRef = this.getEntityReference(request); const policyRaw = request.body; if (lodash.isEmpty(policyRaw)) { throw new errors.InputError(`permission policy must be present`); } policyRaw.forEach((element) => { element.entityReference = entityRef; }); const processedPolicies = await this.processPolicies( policyRaw, true, undefined, conditionsFilter ); await this.enforcer.removePolicies(processedPolicies); response.locals.meta = { policies: processedPolicies }; response.status(204).end(); } ); router.post( "/policies", restInterceptor.logAuditorEvent(auditor), async (request, response) => { await authorizeConditional( request, pluginRbacCommon.policyEntityCreatePermission, this.options ); const policyRaw = request.body; if (lodash.isEmpty(policyRaw)) { throw new errors.InputError(`permission policy must be present`); } const processedPolicies = await this.processPolicies( policyRaw, false, undefined ); const entityRef = processedPolicies[0][0]; const roleMetadata = await this.roleMetadata.findRoleMetadata(entityRef); if (entityRef.startsWith("role:default") && !roleMetadata) { throw new Error(`Corresponding role ${entityRef} was not found`); } await this.enforcer.addPolicies(processedPolicies); response.locals.meta = { policies: processedPolicies }; response.status(201).end(); } ); router.put( "/policies/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityUpdatePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const entityRef = this.getEntityReference(request); const oldPolicyRaw = request.body.oldPolicy; if (lodash.isEmpty(oldPolicyRaw)) { throw new errors.InputError(`'oldPolicy' object must be present`); } const newPolicyRaw = request.body.newPolicy; if (lodash.isEmpty(newPolicyRaw)) { throw new errors.InputError(`'newPolicy' object must be present`); } [...oldPolicyRaw, ...newPolicyRaw].forEach((element) => { element.entityReference = entityRef; }); const processedOldPolicy = await this.processPolicies( oldPolicyRaw, true, "old policy", conditionsFilter ); oldPolicyRaw.sort( (a, b) => a.permission === b.permission ? this.nameSort(a.policy, b.policy) : this.nameSort(a.permission, b.permission) ); newPolicyRaw.sort( (a, b) => a.permission === b.permission ? this.nameSort(a.policy, b.policy) : this.nameSort(a.permission, b.permission) ); if (lodash.isEqual(oldPolicyRaw, newPolicyRaw) && !oldPolicyRaw.some(lodash.isEmpty)) { response.status(204).end(); } else if (oldPolicyRaw.length > newPolicyRaw.length) { throw new errors.InputError( `'oldPolicy' object has more permission policies compared to 'newPolicy' object` ); } const processedNewPolicy = await this.processPolicies( newPolicyRaw, false, "new policy", conditionsFilter ); const roleMetadata = await this.roleMetadata.findRoleMetadata(entityRef); if (entityRef.startsWith("role:default") && !roleMetadata) { throw new Error(`Corresponding role ${entityRef} was not found`); } await this.enforcer.updatePolicies( processedOldPolicy, processedNewPolicy ); response.locals.meta = { policies: processedNewPolicy }; response.status(200).end(); } ); router.get( "/roles", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roles = await this.enforcer.getGroupingPolicy(); const body = await this.transformRoleArray(conditionsFilter, ...roles); response.json(body); } ); router.get( "/roles/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleEntityRef = this.getEntityReference(request, true); const role = await this.enforcer.getFilteredGroupingPolicy( 1, roleEntityRef ); const body = await this.transformRoleArray(conditionsFilter, ...role); if (body.length !== 0) { response.json(body); } else { throw new errors.NotFoundError(); } } ); router.post( "/roles", restInterceptor.logAuditorEvent(auditor), async (request, response) => { const uniqueItems = /* @__PURE__ */ new Set(); const { credentials } = await authorizeConditional( request, pluginRbacCommon.policyEntityCreatePermission, this.options ); const roleRaw = request.body; let err = policiesValidation.validateRole(roleRaw); if (err) { throw new errors.InputError( // 400 `Invalid role definition. Cause: ${err.message}` ); } this.transformMemberReferencesToLowercase(roleRaw); const rMetadata = await this.roleMetadata.findRoleMetadata( roleRaw.name ); err = await policiesValidation.validateSource("rest", rMetadata); if (err) { throw new errors.NotAllowedError(`Unable to add role: ${err.message}`); } const roles = this.transformRoleToArray(roleRaw); for (const role of roles) { if (await this.enforcer.hasGroupingPolicy(...role)) { throw new errors.ConflictError(); } const roleString = JSON.stringify(role); if (uniqueItems.has(roleString)) { throw new errors.ConflictError( `Duplicate role members found; ${role.at(0)}, ${role.at( 1 )} is a duplicate` ); } else { uniqueItems.add(roleString); } } const modifiedBy = credentials.principal.userEntityRef; const metadata = { roleEntityRef: roleRaw.name, source: "rest", description: roleRaw.metadata?.description ?? "", author: modifiedBy, modifiedBy, owner: roleRaw.metadata?.owner ?? modifiedBy }; await this.enforcer.addGroupingPolicies(roles, metadata); response.locals.meta = { ...metadata, members: roles.map((gp) => gp[0]) }; response.status(201).end(); } ); router.put( "/roles/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { const uniqueItems = /* @__PURE__ */ new Set(); let conditionsFilter; const { decision, credentials } = await authorizeConditional( request, pluginRbacCommon.policyEntityUpdatePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleEntityRef = this.getEntityReference(request, true); const oldRoleRaw = request.body.oldRole; if (!oldRoleRaw) { throw new errors.InputError(`'oldRole' object must be present`); } const newRoleRaw = request.body.newRole; if (!newRoleRaw) { throw new errors.InputError(`'newRole' object must be present`); } oldRoleRaw.name = roleEntityRef; let err = policiesValidation.validateRole(oldRoleRaw); if (err) { throw new errors.InputError( // 400 `Invalid old role object. Cause: ${err.message}` ); } err = policiesValidation.validateRole(newRoleRaw); if (err) { throw new errors.InputError( // 400 `Invalid new role object. Cause: ${err.message}` ); } this.transformMemberReferencesToLowercase(oldRoleRaw); this.transformMemberReferencesToLowercase(newRoleRaw); const oldRole = this.transformRoleToArray(oldRoleRaw); const newRole = this.transformRoleToArray(newRoleRaw); const modifiedBy = credentials.principal.userEntityRef; const newMetadata = { ...newRoleRaw.metadata, source: newRoleRaw.metadata?.source ?? "rest", roleEntityRef: newRoleRaw.name, modifiedBy, owner: newRoleRaw.metadata?.owner ?? "" }; const oldMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef); if (!oldMetadata) { throw new errors.NotFoundError( `Unable to find metadata for ${roleEntityRef}` ); } err = await policiesValidation.validateSource("rest", oldMetadata); if (err) { throw new errors.NotAllowedError(`Unable to edit role: ${err.message}`); } if (!helper.matches(oldMetadata, conditionsFilter)) { throw new errors.NotAllowedError(); } if (lodash.isEqual(oldRole, newRole) && helper.deepSortedEqual(oldMetadata, newMetadata, [ "author", "modifiedBy", "createdAt", "lastModified", "owner" ])) { response.status(204).end(); return; } for (const role of newRole) { const hasRole = oldRole.some((element) => { return lodash.isEqual(element, role); }); if (await this.enforcer.hasGroupingPolicy(...role)) { if (!hasRole) { throw new errors.ConflictError(); } } const roleString = JSON.stringify(role); if (uniqueItems.has(roleString)) { throw new errors.ConflictError( `Duplicate role members found; ${role.at(0)}, ${role.at( 1 )} is a duplicate` ); } else { uniqueItems.add(roleString); } } uniqueItems.clear(); for (const role of oldRole) { if (!await this.enforcer.hasGroupingPolicy(...role)) { throw new errors.NotFoundError( `Member reference: ${role[0]} was not found for role ${roleEntityRef}` ); } const roleString = JSON.stringify(role); if (uniqueItems.has(roleString)) { throw new errors.ConflictError( `Duplicate role members found; ${role.at(0)}, ${role.at( 1 )} is a duplicate` ); } else { uniqueItems.add(roleString); } } await this.enforcer.updateGroupingPolicies( oldRole, newRole, newMetadata ); let message = `Updated ${oldMetadata.roleEntityRef}.`; if (newMetadata.roleEntityRef !== oldMetadata.roleEntityRef) { message = `${message}. Role entity reference renamed to ${newMetadata.roleEntityRef}`; } response.locals.meta = { ...newMetadata, members: newRole.map((gp) => gp[0]) }; response.status(200).end(); } ); router.delete( "/roles/:kind/:namespace/:name", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision, credentials } = await authorizeConditional( request, pluginRbacCommon.policyEntityDeletePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleEntityRef = this.getEntityReference(request, true); const currentMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef); if (!helper.matches(currentMetadata, conditionsFilter)) { throw new errors.NotAllowedError(); } const err = await policiesValidation.validateSource("rest", currentMetadata); if (err) { throw new errors.NotAllowedError(`Unable to delete role: ${err.message}`); } let roleMembers = []; if (request.query.memberReferences) { const memberReference = this.getFirstQuery( request.query.memberReferences ).toLocaleLowerCase("en-US"); const gp = await this.enforcer.getFilteredGroupingPolicy( 0, memberReference, roleEntityRef ); if (gp.length > 0) { roleMembers.push(gp[0]); } else { throw new errors.NotFoundError( `role member '${memberReference}' was not found` ); } } else { roleMembers = await this.enforcer.getFilteredGroupingPolicy( 1, roleEntityRef ); } for (const role of roleMembers) { if (!await this.enforcer.hasGroupingPolicy(...role)) { throw new errors.NotFoundError(`role member '${role[0]}' was not found`); } } const modifiedBy = credentials.principal.userEntityRef; const metadata = { roleEntityRef, source: "rest", modifiedBy }; await this.enforcer.removeGroupingPolicies( roleMembers, metadata, false ); response.locals.meta = { ...metadata, members: roleMembers.map((gp) => gp[0]) }; response.status(204).end(); } ); router.get( "/roles/conditions", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter); const matchedRoleName = roleMetadata.flatMap((role) => { return role.roleEntityRef; }); const conditions = await this.conditionalStorage.filterConditions( this.getFirstQuery(request.query.roleEntityRef), this.getFirstQuery(request.query.pluginId), this.getFirstQuery(request.query.resourceType), this.getActionQueries(request.query.actions) ); const body = conditions.map((condition) => { return { ...condition, permissionMapping: condition.permissionMapping.map( (pm) => pm.action ) }; }).filter((condition) => { return matchedRoleName.includes(condition.roleEntityRef); }); response.json(body); } ); router.post( "/roles/conditions", restInterceptor.logAuditorEvent(auditor), async (request, response) => { await authorizeConditional( request, pluginRbacCommon.policyEntityCreatePermission, this.options ); const roleConditionPolicy = request.body; conditionValidation.validateRoleCondition(roleConditionPolicy); const conditionToCreate = await helper.processConditionMapping( roleConditionPolicy, this.pluginPermMetaData, auth ); const id = await this.conditionalStorage.createCondition(conditionToCreate); const body = { id }; response.locals.meta = { condition: roleConditionPolicy }; response.status(201).json(body); } ); router.get( "/roles/conditions/:id", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityReadPermission, this.options ); const id = parseInt(request.params.id, 10); if (isNaN(id)) { throw new errors.InputError("Id is not a valid number."); } const condition = await this.conditionalStorage.getCondition(id); if (!condition) { throw new errors.NotFoundError(); } if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter); const matchedRoleName = roleMetadata.flatMap((role) => { return role.roleEntityRef; }); const body = matchedRoleName.includes(condition.roleEntityRef) ? { ...condition, permissionMapping: condition.permissionMapping.map( (pm) => pm.action ) } : []; response.json(body); } ); router.delete( "/roles/conditions/:id", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityDeletePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const id = parseInt(request.params.id, 10); if (isNaN(id)) { throw new errors.InputError("Id is not a valid number."); } const condition = await this.conditionalStorage.getCondition(id); if (!condition) { throw new errors.NotFoundError(`Condition with id ${id} was not found`); } const conditionToDelete = { ...condition, permissionMapping: condition.permissionMapping.map((pm) => pm.action) }; const roleMetadata = await this.roleMetadata.findRoleMetadata( conditionToDelete.roleEntityRef ); if (!helper.matches(roleMetadata, conditionsFilter)) { throw new errors.NotAllowedError(); } await this.conditionalStorage.deleteCondition(id); response.locals.meta = { condition: conditionToDelete }; response.status(204).end(); } ); router.put( "/roles/conditions/:id", restInterceptor.logAuditorEvent(auditor), async (request, response) => { let conditionsFilter; const { decision } = await authorizeConditional( request, pluginRbacCommon.policyEntityUpdatePermission, this.options ); if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) { conditionsFilter = transformConditions(decision.conditions); } const id = parseInt(request.params.id, 10); if (isNaN(id)) { throw new errors.InputError("Id is not a valid number."); } const condition = await this.conditionalStorage.getCondition(id); if (!condition) { throw new errors.NotFoundError(`Condition with id ${id} was not found`); } const roleMetadata = await this.roleMetadata.findRoleMetadata( condition.roleEntityRef ); if (!helper.matches(roleMetadata, conditionsFilter)) { throw new errors.NotAllowedError(); } const roleConditionPolicy = request.body; conditionValidation.validateRoleCondition(roleConditionPolicy); const conditionToUpdate = await helper.processConditionMapping( roleConditionPolicy, this.pluginPermMetaData, auth ); await this.conditionalStorage.updateCondition(id, conditionToUpdate); response.locals.meta = { condition: roleConditionPolicy }; response.status(200).end(); } ); router.post( "/refresh/:id", restInterceptor.logAuditorEvent(auditor), async (request, response) => { await authorizeConditional( request, pluginRbacCommon.policyEntityCreatePermission, this.options ); if (!this.rbacProviders) { throw new errors.NotFoundError(`No RBAC providers were found`); } const idProvider = this.rbacProviders.find((provider) => { const id = provider.getProviderName(); return id === request.params.id; }); if (!idProvider) { throw new errors.NotFoundError( `The RBAC provider ${request.params.id} was not found` ); } await idProvider.refresh(); response.status(200).end(); } ); permissionDefinitionRoutes.registerPermissionDefinitionRoutes( router, this.pluginPermMetaData, this.pluginIdProvider, this.extraPluginsIdStorage, this.options ); router.use(restInterceptor.setAuditorError()); return router; } getEntityReference(request, role) { const kind = request.params.kind; const namespace = request.params.namespace; const name = request.params.name; const entityRef = `${kind}:${namespace}/${name}`; const err = policiesValidation.validateEntityReference(entityRef, role); if (err) { throw new errors.InputError(err.message); } return entityRef; } async transformPolicyArray(...policies) { const roleToSourceMap = await helper.buildRoleSourceMap( policies, this.roleMetadata ); const roleBasedPolices = []; for (const p of policies) { const [entityReference, permission, policy, effect] = p; roleBasedPolices.push({ entityReference, permission, policy, effect, metadata: { source: roleToSourceMap.get(entityReference) } }); } return roleBasedPolices; } async transformRoleArray(filter, ...roles) { const combinedRoles = {}; roles.forEach(([value, role]) => { if (combinedRoles.hasOwnProperty(role)) { combinedRoles[role].push(value); } else { combinedRoles[role] = [value]; } }); const result = await Promise.all( Object.entries(combinedRoles).flatMap(async ([role, value]) => { const metadataDao = await this.roleMetadata.findRoleMetadata(role); const metadata = metadataDao ? roleMetadata.daoToMetadata(metadataDao) : undefined; return Promise.resolve({ memberReferences: value, name: role, metadata }); }) ); const filteredResult = result.filter((role) => { return role.metadata && helper.matches(role.metadata, filter); }); return filteredResult; } transformPolicyToArray(policy) { return [ policy.entityReference, policy.permission, policy.policy, policy.effect ]; } transformRoleToArray(role) { const roles = []; for (const entity of role.memberReferences) { roles.push([entity, role.name]); } return roles; } transformMemberReferencesToLowercase(role) { role.memberReferences = role.memberReferences.map( (member) => member.toLocaleLowerCase("en-US") ); } getActionQueries(queryValue) { if (!queryValue) { return undefined; } if (Array.isArray(queryValue)) { const permissionNames = []; for (const permissionQuery of queryValue) { if (typeof permissionQuery === "string" && helper.isPermissionAction(permissionQuery)) { permissionNames.push(permissionQuery); } else { throw new errors.InputError( `Invalid permission action query value: ${permissionQuery}. Permission name should be string.` ); } } return permissionNames; } if (typeof queryValue === "string" && helper.isPermissionAction(queryValue)) { return [queryValue]; } throw new errors.InputError( `Invalid permission action query value: ${queryValue}. Permission name should be string.` ); } getFirstQuery(queryValue) { if (!queryValue) { return ""; } if (Array.isArray(queryValue)) { if (typeof queryValue[0] === "string") { return queryValue[0].toString(); } throw new errors.InputError(`This api doesn't support nested query`); } if (typeof queryValue === "string") { return queryValue; } throw new errors.InputError(`This api doesn't support nested query`); } isPolicyFilterEnabled(request) { return !!request.query.entityRef || !!request.query.permission || !!request.query.policy || !!request.query.effect; } async processPolicies(policyArray, isOld, errorMessage, filter) { const policies = []; const uniqueItems = /* @__PURE__ */ new Set(); for (const policy of policyArray) { let err = policiesValidation.validatePolicy(policy); if (err) { throw new errors.InputError( `Invalid ${errorMessage ?? "policy"} definition. Cause: ${err.message}` ); } const metadata = await this.roleMetadata.findRoleMetadata( policy.entityReference ); if (!helper.matches(metadata, filter)) { throw new errors.NotAllowedError(); } let action = errorMessage ? "edit" : "delete"; action = isOld ? action : "add"; err = await policiesValidation.validateSource("rest", metadata); if (err) { throw new errors.NotAllowedError( `Unable to ${action} policy ${policy.entityReference},${policy.permission},${policy.policy},${policy.effect}: ${err.message}` ); } const transformedPolicy = this.transformPolicyToArray(policy); if (isOld && !await this.enforcer.hasPolicy(...transformedPolicy)) { throw new errors.NotFoundError( `Policy '${helper.policyToString(transformedPolicy)}' not found` ); } if (!isOld && await this.enforcer.hasPolicy(...transformedPolicy)) { throw new errors.ConflictError( `Policy '${helper.policyToString( transformedPolicy )}' has been already stored` ); } const rowString = JSON.stringify(transformedPolicy); if (uniqueItems.has(rowString)) { throw new errors.ConflictError( `Duplicate polices found; ${policy.entityReference}, ${policy.permission}, ${policy.policy}, ${policy.effect} is a duplicate` ); } else { uniqueItems.add(rowString); policies.push(transformedPolicy); } } return policies; } nameSort(nameA, nameB) { if (nameA.toLocaleUpperCase("en-US") < nameB.toLocaleUpperCase("en-US")) { return -1; } if (nameA.toLocaleUpperCase("en-US") > nameB.toLocaleUpperCase("en-US")) { return 1; } return 0; } } exports.PoliciesServer = PoliciesServer; exports.authorizeConditional = authorizeConditional; //# sourceMappingURL=policies-rest-api.cjs.js.map