@backstage-community/plugin-rbac-backend 5.6.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/admin-permissions/admin-creation.cjs.js +41 -27
- package/dist/admin-permissions/admin-creation.cjs.js.map +1 -1
- package/dist/auditor/auditor.cjs.js +65 -0
- package/dist/auditor/auditor.cjs.js.map +1 -0
- package/dist/auditor/rest-interceptor.cjs.js +130 -0
- package/dist/auditor/rest-interceptor.cjs.js.map +1 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js +90 -92
- package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -1
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +40 -51
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -1
- package/dist/helper.cjs.js +22 -19
- package/dist/helper.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/plugin.cjs.js +3 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/policies/permission-policy.cjs.js +32 -70
- package/dist/policies/permission-policy.cjs.js.map +1 -1
- package/dist/providers/connect-providers.cjs.js +75 -68
- package/dist/providers/connect-providers.cjs.js.map +1 -1
- package/dist/service/enforcer-delegate.cjs.js +8 -10
- package/dist/service/enforcer-delegate.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +449 -519
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/dist/service/policy-builder.cjs.js +4 -10
- package/dist/service/policy-builder.cjs.js.map +1 -1
- package/package.json +1 -2
- package/dist/audit-log/audit-logger.cjs.js +0 -114
- package/dist/audit-log/audit-logger.cjs.js.map +0 -1
- package/dist/audit-log/rest-errors-interceptor.cjs.js +0 -100
- package/dist/audit-log/rest-errors-interceptor.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
### Dependencies
|
|
2
2
|
|
|
3
|
+
## 6.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 9cccb0d: **BREAKING**: Migration to the core Auditor service. The Auditor format has been updated. Audit fields and event names (ids) have been updated to conform with the new Auditor service conventions. Filtering queries based on the old format may no longer work.
|
|
8
|
+
|
|
3
9
|
## 5.6.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var auditor = require('../auditor/auditor.cjs.js');
|
|
4
4
|
var helper = require('../helper.cjs.js');
|
|
5
5
|
var policiesValidation = require('../validation/policies-validation.cjs.js');
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ const getAdminRoleMetadata = () => {
|
|
|
19
19
|
createdAt: currentDate.toUTCString()
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
-
const useAdminsFromConfig = async (admins, enf,
|
|
22
|
+
const useAdminsFromConfig = async (admins, enf, auditor$1, roleMetadataStorage, knex) => {
|
|
23
23
|
const addedGroupPolicies = /* @__PURE__ */ new Map();
|
|
24
24
|
const newGroupPolicies = /* @__PURE__ */ new Map();
|
|
25
25
|
for (const admin of admins) {
|
|
@@ -31,8 +31,20 @@ const useAdminsFromConfig = async (admins, enf, auditLogger$1, roleMetadataStora
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
const adminRoleMeta = await roleMetadataStorage.findRoleMetadata(ADMIN_ROLE_NAME);
|
|
34
|
+
const addedRoleMembers = Array.from(newGroupPolicies.entries());
|
|
35
|
+
const meta = {
|
|
36
|
+
...getAdminRoleMetadata(),
|
|
37
|
+
members: addedRoleMembers.map((gp) => gp[0])
|
|
38
|
+
};
|
|
39
|
+
const auditorEvent = await auditor$1.createEvent({
|
|
40
|
+
eventId: auditor.RoleEvents.ROLE_WRITE,
|
|
41
|
+
severityLevel: "medium",
|
|
42
|
+
meta: {
|
|
43
|
+
actionType: adminRoleMeta ? auditor.ActionType.UPDATE : auditor.ActionType.CREATE,
|
|
44
|
+
source: meta.source
|
|
45
|
+
}
|
|
46
|
+
});
|
|
34
47
|
const trx = await knex.transaction();
|
|
35
|
-
let addedRoleMembers;
|
|
36
48
|
try {
|
|
37
49
|
if (!adminRoleMeta) {
|
|
38
50
|
await roleMetadataStorage.createRoleMetadata(getAdminRoleMetadata(), trx);
|
|
@@ -43,28 +55,23 @@ const useAdminsFromConfig = async (admins, enf, auditLogger$1, roleMetadataStora
|
|
|
43
55
|
trx
|
|
44
56
|
);
|
|
45
57
|
}
|
|
46
|
-
addedRoleMembers = Array.from(newGroupPolicies.entries());
|
|
47
58
|
await enf.addGroupingPolicies(
|
|
48
59
|
addedRoleMembers,
|
|
49
60
|
getAdminRoleMetadata(),
|
|
50
61
|
trx
|
|
51
62
|
);
|
|
52
63
|
await trx.commit();
|
|
64
|
+
await auditorEvent.success({
|
|
65
|
+
meta
|
|
66
|
+
});
|
|
53
67
|
} catch (error) {
|
|
54
68
|
await trx.rollback(error);
|
|
69
|
+
await auditorEvent.fail({
|
|
70
|
+
error,
|
|
71
|
+
meta
|
|
72
|
+
});
|
|
55
73
|
throw error;
|
|
56
74
|
}
|
|
57
|
-
await auditLogger$1.auditLog({
|
|
58
|
-
actorId: auditLogger.RBAC_BACKEND,
|
|
59
|
-
message: `Created or updated role`,
|
|
60
|
-
eventName: auditLogger.RoleEvents.CREATE_OR_UPDATE_ROLE,
|
|
61
|
-
metadata: {
|
|
62
|
-
...getAdminRoleMetadata(),
|
|
63
|
-
members: addedRoleMembers.map((gp) => gp[0])
|
|
64
|
-
},
|
|
65
|
-
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
66
|
-
status: "succeeded"
|
|
67
|
-
});
|
|
68
75
|
const configGroupPolicies = await enf.getFilteredGroupingPolicy(
|
|
69
76
|
1,
|
|
70
77
|
ADMIN_ROLE_NAME
|
|
@@ -75,28 +82,35 @@ const useAdminsFromConfig = async (admins, enf, auditLogger$1, roleMetadataStora
|
|
|
75
82
|
"configuration",
|
|
76
83
|
ADMIN_ROLE_NAME,
|
|
77
84
|
enf,
|
|
78
|
-
|
|
85
|
+
auditor$1,
|
|
79
86
|
ADMIN_ROLE_AUTHOR
|
|
80
87
|
);
|
|
81
88
|
};
|
|
82
|
-
const addAdminPermissions = async (policies, enf,
|
|
89
|
+
const addAdminPermissions = async (policies, enf, auditor$1) => {
|
|
83
90
|
const policiesToAdd = [];
|
|
84
91
|
for (const policy of policies) {
|
|
85
92
|
if (!await enf.hasPolicy(...policy)) {
|
|
86
93
|
policiesToAdd.push(policy);
|
|
87
94
|
}
|
|
88
95
|
}
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
eventName: auditLogger.PermissionEvents.CREATE_POLICY,
|
|
94
|
-
metadata: { policies, source: "configuration" },
|
|
95
|
-
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
96
|
-
status: "succeeded"
|
|
96
|
+
const auditorEvent = await auditor$1.createEvent({
|
|
97
|
+
eventId: auditor.PermissionEvents.POLICY_WRITE,
|
|
98
|
+
severityLevel: "medium",
|
|
99
|
+
meta: { actionType: auditor.ActionType.CREATE, source: "configuration" }
|
|
97
100
|
});
|
|
101
|
+
try {
|
|
102
|
+
await enf.addPolicies(policiesToAdd);
|
|
103
|
+
await auditorEvent.success({
|
|
104
|
+
meta: { policies: policiesToAdd }
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
await auditorEvent.fail({
|
|
108
|
+
error,
|
|
109
|
+
meta: { policies: policiesToAdd }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
98
112
|
};
|
|
99
|
-
const setAdminPermissions = async (enf,
|
|
113
|
+
const setAdminPermissions = async (enf, auditor) => {
|
|
100
114
|
const adminPermissions = [
|
|
101
115
|
[ADMIN_ROLE_NAME, "policy-entity", "read", "allow"],
|
|
102
116
|
[ADMIN_ROLE_NAME, "policy-entity", "create", "allow"],
|
|
@@ -105,7 +119,7 @@ const setAdminPermissions = async (enf, auditLogger) => {
|
|
|
105
119
|
// Needed for the RBAC frontend plugin.
|
|
106
120
|
[ADMIN_ROLE_NAME, "catalog-entity", "read", "allow"]
|
|
107
121
|
];
|
|
108
|
-
await addAdminPermissions(adminPermissions, enf,
|
|
122
|
+
await addAdminPermissions(adminPermissions, enf, auditor);
|
|
109
123
|
};
|
|
110
124
|
|
|
111
125
|
exports.ADMIN_ROLE_AUTHOR = ADMIN_ROLE_AUTHOR;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-creation.cjs.js","sources":["../../src/admin-permissions/admin-creation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Config } from '@backstage/config';\n\nimport {
|
|
1
|
+
{"version":3,"file":"admin-creation.cjs.js","sources":["../../src/admin-permissions/admin-creation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { Config } from '@backstage/config';\n\nimport { Knex } from 'knex';\n\nimport { ActionType, PermissionEvents, RoleEvents } from '../auditor/auditor';\n\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport { removeTheDifference } from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { validateEntityReference } from '../validation/policies-validation';\nimport { AuditorService } from '@backstage/backend-plugin-api';\n\nexport const ADMIN_ROLE_NAME = 'role:default/rbac_admin';\nexport const ADMIN_ROLE_AUTHOR = 'application configuration';\nconst DEF_ADMIN_ROLE_DESCRIPTION =\n 'The default permission policy for the admin role allows for the creation, deletion, updating, and reading of roles and permission policies.';\n\nconst getAdminRoleMetadata = (): RoleMetadataDao => {\n const currentDate: Date = new Date();\n return {\n source: 'configuration',\n roleEntityRef: ADMIN_ROLE_NAME,\n description: DEF_ADMIN_ROLE_DESCRIPTION,\n author: ADMIN_ROLE_AUTHOR,\n modifiedBy: ADMIN_ROLE_AUTHOR,\n lastModified: currentDate.toUTCString(),\n createdAt: currentDate.toUTCString(),\n };\n};\n\nexport const useAdminsFromConfig = async (\n admins: Config[],\n enf: EnforcerDelegate,\n auditor: AuditorService,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n) => {\n const addedGroupPolicies = new Map<string, string>();\n const newGroupPolicies = new Map<string, string>();\n\n for (const admin of admins) {\n const entityRef = admin.getString('name').toLocaleLowerCase('en-US');\n validateEntityReference(entityRef);\n\n addedGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n\n if (!(await enf.hasGroupingPolicy(...[entityRef, ADMIN_ROLE_NAME]))) {\n newGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n }\n }\n\n const adminRoleMeta =\n await roleMetadataStorage.findRoleMetadata(ADMIN_ROLE_NAME);\n const addedRoleMembers = Array.from<string[]>(newGroupPolicies.entries());\n const meta = {\n ...getAdminRoleMetadata(),\n members: addedRoleMembers.map(gp => gp[0]),\n };\n const auditorEvent = await auditor.createEvent({\n eventId: RoleEvents.ROLE_WRITE,\n severityLevel: 'medium',\n meta: {\n actionType: adminRoleMeta ? ActionType.UPDATE : ActionType.CREATE,\n source: meta.source,\n },\n });\n\n const trx = await knex.transaction();\n try {\n if (!adminRoleMeta) {\n // even if there are no user, we still create default role metadata for admins\n await roleMetadataStorage.createRoleMetadata(getAdminRoleMetadata(), trx);\n } else if (adminRoleMeta.source === 'legacy') {\n await roleMetadataStorage.updateRoleMetadata(\n getAdminRoleMetadata(),\n ADMIN_ROLE_NAME,\n trx,\n );\n }\n\n await enf.addGroupingPolicies(\n addedRoleMembers,\n getAdminRoleMetadata(),\n trx,\n );\n\n await trx.commit();\n await auditorEvent.success({\n meta,\n });\n } catch (error) {\n await trx.rollback(error);\n await auditorEvent.fail({\n error,\n meta,\n });\n throw error;\n }\n\n const configGroupPolicies = await enf.getFilteredGroupingPolicy(\n 1,\n ADMIN_ROLE_NAME,\n );\n\n await removeTheDifference(\n configGroupPolicies.map(gp => gp[0]),\n Array.from<string>(addedGroupPolicies.keys()),\n 'configuration',\n ADMIN_ROLE_NAME,\n enf,\n auditor,\n ADMIN_ROLE_AUTHOR,\n );\n};\n\nconst addAdminPermissions = async (\n policies: string[][],\n enf: EnforcerDelegate,\n auditor: AuditorService,\n) => {\n const policiesToAdd: string[][] = [];\n for (const policy of policies) {\n if (!(await enf.hasPolicy(...policy))) {\n policiesToAdd.push(policy);\n }\n }\n\n const auditorEvent = await auditor.createEvent({\n eventId: PermissionEvents.POLICY_WRITE,\n severityLevel: 'medium',\n meta: { actionType: ActionType.CREATE, source: 'configuration' },\n });\n\n try {\n await enf.addPolicies(policiesToAdd);\n await auditorEvent.success({\n meta: { policies: policiesToAdd },\n });\n } catch (error) {\n await auditorEvent.fail({\n error,\n meta: { policies: policiesToAdd },\n });\n }\n};\n\nexport const setAdminPermissions = async (\n enf: EnforcerDelegate,\n auditor: AuditorService,\n) => {\n const adminPermissions = [\n [ADMIN_ROLE_NAME, 'policy-entity', 'read', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'create', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'delete', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'update', 'allow'],\n // Needed for the RBAC frontend plugin.\n [ADMIN_ROLE_NAME, 'catalog-entity', 'read', 'allow'],\n ];\n await addAdminPermissions(adminPermissions, enf, auditor);\n};\n"],"names":["auditor","validateEntityReference","RoleEvents","ActionType","removeTheDifference","PermissionEvents"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
4
|
+
|
|
5
|
+
const ActionType = {
|
|
6
|
+
CREATE: "create",
|
|
7
|
+
CREATE_OR_UPDATE: "create_or_update",
|
|
8
|
+
UPDATE: "update",
|
|
9
|
+
DELETE: "delete"
|
|
10
|
+
};
|
|
11
|
+
const RoleEvents = {
|
|
12
|
+
ROLE_WRITE: "role-write",
|
|
13
|
+
ROLE_READ: "role-read"
|
|
14
|
+
};
|
|
15
|
+
const PermissionEvents = {
|
|
16
|
+
POLICY_WRITE: "policy-write",
|
|
17
|
+
POLICY_READ: "policy-read"
|
|
18
|
+
};
|
|
19
|
+
const EvaluationEvents = {
|
|
20
|
+
PERMISSION_EVALUATION: "permission-evaluation"
|
|
21
|
+
};
|
|
22
|
+
const ListPluginPoliciesEvents = {
|
|
23
|
+
PLUGIN_POLICIES_READ: "plugin-policies-read"
|
|
24
|
+
};
|
|
25
|
+
const ListConditionEvents = {
|
|
26
|
+
CONDITION_RULES_READ: "condition-rules-read"
|
|
27
|
+
};
|
|
28
|
+
const PoliciesData = {
|
|
29
|
+
PERMISSIONS_READ: "permissions-read"
|
|
30
|
+
};
|
|
31
|
+
const ConditionEvents = {
|
|
32
|
+
CONDITION_WRITE: "condition-write",
|
|
33
|
+
CONDITION_READ: "condition-read",
|
|
34
|
+
CONDITIONAL_POLICIES_FILE_NOT_FOUND: "conditional-policies-file-not-found",
|
|
35
|
+
CONDITIONAL_POLICIES_FILE_CHANGE: "conditional-policies-file-change"
|
|
36
|
+
};
|
|
37
|
+
async function createPermissionEvaluationAuditorEvent(auditor, userEntityRef, request, policyDecision) {
|
|
38
|
+
const auditInfo = {
|
|
39
|
+
userEntityRef,
|
|
40
|
+
permissionName: request.permission.name,
|
|
41
|
+
action: pluginRbacCommon.toPermissionAction(request.permission.attributes)
|
|
42
|
+
};
|
|
43
|
+
const resourceType = request.permission.resourceType;
|
|
44
|
+
if (resourceType) {
|
|
45
|
+
auditInfo.resourceType = resourceType;
|
|
46
|
+
}
|
|
47
|
+
return await auditor.createEvent({
|
|
48
|
+
eventId: EvaluationEvents.PERMISSION_EVALUATION,
|
|
49
|
+
severityLevel: "medium",
|
|
50
|
+
meta: {
|
|
51
|
+
...auditInfo
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
exports.ActionType = ActionType;
|
|
57
|
+
exports.ConditionEvents = ConditionEvents;
|
|
58
|
+
exports.EvaluationEvents = EvaluationEvents;
|
|
59
|
+
exports.ListConditionEvents = ListConditionEvents;
|
|
60
|
+
exports.ListPluginPoliciesEvents = ListPluginPoliciesEvents;
|
|
61
|
+
exports.PermissionEvents = PermissionEvents;
|
|
62
|
+
exports.PoliciesData = PoliciesData;
|
|
63
|
+
exports.RoleEvents = RoleEvents;
|
|
64
|
+
exports.createPermissionEvaluationAuditorEvent = createPermissionEvaluationAuditorEvent;
|
|
65
|
+
//# sourceMappingURL=auditor.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditor.cjs.js","sources":["../../src/auditor/auditor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type { PolicyQuery } from '@backstage/plugin-permission-node';\n\nimport {\n PermissionAction,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\n\nexport const ActionType = {\n CREATE: 'create',\n CREATE_OR_UPDATE: 'create_or_update',\n UPDATE: 'update',\n DELETE: 'delete',\n};\n\nexport const RoleEvents = {\n ROLE_WRITE: 'role-write',\n ROLE_READ: 'role-read',\n} as const;\n\nexport const PermissionEvents = {\n POLICY_WRITE: 'policy-write',\n POLICY_READ: 'policy-read',\n} as const;\n\nexport const EvaluationEvents = {\n PERMISSION_EVALUATION: 'permission-evaluation',\n} as const;\n\nexport const ListPluginPoliciesEvents = {\n PLUGIN_POLICIES_READ: 'plugin-policies-read',\n};\n\nexport const ListConditionEvents = {\n CONDITION_RULES_READ: 'condition-rules-read',\n};\n\nexport type EvaluationAuditInfo = {\n userEntityRef: string;\n permissionName: string;\n action: PermissionAction;\n resourceType?: string;\n decision?: PolicyDecision;\n};\n\nexport const PoliciesData = {\n PERMISSIONS_READ: 'permissions-read',\n};\n\nexport const ConditionEvents = {\n CONDITION_WRITE: 'condition-write',\n CONDITION_READ: 'condition-read',\n CONDITIONAL_POLICIES_FILE_NOT_FOUND: 'conditional-policies-file-not-found',\n CONDITIONAL_POLICIES_FILE_CHANGE: 'conditional-policies-file-change',\n};\n\nexport async function createPermissionEvaluationAuditorEvent(\n auditor: AuditorService,\n userEntityRef: string,\n request: PolicyQuery,\n policyDecision?: PolicyDecision,\n): Promise<AuditorServiceEvent> {\n const auditInfo: EvaluationAuditInfo = {\n userEntityRef,\n permissionName: request.permission.name,\n action: toPermissionAction(request.permission.attributes),\n };\n\n const resourceType = (request.permission as ResourcePermission).resourceType;\n if (resourceType) {\n auditInfo.resourceType = resourceType;\n }\n if (policyDecision) {\n auditInfo.decision = policyDecision;\n }\n\n return await auditor.createEvent({\n eventId: EvaluationEvents.PERMISSION_EVALUATION,\n severityLevel: 'medium',\n meta: {\n ...auditInfo,\n },\n });\n}\n"],"names":["toPermissionAction"],"mappings":";;;;AA8BO,MAAM,UAAa,GAAA;AAAA,EACxB,MAAQ,EAAA,QAAA;AAAA,EACR,gBAAkB,EAAA,kBAAA;AAAA,EAClB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA;AACV;AAEO,MAAM,UAAa,GAAA;AAAA,EACxB,UAAY,EAAA,YAAA;AAAA,EACZ,SAAW,EAAA;AACb;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,YAAc,EAAA,cAAA;AAAA,EACd,WAAa,EAAA;AACf;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,qBAAuB,EAAA;AACzB;AAEO,MAAM,wBAA2B,GAAA;AAAA,EACtC,oBAAsB,EAAA;AACxB;AAEO,MAAM,mBAAsB,GAAA;AAAA,EACjC,oBAAsB,EAAA;AACxB;AAUO,MAAM,YAAe,GAAA;AAAA,EAC1B,gBAAkB,EAAA;AACpB;AAEO,MAAM,eAAkB,GAAA;AAAA,EAC7B,eAAiB,EAAA,iBAAA;AAAA,EACjB,cAAgB,EAAA,gBAAA;AAAA,EAChB,mCAAqC,EAAA,qCAAA;AAAA,EACrC,gCAAkC,EAAA;AACpC;AAEA,eAAsB,sCACpB,CAAA,OAAA,EACA,aACA,EAAA,OAAA,EACA,cAC8B,EAAA;AAC9B,EAAA,MAAM,SAAiC,GAAA;AAAA,IACrC,aAAA;AAAA,IACA,cAAA,EAAgB,QAAQ,UAAW,CAAA,IAAA;AAAA,IACnC,MAAQ,EAAAA,mCAAA,CAAmB,OAAQ,CAAA,UAAA,CAAW,UAAU;AAAA,GAC1D;AAEA,EAAM,MAAA,YAAA,GAAgB,QAAQ,UAAkC,CAAA,YAAA;AAChE,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,SAAA,CAAU,YAAe,GAAA,YAAA;AAAA;AAM3B,EAAO,OAAA,MAAM,QAAQ,WAAY,CAAA;AAAA,IAC/B,SAAS,gBAAiB,CAAA,qBAAA;AAAA,IAC1B,aAAe,EAAA,QAAA;AAAA,IACf,IAAM,EAAA;AAAA,MACJ,GAAG;AAAA;AACL,GACD,CAAA;AACH;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var auditor = require('./auditor.cjs.js');
|
|
4
|
+
|
|
5
|
+
const eventMap = {
|
|
6
|
+
"/policies": {
|
|
7
|
+
POST: auditor.PermissionEvents.POLICY_WRITE,
|
|
8
|
+
PUT: auditor.PermissionEvents.POLICY_WRITE,
|
|
9
|
+
DELETE: auditor.PermissionEvents.POLICY_WRITE,
|
|
10
|
+
GET: auditor.PermissionEvents.POLICY_READ
|
|
11
|
+
},
|
|
12
|
+
"/roles/conditions": {
|
|
13
|
+
POST: auditor.ConditionEvents.CONDITION_WRITE,
|
|
14
|
+
PUT: auditor.ConditionEvents.CONDITION_WRITE,
|
|
15
|
+
DELETE: auditor.ConditionEvents.CONDITION_WRITE,
|
|
16
|
+
GET: auditor.ConditionEvents.CONDITION_READ
|
|
17
|
+
},
|
|
18
|
+
"/roles": {
|
|
19
|
+
POST: auditor.RoleEvents.ROLE_WRITE,
|
|
20
|
+
PUT: auditor.RoleEvents.ROLE_WRITE,
|
|
21
|
+
DELETE: auditor.RoleEvents.ROLE_WRITE,
|
|
22
|
+
GET: auditor.RoleEvents.ROLE_READ
|
|
23
|
+
},
|
|
24
|
+
"/plugins/policies": {
|
|
25
|
+
GET: auditor.ListPluginPoliciesEvents.PLUGIN_POLICIES_READ
|
|
26
|
+
},
|
|
27
|
+
"/plugins/condition-rules": {
|
|
28
|
+
GET: auditor.ListConditionEvents.CONDITION_RULES_READ
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const eventToActionMap = {
|
|
32
|
+
POST: auditor.ActionType.CREATE,
|
|
33
|
+
PUT: auditor.ActionType.UPDATE,
|
|
34
|
+
DELETE: auditor.ActionType.DELETE
|
|
35
|
+
};
|
|
36
|
+
function getRequestAuditorMeta(req, eventId) {
|
|
37
|
+
const meta = {
|
|
38
|
+
...req.method in eventToActionMap ? { actionType: eventToActionMap[req.method] } : {},
|
|
39
|
+
source: "rest"
|
|
40
|
+
};
|
|
41
|
+
if (req.method !== "GET") {
|
|
42
|
+
return meta;
|
|
43
|
+
}
|
|
44
|
+
let extraMeta = {};
|
|
45
|
+
const hasQuery = Object.keys(req.query).length > 0;
|
|
46
|
+
const hasParams = Object.keys(req.params).length > 0;
|
|
47
|
+
switch (eventId) {
|
|
48
|
+
case auditor.PermissionEvents.POLICY_READ:
|
|
49
|
+
if (hasParams) {
|
|
50
|
+
extraMeta = {
|
|
51
|
+
queryType: "by-role",
|
|
52
|
+
entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`
|
|
53
|
+
};
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
extraMeta = {
|
|
57
|
+
queryType: hasQuery ? "by-query" : "all",
|
|
58
|
+
...hasQuery ? { query: req.query } : {}
|
|
59
|
+
};
|
|
60
|
+
break;
|
|
61
|
+
case auditor.RoleEvents.ROLE_READ:
|
|
62
|
+
extraMeta = {
|
|
63
|
+
queryType: hasParams ? "by-role" : "all",
|
|
64
|
+
...hasParams ? {
|
|
65
|
+
entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`
|
|
66
|
+
} : {}
|
|
67
|
+
};
|
|
68
|
+
break;
|
|
69
|
+
case auditor.ConditionEvents.CONDITION_READ:
|
|
70
|
+
if (hasParams) {
|
|
71
|
+
extraMeta = {
|
|
72
|
+
queryType: "by-id",
|
|
73
|
+
id: req.params.id
|
|
74
|
+
};
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
extraMeta = {
|
|
78
|
+
queryType: hasQuery ? "by-query" : "all",
|
|
79
|
+
...hasQuery ? { query: req.query } : {}
|
|
80
|
+
};
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return { ...meta, ...extraMeta };
|
|
84
|
+
}
|
|
85
|
+
function logAuditorEvent(auditor) {
|
|
86
|
+
return async (req, resp, next) => {
|
|
87
|
+
let auditorEvent;
|
|
88
|
+
const matchedPath = Object.keys(eventMap).find(
|
|
89
|
+
(path) => req.path.startsWith(path)
|
|
90
|
+
);
|
|
91
|
+
if (matchedPath) {
|
|
92
|
+
const methodEvent = eventMap[matchedPath][req.method];
|
|
93
|
+
if (methodEvent) {
|
|
94
|
+
const meta = getRequestAuditorMeta(req, methodEvent);
|
|
95
|
+
auditorEvent = await auditor.createEvent({
|
|
96
|
+
eventId: methodEvent,
|
|
97
|
+
severityLevel: "medium",
|
|
98
|
+
request: req,
|
|
99
|
+
meta
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
resp.on("finish", async () => {
|
|
104
|
+
const meta = {
|
|
105
|
+
response: { status: resp.statusCode },
|
|
106
|
+
...resp.locals.meta ?? {}
|
|
107
|
+
};
|
|
108
|
+
if (resp.statusCode < 400) {
|
|
109
|
+
await auditorEvent?.success({ meta });
|
|
110
|
+
} else {
|
|
111
|
+
const error = resp.locals.error ?? new Error(resp.statusMessage);
|
|
112
|
+
await auditorEvent?.fail({
|
|
113
|
+
error,
|
|
114
|
+
meta
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
next();
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function setAuditorError() {
|
|
122
|
+
return async (err, _req, resp, next) => {
|
|
123
|
+
resp.locals.error = err;
|
|
124
|
+
next(err);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exports.logAuditorEvent = logAuditorEvent;
|
|
129
|
+
exports.setAuditorError = setAuditorError;
|
|
130
|
+
//# sourceMappingURL=rest-interceptor.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest-interceptor.cjs.js","sources":["../../src/auditor/rest-interceptor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n type RequestHandler,\n type NextFunction,\n type Request,\n type Response,\n type ErrorRequestHandler,\n} from 'express';\n\nimport {\n ActionType,\n ConditionEvents,\n ListConditionEvents,\n ListPluginPoliciesEvents,\n PermissionEvents,\n RoleEvents,\n} from './auditor';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\nimport type { JsonObject } from '@backstage/types';\n\n// Mapping paths and methods to corresponding events and messages\nconst eventMap: {\n [key: string]: { [key: string]: string };\n} = {\n '/policies': {\n POST: PermissionEvents.POLICY_WRITE,\n PUT: PermissionEvents.POLICY_WRITE,\n DELETE: PermissionEvents.POLICY_WRITE,\n GET: PermissionEvents.POLICY_READ,\n },\n '/roles/conditions': {\n POST: ConditionEvents.CONDITION_WRITE,\n PUT: ConditionEvents.CONDITION_WRITE,\n DELETE: ConditionEvents.CONDITION_WRITE,\n GET: ConditionEvents.CONDITION_READ,\n },\n '/roles': {\n POST: RoleEvents.ROLE_WRITE,\n PUT: RoleEvents.ROLE_WRITE,\n DELETE: RoleEvents.ROLE_WRITE,\n GET: RoleEvents.ROLE_READ,\n },\n '/plugins/policies': {\n GET: ListPluginPoliciesEvents.PLUGIN_POLICIES_READ,\n },\n '/plugins/condition-rules': {\n GET: ListConditionEvents.CONDITION_RULES_READ,\n },\n};\n\nconst eventToActionMap: {\n [key: string]: string;\n} = {\n POST: ActionType.CREATE,\n PUT: ActionType.UPDATE,\n DELETE: ActionType.DELETE,\n};\n\nfunction getRequestAuditorMeta(req: Request, eventId: string): JsonObject {\n const meta = {\n ...(req.method in eventToActionMap\n ? { actionType: eventToActionMap[req.method] }\n : {}),\n source: 'rest',\n };\n\n if (req.method !== 'GET') {\n return meta;\n }\n\n let extraMeta = {};\n const hasQuery = Object.keys(req.query).length > 0;\n const hasParams = Object.keys(req.params).length > 0;\n switch (eventId) {\n case PermissionEvents.POLICY_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-role',\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n case RoleEvents.ROLE_READ:\n extraMeta = {\n queryType: hasParams ? 'by-role' : 'all',\n ...(hasParams\n ? {\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n }\n : {}),\n };\n break;\n case ConditionEvents.CONDITION_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-id',\n id: req.params.id,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n default:\n break;\n }\n return { ...meta, ...extraMeta };\n}\n\nexport function logAuditorEvent(auditor: AuditorService): RequestHandler {\n return async (req: Request, resp: Response, next: NextFunction) => {\n let auditorEvent: AuditorServiceEvent | undefined;\n const matchedPath = Object.keys(eventMap).find(path =>\n req.path.startsWith(path),\n );\n if (matchedPath) {\n const methodEvent = eventMap[matchedPath][req.method];\n if (methodEvent) {\n const meta = getRequestAuditorMeta(req, methodEvent);\n auditorEvent = await auditor.createEvent({\n eventId: methodEvent,\n severityLevel: 'medium',\n request: req,\n meta,\n });\n }\n }\n\n resp.on('finish', async () => {\n const meta = {\n response: { status: resp.statusCode },\n ...(resp.locals.meta ?? {}),\n };\n if (resp.statusCode < 400) {\n await auditorEvent?.success({ meta });\n } else {\n const error = resp.locals.error ?? new Error(resp.statusMessage);\n await auditorEvent?.fail({\n error,\n meta,\n });\n }\n });\n\n next();\n };\n}\n\nexport function setAuditorError(): ErrorRequestHandler {\n return async (\n err: Error,\n _req: Request,\n resp: Response,\n next: NextFunction,\n ) => {\n resp.locals.error = err;\n next(err);\n };\n}\n"],"names":["PermissionEvents","ConditionEvents","RoleEvents","ListPluginPoliciesEvents","ListConditionEvents","ActionType"],"mappings":"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"}
|