@backstage-community/plugin-rbac-backend
Version:
977 lines (973 loc) • 36.6 kB
JavaScript
;
var catalogModel = require('@backstage/catalog-model');
var errors = require('@backstage/errors');
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');
var router = require('./router.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, conditionValidationLimits, rbacProviders) {
this.options = options;
this.enforcer = enforcer;
this.conditionalStorage = conditionalStorage;
this.pluginPermMetaData = pluginPermMetaData;
this.roleMetadata = roleMetadata;
this.extraPluginsIdStorage = extraPluginsIdStorage;
this.pluginIdProvider = pluginIdProvider;
this.conditionValidationLimits = conditionValidationLimits;
this.rbacProviders = rbacProviders;
}
async serve() {
const router$1 = await router.createRouter(this.options);
const { logger, auditor, auth, permissionsRegistry } = this.options;
const defRoleMeta = this.roleMetadata.getCachedDefaultRoleMetadata();
let defRole;
if (defRoleMeta) {
defRole = {
name: defRoleMeta.roleEntityRef,
memberReferences: [],
metadata: roleMetadata.daoToMetadata(defRoleMeta)
};
}
const isPluginEnabled = this.options.config.getOptionalBoolean("permission.enabled");
if (!isPluginEnabled) {
return router$1;
}
const transformConditions = conditions.conditionTransformerFunc(permissionsRegistry);
router$1.get("/", async (request, response) => {
await authorizeConditional(
request,
pluginRbacCommon.policyEntityReadPermission,
this.options
);
response.send({ status: "Authorized" });
});
router$1.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$1.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$1.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 = this.getPolicyArrayFromBody(request.body);
policyRaw.forEach((element) => {
element.entityReference = entityRef;
});
const processedPolicies = await this.processPolicies(
policyRaw,
true,
void 0,
conditionsFilter
);
await this.enforcer.removePolicies(processedPolicies);
response.locals.meta = { policies: processedPolicies };
response.status(204).end();
}
);
router$1.post(
"/policies",
restInterceptor.logAuditorEvent(auditor),
async (request, response) => {
await authorizeConditional(
request,
pluginRbacCommon.policyEntityCreatePermission,
this.options
);
const policyRaw = this.getPolicyArrayFromBody(request.body);
const processedPolicies = await this.processPolicies(
policyRaw,
false,
void 0
);
await this.enforcer.addPolicies(processedPolicies);
response.locals.meta = { policies: processedPolicies };
response.status(201).end();
}
);
router$1.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
);
await this.enforcer.updatePolicies(
processedOldPolicy,
processedNewPolicy
);
response.locals.meta = { policies: processedNewPolicy };
response.status(200).end();
}
);
router$1.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);
if (defRole) {
body.push(defRole);
}
response.json(body);
}
);
router$1.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);
let body;
if (defRole && roleEntityRef === defRole.name) {
body = [defRole];
} else {
const role = await this.enforcer.getFilteredGroupingPolicy(
1,
roleEntityRef
);
body = await this.transformRoleArray(conditionsFilter, ...role);
}
if (body.length !== 0) {
response.json(body);
} else {
throw new errors.NotFoundError();
}
}
);
router$1.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$1.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(roleMetadata.daoToMetadata(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$1.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 (!currentMetadata || !helper.matches(roleMetadata.daoToMetadata(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$1.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$1.post(
"/roles/conditions",
restInterceptor.logAuditorEvent(auditor),
async (request, response) => {
await authorizeConditional(
request,
pluginRbacCommon.policyEntityCreatePermission,
this.options
);
const roleConditionPolicy = request.body;
conditionValidation.validateRoleCondition(
roleConditionPolicy,
this.conditionValidationLimits
);
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$1.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$1.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$1 = await this.roleMetadata.findRoleMetadata(
conditionToDelete.roleEntityRef
);
if (!roleMetadata$1 || !helper.matches(roleMetadata.daoToMetadata(roleMetadata$1), conditionsFilter)) {
throw new errors.NotAllowedError();
}
await this.conditionalStorage.deleteCondition(id);
response.locals.meta = { condition: conditionToDelete };
response.status(204).end();
}
);
router$1.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$1 = await this.roleMetadata.findRoleMetadata(
condition.roleEntityRef
);
if (!roleMetadata$1 || !helper.matches(roleMetadata.daoToMetadata(roleMetadata$1), conditionsFilter)) {
throw new errors.NotAllowedError();
}
const roleConditionPolicy = request.body;
conditionValidation.validateRoleCondition(
roleConditionPolicy,
this.conditionValidationLimits
);
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$1.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$1,
this.pluginPermMetaData,
this.pluginIdProvider,
this.extraPluginsIdStorage,
this.options
);
router$1.use(restInterceptor.setAuditorError());
return router$1;
}
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) : void 0;
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 void 0;
}
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;
}
getPolicyArrayFromBody(body) {
if (!Array.isArray(body)) {
throw new errors.InputError(`permission policy must be provided as an array`);
}
if (lodash.isEmpty(body)) {
throw new errors.InputError(`permission policy must be present`);
}
return body;
}
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 (!metadata) {
const { kind } = catalogModel.parseEntityRef(policy.entityReference);
if (kind === "role") {
throw new errors.NotFoundError(
`Corresponding role ${policy.entityReference} was not found`
);
}
throw new errors.NotAllowedError();
}
if (!helper.matches(roleMetadata.daoToMetadata(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