@backstage-community/plugin-rbac-backend 5.2.3
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 +671 -0
- package/README.md +220 -0
- package/config.d.ts +68 -0
- package/dist/admin-permissions/admin-creation.cjs.js +117 -0
- package/dist/admin-permissions/admin-creation.cjs.js.map +1 -0
- package/dist/audit-log/audit-logger.cjs.js +108 -0
- package/dist/audit-log/audit-logger.cjs.js.map +1 -0
- package/dist/audit-log/rest-errors-interceptor.cjs.js +100 -0
- package/dist/audit-log/rest-errors-interceptor.cjs.js.map +1 -0
- package/dist/conditional-aliases/alias-resolver.cjs.js +76 -0
- package/dist/conditional-aliases/alias-resolver.cjs.js.map +1 -0
- package/dist/database/casbin-adapter-factory.cjs.js +87 -0
- package/dist/database/casbin-adapter-factory.cjs.js.map +1 -0
- package/dist/database/conditional-storage.cjs.js +172 -0
- package/dist/database/conditional-storage.cjs.js.map +1 -0
- package/dist/database/migration.cjs.js +21 -0
- package/dist/database/migration.cjs.js.map +1 -0
- package/dist/database/role-metadata.cjs.js +89 -0
- package/dist/database/role-metadata.cjs.js.map +1 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js +407 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -0
- package/dist/file-permissions/file-watcher.cjs.js +46 -0
- package/dist/file-permissions/file-watcher.cjs.js.map +1 -0
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +208 -0
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -0
- package/dist/helper.cjs.js +171 -0
- package/dist/helper.cjs.js.map +1 -0
- package/dist/index.cjs.js +14 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/plugin.cjs.js +79 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/policies/allow-all-policy.cjs.js +12 -0
- package/dist/policies/allow-all-policy.cjs.js.map +1 -0
- package/dist/policies/permission-policy.cjs.js +243 -0
- package/dist/policies/permission-policy.cjs.js.map +1 -0
- package/dist/providers/connect-providers.cjs.js +211 -0
- package/dist/providers/connect-providers.cjs.js.map +1 -0
- package/dist/role-manager/ancestor-search-memo.cjs.js +159 -0
- package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -0
- package/dist/role-manager/member-list.cjs.js +101 -0
- package/dist/role-manager/member-list.cjs.js.map +1 -0
- package/dist/role-manager/role-manager.cjs.js +281 -0
- package/dist/role-manager/role-manager.cjs.js.map +1 -0
- package/dist/service/enforcer-delegate.cjs.js +353 -0
- package/dist/service/enforcer-delegate.cjs.js.map +1 -0
- package/dist/service/permission-model.cjs.js +21 -0
- package/dist/service/permission-model.cjs.js.map +1 -0
- package/dist/service/plugin-endpoints.cjs.js +121 -0
- package/dist/service/plugin-endpoints.cjs.js.map +1 -0
- package/dist/service/policies-rest-api.cjs.js +949 -0
- package/dist/service/policies-rest-api.cjs.js.map +1 -0
- package/dist/service/policy-builder.cjs.js +134 -0
- package/dist/service/policy-builder.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +24 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/dist/validation/condition-validation.cjs.js +107 -0
- package/dist/validation/condition-validation.cjs.js.map +1 -0
- package/dist/validation/policies-validation.cjs.js +194 -0
- package/dist/validation/policies-validation.cjs.js.map +1 -0
- package/migrations/20231015161232_migrations.js +41 -0
- package/migrations/20231212224526_migrations.js +84 -0
- package/migrations/20231221113214_migrations.js +60 -0
- package/migrations/20240201144429_migrations.js +37 -0
- package/migrations/20240215154456_migrations.js +143 -0
- package/migrations/20240308134410_migrations.js +31 -0
- package/migrations/20240308134941_migrations.js +43 -0
- package/migrations/20240404111242_migrations.js +53 -0
- package/migrations/20240611092136_migrations.js +29 -0
- package/package.json +98 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
+
var errors = require('@backstage/errors');
|
|
5
|
+
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
6
|
+
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
7
|
+
|
|
8
|
+
const validateSource = async (source, roleMetadata) => {
|
|
9
|
+
if (!roleMetadata) {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
if (roleMetadata.source !== source && roleMetadata.source !== "legacy") {
|
|
13
|
+
return new Error(
|
|
14
|
+
`source does not match originating role ${roleMetadata.roleEntityRef}, consider making changes to the '${roleMetadata.source.toLocaleUpperCase()}'`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
return void 0;
|
|
18
|
+
};
|
|
19
|
+
function validatePolicy(policy) {
|
|
20
|
+
const err = validateEntityReference(policy.entityReference);
|
|
21
|
+
if (err) {
|
|
22
|
+
return err;
|
|
23
|
+
}
|
|
24
|
+
if (!policy.permission) {
|
|
25
|
+
return new Error(`'permission' field must not be empty`);
|
|
26
|
+
}
|
|
27
|
+
if (!policy.policy) {
|
|
28
|
+
return new Error(`'policy' field must not be empty`);
|
|
29
|
+
} else if (!pluginRbacCommon.isValidPermissionAction(policy.policy)) {
|
|
30
|
+
return new Error(
|
|
31
|
+
`'policy' has invalid value: '${policy.policy}'. It should be one of: ${pluginRbacCommon.PermissionActionValues.join(", ")}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (!policy.effect) {
|
|
35
|
+
return new Error(`'effect' field must not be empty`);
|
|
36
|
+
} else if (!isValidEffectValue(policy.effect)) {
|
|
37
|
+
return new Error(
|
|
38
|
+
`'effect' has invalid value: '${policy.effect}'. It should be: '${pluginPermissionCommon.AuthorizeResult.ALLOW.toLocaleLowerCase()}' or '${pluginPermissionCommon.AuthorizeResult.DENY.toLocaleLowerCase()}'`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
function validateRole(role) {
|
|
44
|
+
if (!role.name) {
|
|
45
|
+
return new Error(`'name' field must not be empty`);
|
|
46
|
+
}
|
|
47
|
+
let err = validateEntityReference(role.name, true);
|
|
48
|
+
if (err) {
|
|
49
|
+
return err;
|
|
50
|
+
}
|
|
51
|
+
if (!role.memberReferences || role.memberReferences.length === 0) {
|
|
52
|
+
return new Error(`'memberReferences' field must not be empty`);
|
|
53
|
+
}
|
|
54
|
+
for (const member of role.memberReferences) {
|
|
55
|
+
err = validateEntityReference(member);
|
|
56
|
+
if (err) {
|
|
57
|
+
return err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
function isValidEffectValue(effect) {
|
|
63
|
+
return effect === pluginPermissionCommon.AuthorizeResult.ALLOW.toLocaleLowerCase() || effect === pluginPermissionCommon.AuthorizeResult.DENY.toLocaleLowerCase();
|
|
64
|
+
}
|
|
65
|
+
function isValidEntityName(name) {
|
|
66
|
+
const validNamePattern = /^[a-zA-Z0-9]+([._-][a-zA-Z0-9]+)*$/;
|
|
67
|
+
return validNamePattern.test(name) && name.length <= 63;
|
|
68
|
+
}
|
|
69
|
+
function isValidEntityNamespace(namespace) {
|
|
70
|
+
const validNamespacePattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
71
|
+
return validNamespacePattern.test(namespace) && namespace.length <= 63;
|
|
72
|
+
}
|
|
73
|
+
function validateEntityReference(entityRef, role) {
|
|
74
|
+
if (!entityRef) {
|
|
75
|
+
return new Error(`'entityReference' must not be empty`);
|
|
76
|
+
}
|
|
77
|
+
let entityRefCompound;
|
|
78
|
+
try {
|
|
79
|
+
entityRefCompound = catalogModel.parseEntityRef(entityRef);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
return err;
|
|
82
|
+
}
|
|
83
|
+
const entityRefFull = `${entityRefCompound.kind}:${entityRefCompound.namespace}/${entityRefCompound.name}`;
|
|
84
|
+
if (entityRefFull !== entityRef) {
|
|
85
|
+
return new Error(
|
|
86
|
+
`entity reference '${entityRef}' does not match the required format [<kind>:][<namespace>/]<name>. Provide, please, full entity reference.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (role && entityRefCompound.kind !== "role") {
|
|
90
|
+
return new Error(
|
|
91
|
+
`Unsupported kind ${entityRefCompound.kind}. Supported value should be "role"`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (entityRefCompound.kind !== "user" && entityRefCompound.kind !== "group" && entityRefCompound.kind !== "role") {
|
|
95
|
+
return new Error(
|
|
96
|
+
`Unsupported kind ${entityRefCompound.kind}. List supported values ["user", "group", "role"]`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (!isValidEntityName(entityRefCompound.name)) {
|
|
100
|
+
return new Error(
|
|
101
|
+
`The name '${entityRefCompound.name}' in the entity reference must be a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (!isValidEntityNamespace(entityRefCompound.namespace)) {
|
|
105
|
+
return new Error(
|
|
106
|
+
`The namespace '${entityRefCompound.namespace}' in the entity reference must be a string that is sequences of [a-z0-9] separated by [-], at most 63 characters in total`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
async function validateGroupingPolicy(groupPolicy, roleMetadataStorage, source) {
|
|
112
|
+
if (groupPolicy.length !== 2) {
|
|
113
|
+
return new Error(`Group policy should have length 2`);
|
|
114
|
+
}
|
|
115
|
+
const member = groupPolicy[0];
|
|
116
|
+
let err = validateEntityReference(member);
|
|
117
|
+
if (err) {
|
|
118
|
+
return new Error(
|
|
119
|
+
`Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const parent = groupPolicy[1];
|
|
123
|
+
err = validateEntityReference(parent);
|
|
124
|
+
if (err) {
|
|
125
|
+
return new Error(
|
|
126
|
+
`Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (member.startsWith(`role:`)) {
|
|
130
|
+
return new Error(
|
|
131
|
+
`Group policy is invalid: ${groupPolicy}. rbac-backend plugin doesn't support role inheritance.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (member.startsWith(`group:`) && parent.startsWith(`group:`)) {
|
|
135
|
+
return new Error(
|
|
136
|
+
`Group policy is invalid: ${groupPolicy}. Group inheritance information could be provided only with help of Catalog API.`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (member.startsWith(`user:`) && parent.startsWith(`group:`)) {
|
|
140
|
+
return new Error(
|
|
141
|
+
`Group policy is invalid: ${groupPolicy}. User membership information could be provided only with help of Catalog API.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const metadata = await roleMetadataStorage.findRoleMetadata(parent);
|
|
145
|
+
err = await validateSource(source, metadata);
|
|
146
|
+
if (metadata && err) {
|
|
147
|
+
return new errors.NotAllowedError(
|
|
148
|
+
`Unable to validate role ${groupPolicy}. Cause: ${err.message}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
153
|
+
const checkForDuplicatePolicies = async (fileEnf, policy, policyFile) => {
|
|
154
|
+
const duplicates = await fileEnf.getFilteredPolicy(0, ...policy);
|
|
155
|
+
if (duplicates.length > 1) {
|
|
156
|
+
return new Error(
|
|
157
|
+
`Duplicate policy: ${policy} found in the file ${policyFile}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const flipPolicyEffect = [
|
|
161
|
+
policy[0],
|
|
162
|
+
policy[1],
|
|
163
|
+
policy[2],
|
|
164
|
+
policy[3] === "deny" ? "allow" : "deny"
|
|
165
|
+
];
|
|
166
|
+
const dupWithDifferentEffect = await fileEnf.getFilteredPolicy(
|
|
167
|
+
0,
|
|
168
|
+
...flipPolicyEffect
|
|
169
|
+
);
|
|
170
|
+
if (dupWithDifferentEffect.length > 0) {
|
|
171
|
+
return new Error(
|
|
172
|
+
`Duplicate policy: ${policy[0]}, ${policy[1]}, ${policy[2]} with different effect found in the file ${policyFile}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return void 0;
|
|
176
|
+
};
|
|
177
|
+
const checkForDuplicateGroupPolicies = async (fileEnf, policy, policyFile) => {
|
|
178
|
+
const duplicates = await fileEnf.getFilteredGroupingPolicy(0, ...policy);
|
|
179
|
+
if (duplicates.length > 1) {
|
|
180
|
+
return new Error(
|
|
181
|
+
`Duplicate role: ${policy} found in the file ${policyFile}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return void 0;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
exports.checkForDuplicateGroupPolicies = checkForDuplicateGroupPolicies;
|
|
188
|
+
exports.checkForDuplicatePolicies = checkForDuplicatePolicies;
|
|
189
|
+
exports.validateEntityReference = validateEntityReference;
|
|
190
|
+
exports.validateGroupingPolicy = validateGroupingPolicy;
|
|
191
|
+
exports.validatePolicy = validatePolicy;
|
|
192
|
+
exports.validateRole = validateRole;
|
|
193
|
+
exports.validateSource = validateSource;
|
|
194
|
+
//# sourceMappingURL=policies-validation.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policies-validation.cjs.js","sources":["../../src/validation/policies-validation.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 { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport { Enforcer } from 'casbin';\n\nimport {\n isValidPermissionAction,\n PermissionActionValues,\n Role,\n RoleBasedPolicy,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\n\n/**\n * validateSource validates the source to the role that is being modified. This includes comparing the source from the\n * originating role to the source that the modification is coming from.\n * We do this to ensure consistency between permissions and roles and where they are originally defined.\n * This is a strict comparison where the source of all new roles (grouping policies) and permissions must match\n * the source of the first role that was created.\n * We are not strict for permission policies defined with an originating role source of configuration.\n * @param source The source in which the modification is coming from\n * @param roleMetadata The original role that was created\n * @returns An error in the event that the source does not match the originating role\n */\nexport const validateSource = async (\n source: Source,\n roleMetadata: RoleMetadataDao | undefined,\n): Promise<Error | undefined> => {\n if (!roleMetadata) {\n return undefined; // Role does not exist yet, there is no conflict with the source\n }\n\n if (roleMetadata.source !== source && roleMetadata.source !== 'legacy') {\n return new Error(\n `source does not match originating role ${\n roleMetadata.roleEntityRef\n }, consider making changes to the '${roleMetadata.source.toLocaleUpperCase()}'`,\n );\n }\n\n return undefined;\n};\n\n// This should be called on add and edit and delete\nexport function validatePolicy(policy: RoleBasedPolicy): Error | undefined {\n const err = validateEntityReference(policy.entityReference);\n if (err) {\n return err;\n }\n\n if (!policy.permission) {\n return new Error(`'permission' field must not be empty`);\n }\n\n if (!policy.policy) {\n return new Error(`'policy' field must not be empty`);\n } else if (!isValidPermissionAction(policy.policy)) {\n return new Error(\n `'policy' has invalid value: '${\n policy.policy\n }'. It should be one of: ${PermissionActionValues.join(', ')}`,\n );\n }\n\n if (!policy.effect) {\n return new Error(`'effect' field must not be empty`);\n } else if (!isValidEffectValue(policy.effect)) {\n return new Error(\n `'effect' has invalid value: '${\n policy.effect\n }'. It should be: '${AuthorizeResult.ALLOW.toLocaleLowerCase()}' or '${AuthorizeResult.DENY.toLocaleLowerCase()}'`,\n );\n }\n\n return undefined;\n}\n\nexport function validateRole(role: Role): Error | undefined {\n if (!role.name) {\n return new Error(`'name' field must not be empty`);\n }\n\n let err = validateEntityReference(role.name, true);\n if (err) {\n return err;\n }\n\n if (!role.memberReferences || role.memberReferences.length === 0) {\n return new Error(`'memberReferences' field must not be empty`);\n }\n\n for (const member of role.memberReferences) {\n err = validateEntityReference(member);\n if (err) {\n return err;\n }\n }\n return undefined;\n}\n\nfunction isValidEffectValue(effect: string): boolean {\n return (\n effect === AuthorizeResult.ALLOW.toLocaleLowerCase() ||\n effect === AuthorizeResult.DENY.toLocaleLowerCase()\n );\n}\n\nfunction isValidEntityName(name: string): boolean {\n const validNamePattern = /^[a-zA-Z0-9]+([._-][a-zA-Z0-9]+)*$/;\n return validNamePattern.test(name) && name.length <= 63;\n}\n\nfunction isValidEntityNamespace(namespace: string): boolean {\n const validNamespacePattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n return validNamespacePattern.test(namespace) && namespace.length <= 63;\n}\n\n// We supports only full form entity reference: [<kind>:][<namespace>/]<name>\nexport function validateEntityReference(\n entityRef?: string,\n role?: boolean,\n): Error | undefined {\n if (!entityRef) {\n return new Error(`'entityReference' must not be empty`);\n }\n\n let entityRefCompound: CompoundEntityRef;\n try {\n entityRefCompound = parseEntityRef(entityRef);\n } catch (err) {\n return err as Error;\n }\n\n const entityRefFull = `${entityRefCompound.kind}:${entityRefCompound.namespace}/${entityRefCompound.name}`;\n if (entityRefFull !== entityRef) {\n return new Error(\n `entity reference '${entityRef}' does not match the required format [<kind>:][<namespace>/]<name>. Provide, please, full entity reference.`,\n );\n }\n\n if (role && entityRefCompound.kind !== 'role') {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. Supported value should be \"role\"`,\n );\n }\n\n if (\n entityRefCompound.kind !== 'user' &&\n entityRefCompound.kind !== 'group' &&\n entityRefCompound.kind !== 'role'\n ) {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. List supported values [\"user\", \"group\", \"role\"]`,\n );\n }\n\n if (!isValidEntityName(entityRefCompound.name)) {\n return new Error(\n `The name '${entityRefCompound.name}' in the entity reference must be a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total`,\n );\n }\n\n if (!isValidEntityNamespace(entityRefCompound.namespace)) {\n return new Error(\n `The namespace '${entityRefCompound.namespace}' in the entity reference must be a string that is sequences of [a-z0-9] separated by [-], at most 63 characters in total`,\n );\n }\n\n return undefined;\n}\n\nexport async function validateGroupingPolicy(\n groupPolicy: string[],\n roleMetadataStorage: RoleMetadataStorage,\n source: Source,\n): Promise<Error | undefined> {\n if (groupPolicy.length !== 2) {\n return new Error(`Group policy should have length 2`);\n }\n\n const member = groupPolicy[0];\n let err = validateEntityReference(member);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n const parent = groupPolicy[1];\n err = validateEntityReference(parent);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n if (member.startsWith(`role:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. rbac-backend plugin doesn't support role inheritance.`,\n );\n }\n if (member.startsWith(`group:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. Group inheritance information could be provided only with help of Catalog API.`,\n );\n }\n if (member.startsWith(`user:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. User membership information could be provided only with help of Catalog API.`,\n );\n }\n\n const metadata = await roleMetadataStorage.findRoleMetadata(parent);\n\n err = await validateSource(source, metadata);\n if (metadata && err) {\n return new NotAllowedError(\n `Unable to validate role ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n\n return undefined;\n}\n\nexport const checkForDuplicatePolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredPolicy(0, ...policy);\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate policy: ${policy} found in the file ${policyFile}`,\n );\n }\n\n const flipPolicyEffect = [\n policy[0],\n policy[1],\n policy[2],\n policy[3] === 'deny' ? 'allow' : 'deny',\n ];\n\n // Check if the same policy exists but with a different effect\n const dupWithDifferentEffect = await fileEnf.getFilteredPolicy(\n 0,\n ...flipPolicyEffect,\n );\n\n if (dupWithDifferentEffect.length > 0) {\n return new Error(\n `Duplicate policy: ${policy[0]}, ${policy[1]}, ${policy[2]} with different effect found in the file ${policyFile}`,\n );\n }\n\n return undefined;\n};\n\nexport const checkForDuplicateGroupPolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredGroupingPolicy(0, ...policy);\n\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate role: ${policy} found in the file ${policyFile}`,\n );\n }\n return undefined;\n};\n"],"names":["isValidPermissionAction","PermissionActionValues","AuthorizeResult","parseEntityRef","NotAllowedError"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
await knex.schema.createTable('policy-conditions', table => {
|
|
19
|
+
table.increments('id').primary();
|
|
20
|
+
table.string('result');
|
|
21
|
+
table.string('pluginId');
|
|
22
|
+
table.string('resourceType');
|
|
23
|
+
// Conditions is potentially long json.
|
|
24
|
+
// In the future maybe we can use `json` or `jsonb` type instead of `text`:
|
|
25
|
+
// table.json('conditions') or table.jsonb('conditions').
|
|
26
|
+
// But let's start with text type.
|
|
27
|
+
// Data type "text" can be unlimited by size for Postgres.
|
|
28
|
+
// Also postgres has a lot of build in features for this data type.
|
|
29
|
+
table.text('conditionsJson');
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* down - reverts(undo) migration.
|
|
35
|
+
*
|
|
36
|
+
* @param { import("knex").Knex } knex
|
|
37
|
+
* @returns { Promise<void> }
|
|
38
|
+
*/
|
|
39
|
+
exports.down = async function down(knex) {
|
|
40
|
+
await knex.schema.dropTable('policy-conditions');
|
|
41
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
const casbinDoesExist = await knex.schema.hasTable('casbin_rule');
|
|
19
|
+
const policyMetadataDoesExist = await knex.schema.hasTable('policy-metadata');
|
|
20
|
+
let policies = [];
|
|
21
|
+
let groupPolicies = [];
|
|
22
|
+
|
|
23
|
+
if (casbinDoesExist) {
|
|
24
|
+
policies = await knex
|
|
25
|
+
.select('*')
|
|
26
|
+
.from('casbin_rule')
|
|
27
|
+
.where('ptype', 'p')
|
|
28
|
+
.then(listPolicies => {
|
|
29
|
+
const allPolicies = [];
|
|
30
|
+
for (const policy of listPolicies) {
|
|
31
|
+
const { v0, v1, v2, v3 } = policy;
|
|
32
|
+
allPolicies.push(`[${v0}, ${v1}, ${v2}, ${v3}]`);
|
|
33
|
+
}
|
|
34
|
+
return allPolicies;
|
|
35
|
+
});
|
|
36
|
+
groupPolicies = await knex
|
|
37
|
+
.select('*')
|
|
38
|
+
.from('casbin_rule')
|
|
39
|
+
.where('ptype', 'g')
|
|
40
|
+
.then(listGroupPolicies => {
|
|
41
|
+
const allGroupPolicies = [];
|
|
42
|
+
for (const groupPolicy of listGroupPolicies) {
|
|
43
|
+
const { v0, v1 } = groupPolicy;
|
|
44
|
+
allGroupPolicies.push(`[${v0}, ${v1}]`);
|
|
45
|
+
}
|
|
46
|
+
return allGroupPolicies;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!policyMetadataDoesExist) {
|
|
51
|
+
await knex.schema
|
|
52
|
+
.createTable('policy-metadata', table => {
|
|
53
|
+
table.increments('id').primary();
|
|
54
|
+
table.string('policy').primary();
|
|
55
|
+
table.string('source');
|
|
56
|
+
})
|
|
57
|
+
.then(async () => {
|
|
58
|
+
const metadata = [];
|
|
59
|
+
for (const policy of policies) {
|
|
60
|
+
metadata.push({ source: 'legacy', policy: policy });
|
|
61
|
+
}
|
|
62
|
+
if (metadata.length > 0) {
|
|
63
|
+
await knex.table('policy-metadata').insert(metadata);
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.then(async () => {
|
|
67
|
+
const metadata = [];
|
|
68
|
+
for (const groupPolicy of groupPolicies) {
|
|
69
|
+
metadata.push({ source: 'legacy', policy: groupPolicy });
|
|
70
|
+
}
|
|
71
|
+
if (metadata.length > 0) {
|
|
72
|
+
await knex.table('policy-metadata').insert(metadata);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param { import("knex").Knex } knex
|
|
80
|
+
* @returns { Promise<void> }
|
|
81
|
+
*/
|
|
82
|
+
exports.down = async function down(knex) {
|
|
83
|
+
await knex.schema.dropTable('policy-metadata');
|
|
84
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
const casbinDoesExist = await knex.schema.hasTable('casbin_rule');
|
|
19
|
+
const roleMetadataDoesExist = await knex.schema.hasTable('role-metadata');
|
|
20
|
+
const groupPolicies = new Set();
|
|
21
|
+
|
|
22
|
+
if (casbinDoesExist) {
|
|
23
|
+
await knex
|
|
24
|
+
.select('*')
|
|
25
|
+
.from('casbin_rule')
|
|
26
|
+
.where('ptype', 'g')
|
|
27
|
+
.then(listGroupPolicies => {
|
|
28
|
+
for (const groupPolicy of listGroupPolicies) {
|
|
29
|
+
const { v1 } = groupPolicy;
|
|
30
|
+
groupPolicies.add(v1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!roleMetadataDoesExist) {
|
|
36
|
+
await knex.schema
|
|
37
|
+
.createTable('role-metadata', table => {
|
|
38
|
+
table.increments('id').primary();
|
|
39
|
+
table.string('roleEntityRef').primary();
|
|
40
|
+
table.string('source');
|
|
41
|
+
})
|
|
42
|
+
.then(async () => {
|
|
43
|
+
const metadata = [];
|
|
44
|
+
for (const groupPolicy of groupPolicies) {
|
|
45
|
+
metadata.push({ source: 'legacy', roleEntityRef: groupPolicy });
|
|
46
|
+
}
|
|
47
|
+
if (metadata.length > 0) {
|
|
48
|
+
await knex.table('role-metadata').insert(metadata);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param { import("knex").Knex } knex
|
|
56
|
+
* @returns { Promise<void> }
|
|
57
|
+
*/
|
|
58
|
+
exports.down = async function down(knex) {
|
|
59
|
+
await knex.schema.dropTable('role-metadata');
|
|
60
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
const isRoleMetaDataExist = await knex.schema.hasTable('role-metadata');
|
|
19
|
+
if (isRoleMetaDataExist) {
|
|
20
|
+
await knex.schema.alterTable('role-metadata', table => {
|
|
21
|
+
table.string('description');
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param { import("knex").Knex } knex
|
|
28
|
+
* @returns { Promise<void> }
|
|
29
|
+
*/
|
|
30
|
+
exports.down = async function down(knex) {
|
|
31
|
+
const isRoleMetaDataExist = await knex.schema.hasTable('role-metadata');
|
|
32
|
+
if (isRoleMetaDataExist) {
|
|
33
|
+
await knex.schema.alterTable('role-metadata', table => {
|
|
34
|
+
table.dropColumn('description');
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
const casbinDoesExist = await knex.schema.hasTable('casbin_rule');
|
|
19
|
+
const policyMetadataExist = await knex.schema.hasTable('policy-metadata');
|
|
20
|
+
const roleMetadataExist = await knex.schema.hasTable('role-metadata');
|
|
21
|
+
|
|
22
|
+
if (casbinDoesExist && policyMetadataExist) {
|
|
23
|
+
const policyMetadataColumns = await knex('policy-metadata').select(
|
|
24
|
+
'id',
|
|
25
|
+
'policy',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const policiesToCheck = policyMetadataColumns.map(metadataColumn => {
|
|
29
|
+
const policy = metadataColumn.policy
|
|
30
|
+
.replace(/\[/g, '')
|
|
31
|
+
.replace(/\]/g, '')
|
|
32
|
+
.split(',')
|
|
33
|
+
.map(str => str.trim());
|
|
34
|
+
return { policy, id: metadataColumn.id };
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const existingPolicies = await knex('casbin_rule')
|
|
38
|
+
.whereIn(
|
|
39
|
+
'v0',
|
|
40
|
+
policiesToCheck.map(policyToCheck => policyToCheck.policy[0]),
|
|
41
|
+
)
|
|
42
|
+
.whereIn(
|
|
43
|
+
'v1',
|
|
44
|
+
policiesToCheck.map(policyToCheck => policyToCheck.policy[1]),
|
|
45
|
+
)
|
|
46
|
+
.andWhere(query => {
|
|
47
|
+
query
|
|
48
|
+
.where(innerQuery => {
|
|
49
|
+
innerQuery.whereNotNull('v2').whereIn(
|
|
50
|
+
'v2',
|
|
51
|
+
policiesToCheck
|
|
52
|
+
.filter(policy => policy.policy.length === 4)
|
|
53
|
+
.map(policy => policy.policy[2]),
|
|
54
|
+
);
|
|
55
|
+
})
|
|
56
|
+
.orWhereNull('v2');
|
|
57
|
+
})
|
|
58
|
+
.andWhere(query => {
|
|
59
|
+
query
|
|
60
|
+
.where(innerQuery => {
|
|
61
|
+
innerQuery.whereNotNull('v3').whereIn(
|
|
62
|
+
'v3',
|
|
63
|
+
policiesToCheck
|
|
64
|
+
.filter(policy => policy.policy.length === 4)
|
|
65
|
+
.map(policy => policy.policy[3]),
|
|
66
|
+
);
|
|
67
|
+
})
|
|
68
|
+
.orWhereNull('v3');
|
|
69
|
+
})
|
|
70
|
+
.select('v0', 'v1', 'v2', 'v3');
|
|
71
|
+
|
|
72
|
+
const existingPoliciesSet = new Set(
|
|
73
|
+
existingPolicies.map(policy =>
|
|
74
|
+
policy.v2
|
|
75
|
+
? `${policy.v0},${policy.v1},${policy.v2},${policy.v3}`
|
|
76
|
+
: `${policy.v0},${policy.v1}`,
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const policiesToDelete = policiesToCheck.filter(
|
|
81
|
+
policyToCheck => !existingPoliciesSet.has(policyToCheck.policy.join(',')),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (policiesToDelete.length > 0) {
|
|
85
|
+
await knex('policy-metadata')
|
|
86
|
+
.whereIn(
|
|
87
|
+
'id',
|
|
88
|
+
policiesToDelete.map(policyToDel => policyToDel.id),
|
|
89
|
+
)
|
|
90
|
+
.del();
|
|
91
|
+
console.log(
|
|
92
|
+
`Deleted inconsistent policy metadata ${JSON.stringify(
|
|
93
|
+
policiesToDelete,
|
|
94
|
+
)} from 'policy-metadata' table.`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (casbinDoesExist && roleMetadataExist) {
|
|
100
|
+
const roleMetadataColumns = await knex('role-metadata').select(
|
|
101
|
+
'id',
|
|
102
|
+
'roleEntityRef',
|
|
103
|
+
);
|
|
104
|
+
const roleMetadata = roleMetadataColumns.map(rm => {
|
|
105
|
+
return { roleEntityRef: rm.roleEntityRef, id: rm.id };
|
|
106
|
+
});
|
|
107
|
+
const existingPoliciesForRoles = await knex('casbin_rule')
|
|
108
|
+
.orWhereIn(
|
|
109
|
+
'v1',
|
|
110
|
+
roleMetadata.map(rm => rm.roleEntityRef),
|
|
111
|
+
)
|
|
112
|
+
.select('v1');
|
|
113
|
+
|
|
114
|
+
const existingRoles = new Set(
|
|
115
|
+
existingPoliciesForRoles.map(policy => policy.v1),
|
|
116
|
+
);
|
|
117
|
+
const rolesMetadataToDelete = roleMetadata.filter(
|
|
118
|
+
rm => !existingRoles.has(rm.roleEntityRef),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (rolesMetadataToDelete.length > 0) {
|
|
122
|
+
await knex('role-metadata')
|
|
123
|
+
.whereIn(
|
|
124
|
+
'id',
|
|
125
|
+
rolesMetadataToDelete.map(rm => rm.id),
|
|
126
|
+
)
|
|
127
|
+
.del();
|
|
128
|
+
console.log(
|
|
129
|
+
`Deleted inconsistent role metadata ${JSON.stringify(
|
|
130
|
+
rolesMetadataToDelete,
|
|
131
|
+
)} from 'role-metadata' table.`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param { import("knex").Knex } knex
|
|
139
|
+
* @returns { Promise<void> }
|
|
140
|
+
*/
|
|
141
|
+
exports.down = function down(_knex) {
|
|
142
|
+
// do nothing
|
|
143
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
const policyConditionsExist = await knex.schema.hasTable('policy-conditions');
|
|
19
|
+
|
|
20
|
+
if (policyConditionsExist) {
|
|
21
|
+
// We drop policy condition table, because we decided to rework this feature
|
|
22
|
+
// and bound policy condition to the role
|
|
23
|
+
await knex.schema.dropTable('policy-conditions');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param { import("knex").Knex } knex
|
|
29
|
+
* @returns { Promise<void> }
|
|
30
|
+
*/
|
|
31
|
+
exports.down = async function down(_knex) {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
await knex.schema.createTable('role-condition-policies', table => {
|
|
19
|
+
table.increments('id').primary();
|
|
20
|
+
table.string('roleEntityRef');
|
|
21
|
+
table.string('result');
|
|
22
|
+
table.string('pluginId');
|
|
23
|
+
table.string('resourceType');
|
|
24
|
+
table.string('permissions');
|
|
25
|
+
// Conditions is potentially long json.
|
|
26
|
+
// In the future maybe we can use `json` or `jsonb` type instead of `text`:
|
|
27
|
+
// table.json('conditions') or table.jsonb('conditions').
|
|
28
|
+
// But let's start with text type.
|
|
29
|
+
// Data type "text" can be unlimited by size for Postgres.
|
|
30
|
+
// Also postgres has a lot of build in features for this data type.
|
|
31
|
+
table.text('conditionsJson');
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* down - reverts(undo) migration.
|
|
37
|
+
*
|
|
38
|
+
* @param { import("knex").Knex } knex
|
|
39
|
+
* @returns { Promise<void> }
|
|
40
|
+
*/
|
|
41
|
+
exports.down = async function down(knex) {
|
|
42
|
+
await knex.schema.dropTable('policy-conditions');
|
|
43
|
+
};
|