permissions: add support for adding permission rules separately from resource type

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-01-08 09:51:03 +01:00
parent a19cb2b8a5
commit 45e0c1d645
6 changed files with 60 additions and 20 deletions
@@ -62,6 +62,14 @@ export const permissionIntegrationsServiceFactory = createServiceFactory({
}
router.addPermissions(permissions);
},
addPermissionRules(rules) {
if (started) {
throw new Error(
'Cannot add permission rules after the plugin has started',
);
}
router.addPermissionRules(rules);
},
};
},
});
@@ -434,6 +434,7 @@ export interface LoggerService {
// @public
export interface PermissionIntegrationsService {
addPermissionRules(rules: PermissionRule<any, any, string>[]): void;
addPermissions(permissions: Permission[]): void;
addResourceType<const TResourceType extends string, TResource>(
options: PermissionIntegrationsServiceAddResourceTypeOptions<
@@ -77,6 +77,21 @@ export interface PermissionIntegrationsService {
*/
addPermissions(permissions: Permission[]): void;
/**
* Adds a set of permission rules to the permission system for a resource type
* that is owned by this plugin.
*
* @remarks
*
* Rules should be created using corresponding `create*PermissionRule`
* functions exported by plugins, who in turn are created with
* `makeCreatePermissionRule`.
*
* Rules can be added either directly by the plugin itself or through a plugin
* module.
*/
addPermissionRules(rules: PermissionRule<any, any, string>[]): void;
/**
* Add a new resource type that is owned by this plugin to the permission
* system.
+1
View File
@@ -142,6 +142,7 @@ export function createPermissionIntegrationRouter<
>,
): express.Router & {
addPermissions(permissions: Permission[]): void;
addPermissionRules(rules: PermissionRule<unknown, unknown, string>[]): void;
addResourceType<const TResourceType extends string, TResource>(
resource: CreatePermissionIntegrationRouterResourceOptions<
TResourceType,
@@ -1006,14 +1006,19 @@ describe('createPermissionIntegrationRouter', () => {
resourceType: 'test-resource',
permissions: [testPermission],
getResources: defaultMockedGetResources1,
rules: [testRule1, testRule2],
rules: [testRule1],
});
router.addPermissionRules([testRule2]);
// This one is for the resource added below, it should be possible to add rules before the resource typeof
router.addPermissionRules([testRule3]);
router.addResourceType({
resourceType: 'test-resource-2',
permissions: [testPermission2],
getResources: defaultMockedGetResources2,
rules: [testRule3],
rules: [],
});
const responseAfter = await request(express().use(router)).get(
@@ -308,6 +308,28 @@ class PermissionIntegrationMetadataStore {
}
}
addPermissionRules(rules: PermissionRule<unknown, unknown, string>[]) {
for (const rule of rules) {
const rulesByName =
this.#rulesByTypeByName.get(rule.resourceType) ?? new Map();
this.#rulesByTypeByName.set(rule.resourceType, rulesByName);
if (rulesByName.has(rule.name)) {
throw new Error(
`Refused to add permission rule for type '${rule.resourceType}' with name '${rule.name}' because it already exists`,
);
}
rulesByName.set(rule.name, rule);
this.#serializedRules.push({
name: rule.name,
description: rule.description,
resourceType: rule.resourceType,
paramsSchema: zodToJsonSchema(rule.paramsSchema ?? z.object({})),
});
}
}
addResourceType(
resource: CreatePermissionIntegrationRouterResourceOptions<string, unknown>,
) {
@@ -320,24 +342,8 @@ class PermissionIntegrationMetadataStore {
}
this.#resourcesByType.set(resourceType, resource);
for (const rule of resource.rules) {
const rulesByName =
this.#rulesByTypeByName.get(resourceType) ?? new Map();
this.#rulesByTypeByName.set(resourceType, rulesByName);
if (rulesByName.has(rule.name)) {
throw new Error(
`Refused to add permission rule for type '${resourceType}' with name '${rule.name}' because it already exists`,
);
}
rulesByName.set(rule.name, rule);
this.#serializedRules.push({
name: rule.name,
description: rule.description,
resourceType: rule.resourceType,
paramsSchema: zodToJsonSchema(rule.paramsSchema ?? z.object({})),
});
if (resource.rules) {
this.addPermissionRules(resource.rules);
}
if (resource.permissions) {
@@ -411,6 +417,7 @@ export function createPermissionIntegrationRouter<
>,
): express.Router & {
addPermissions(permissions: Permission[]): void;
addPermissionRules(rules: PermissionRule<unknown, unknown, string>[]): void;
addResourceType<const TResourceType extends string, TResource>(
resource: CreatePermissionIntegrationRouterResourceOptions<
TResourceType,
@@ -495,6 +502,9 @@ export function createPermissionIntegrationRouter<
addPermissions(permissions: Permission[]) {
store.addPermissions(permissions);
},
addPermissionRules(rules: PermissionRule<unknown, unknown, string>[]) {
store.addPermissionRules(rules);
},
addResourceType<const TResourceType extends string, TResource>(
resource: CreatePermissionIntegrationRouterResourceOptions<
TResourceType,