@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.
Files changed (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/admin-permissions/admin-creation.cjs.js +41 -27
  3. package/dist/admin-permissions/admin-creation.cjs.js.map +1 -1
  4. package/dist/auditor/auditor.cjs.js +65 -0
  5. package/dist/auditor/auditor.cjs.js.map +1 -0
  6. package/dist/auditor/rest-interceptor.cjs.js +130 -0
  7. package/dist/auditor/rest-interceptor.cjs.js.map +1 -0
  8. package/dist/file-permissions/csv-file-watcher.cjs.js +90 -92
  9. package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -1
  10. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +40 -51
  11. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -1
  12. package/dist/helper.cjs.js +22 -19
  13. package/dist/helper.cjs.js.map +1 -1
  14. package/dist/index.d.ts +2 -1
  15. package/dist/plugin.cjs.js +3 -0
  16. package/dist/plugin.cjs.js.map +1 -1
  17. package/dist/policies/permission-policy.cjs.js +32 -70
  18. package/dist/policies/permission-policy.cjs.js.map +1 -1
  19. package/dist/providers/connect-providers.cjs.js +75 -68
  20. package/dist/providers/connect-providers.cjs.js.map +1 -1
  21. package/dist/service/enforcer-delegate.cjs.js +8 -10
  22. package/dist/service/enforcer-delegate.cjs.js.map +1 -1
  23. package/dist/service/policies-rest-api.cjs.js +449 -519
  24. package/dist/service/policies-rest-api.cjs.js.map +1 -1
  25. package/dist/service/policy-builder.cjs.js +4 -10
  26. package/dist/service/policy-builder.cjs.js.map +1 -1
  27. package/package.json +1 -2
  28. package/dist/audit-log/audit-logger.cjs.js +0 -114
  29. package/dist/audit-log/audit-logger.cjs.js.map +0 -1
  30. package/dist/audit-log/rest-errors-interceptor.cjs.js +0 -100
  31. 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 auditLogger = require('../audit-log/audit-logger.cjs.js');
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, auditLogger$1, roleMetadataStorage, knex) => {
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
- auditLogger$1,
85
+ auditor$1,
79
86
  ADMIN_ROLE_AUTHOR
80
87
  );
81
88
  };
82
- const addAdminPermissions = async (policies, enf, auditLogger$1) => {
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 enf.addPolicies(policiesToAdd);
90
- await auditLogger$1.auditLog({
91
- actorId: auditLogger.RBAC_BACKEND,
92
- message: `Created RBAC admin permissions`,
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, auditLogger) => {
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, auditLogger);
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 { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Knex } from 'knex';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\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';\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 auditLogger: AuditLogger,\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\n const trx = await knex.transaction();\n let addedRoleMembers;\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 addedRoleMembers = Array.from<string[]>(newGroupPolicies.entries());\n await enf.addGroupingPolicies(\n addedRoleMembers,\n getAdminRoleMetadata(),\n trx,\n );\n\n await trx.commit();\n } catch (error) {\n await trx.rollback(error);\n throw error;\n }\n\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created or updated role`,\n eventName: RoleEvents.CREATE_OR_UPDATE_ROLE,\n metadata: {\n ...getAdminRoleMetadata(),\n members: addedRoleMembers.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\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 auditLogger,\n ADMIN_ROLE_AUTHOR,\n );\n};\n\nconst addAdminPermissions = async (\n policies: string[][],\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n) => {\n const policiesToAdd: string[][] = [];\n for (const policy of policies) {\n if (!(await enf.hasPolicy(...policy))) {\n policiesToAdd.push(policy);\n }\n }\n await enf.addPolicies(policiesToAdd);\n\n await auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created RBAC admin permissions`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: policies, source: 'configuration' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n};\n\nexport const setAdminPermissions = async (\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\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, auditLogger);\n};\n"],"names":["auditLogger","validateEntityReference","RBAC_BACKEND","RoleEvents","HANDLE_RBAC_DATA_STAGE","removeTheDifference","PermissionEvents"],"mappings":"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"}
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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"}