@backstage-community/plugin-rbac-backend
Version:
227 lines (223 loc) • 8.01 kB
JavaScript
'use strict';
var casbin = require('casbin');
var auditor = require('../auditor/auditor.cjs.js');
var helper = require('../helper.cjs.js');
var permissionModel = require('../service/permission-model.cjs.js');
var policiesValidation = require('../validation/policies-validation.cjs.js');
class Connection {
constructor(id, enforcer, roleMetadataStorage, logger, auditor) {
this.id = id;
this.enforcer = enforcer;
this.roleMetadataStorage = roleMetadataStorage;
this.logger = logger;
this.auditor = auditor;
}
async applyRoles(roles) {
const lowercasedRoles = helper.transformRolesGroupToLowercase(roles);
const stringPolicy = helper.typedPoliciesToString(lowercasedRoles, "g");
const providerRolesforRemoval = [];
const tempEnforcer = await casbin.newEnforcer(
casbin.newModelFromString(permissionModel.MODEL),
new casbin.StringAdapter(stringPolicy)
);
const providerRoles = await this.getProviderRoles();
await this.enforcer.loadPolicy();
for (const providerRole of providerRoles) {
providerRolesforRemoval.push(
...await this.enforcer.getFilteredGroupingPolicy(1, providerRole)
);
}
await this.removeRoles(providerRolesforRemoval, tempEnforcer);
await this.addRoles(lowercasedRoles);
}
async applyPermissions(permissions) {
const stringPolicy = helper.typedPoliciesToString(permissions, "p");
const providerPermissions = [];
const tempEnforcer = await casbin.newEnforcer(
casbin.newModelFromString(permissionModel.MODEL),
new casbin.StringAdapter(stringPolicy)
);
const providerRoles = await this.getProviderRoles();
await this.enforcer.loadPolicy();
for (const providerRole of providerRoles) {
providerPermissions.push(
...await this.enforcer.getFilteredPolicy(0, providerRole)
);
}
await this.removePermissions(providerPermissions, tempEnforcer);
await this.addPermissions(permissions);
}
async addRoles(roles) {
for (const role of roles) {
if (!await this.enforcer.hasGroupingPolicy(...role)) {
const metadata = await this.roleMetadataStorage.findRoleMetadata(
role[1]
);
const err = await policiesValidation.validateGroupingPolicy(role, metadata, this.id);
if (err) {
this.logger.warn(err.message);
continue;
}
let roleMeta = await this.roleMetadataStorage.findRoleMetadata(role[1]);
if (!roleMeta) {
roleMeta = {
modifiedBy: this.id,
source: this.id,
roleEntityRef: role[1]
};
}
const auditorMeta = {
...roleMeta,
members: [role[0]]
};
const auditorEvent = await this.auditor.createEvent({
eventId: auditor.RoleEvents.ROLE_WRITE,
severityLevel: "medium",
meta: {
actionType: roleMeta ? auditor.ActionType.UPDATE : auditor.ActionType.CREATE,
source: auditorMeta.source
}
});
try {
await this.enforcer.addGroupingPolicy(role, roleMeta);
await auditorEvent.success({ meta: auditorMeta });
} catch (error) {
await auditorEvent.fail({
error,
meta: auditorMeta
});
}
}
}
}
async removeRoles(providerRoles, tempEnforcer) {
const lowercasedProviderRoles = helper.transformRolesGroupToLowercase(providerRoles);
for (const role of lowercasedProviderRoles) {
if (!await tempEnforcer.hasGroupingPolicy(...role)) {
const roleMeta = await this.roleMetadataStorage.findRoleMetadata(
role[1]
);
const currentRole = await this.enforcer.getFilteredGroupingPolicy(
1,
role[1]
);
if (!roleMeta) {
this.logger.warn("role does not exist");
continue;
}
const singleRole = roleMeta && currentRole.length === 1;
const actionType = singleRole ? auditor.ActionType.DELETE : auditor.ActionType.UPDATE;
const auditorMeta = { ...roleMeta, members: [role[0]] };
const auditorEvent = await this.auditor.createEvent({
eventId: auditor.RoleEvents.ROLE_WRITE,
severityLevel: "medium",
meta: { actionType, source: roleMeta.source }
});
try {
await this.enforcer.removeGroupingPolicy(
role,
roleMeta,
actionType === auditor.ActionType.UPDATE
);
await auditorEvent.success({ meta: auditorMeta });
} catch (error) {
await auditorEvent.fail({
error,
meta: auditorMeta
});
}
}
}
}
async addPermissions(permissions) {
for (const permission of permissions) {
if (permission[1] === "policy-entity" && permission[2] === "create") {
this.logger.warn(
`Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${permission} to use 'policy.entity.create' instead of 'policy-entity' from source ${this.id}`
);
}
if (!await this.enforcer.hasPolicy(...permission)) {
const transformedPolicy = helper.transformArrayToPolicy(permission);
const metadata = await this.roleMetadataStorage.findRoleMetadata(
permission[0]
);
const auditorMeta = {
policies: [permission]
};
const auditorEvent = await this.auditor.createEvent({
eventId: auditor.PermissionEvents.POLICY_WRITE,
severityLevel: "medium",
meta: { actionType: auditor.ActionType.CREATE, source: this.id }
});
let err = policiesValidation.validatePolicy(transformedPolicy);
if (err) {
auditorEvent.fail({ error: err, meta: auditorMeta });
continue;
}
err = await policiesValidation.validateSource(this.id, metadata);
if (err) {
auditorEvent.fail({ error: err, meta: auditorMeta });
continue;
}
try {
await this.enforcer.addPolicy(permission);
await auditorEvent.success({ meta: auditorMeta });
} catch (error) {
await auditorEvent.fail({ error, meta: auditorMeta });
}
}
}
}
async removePermissions(providerPermissions, tempEnforcer) {
for (const permission of providerPermissions) {
if (!await tempEnforcer.hasPolicy(...permission)) {
const auditorMeta = {
policies: [permission]
};
const auditorEvent = await this.auditor?.createEvent({
eventId: auditor.PermissionEvents.POLICY_WRITE,
severityLevel: "medium",
meta: { actionType: auditor.ActionType.DELETE, source: this.id }
});
try {
await this.enforcer.removePolicy(permission);
await auditorEvent.success({ meta: auditorMeta });
} catch (error) {
await auditorEvent.fail({
error,
meta: auditorMeta
});
}
}
}
}
async getProviderRoles() {
const currentRoles = await this.roleMetadataStorage.filterRoleMetadata(
this.id
);
return currentRoles.map((meta) => meta.roleEntityRef);
}
}
async function connectRBACProviders(providers, enforcer, roleMetadataStorage, logger, auditor) {
await Promise.all(
providers.map(async (provider) => {
try {
const connection = new Connection(
provider.getProviderName(),
enforcer,
roleMetadataStorage,
logger,
auditor
);
return provider.connect(connection);
} catch (error) {
throw new Error(
`Unable to connect provider ${provider.getProviderName()}, ${error}`
);
}
})
);
}
exports.Connection = Connection;
exports.connectRBACProviders = connectRBACProviders;
//# sourceMappingURL=connect-providers.cjs.js.map