Skip to content

Commit e2c8223

Browse files
committed
feat: Allow NamespacedAuthorizer to use different fragment
1 parent 9b08f27 commit e2c8223

2 files changed

Lines changed: 18 additions & 7 deletions

File tree

packages/uma/src/policies/authorizers/NamespacedAuthorizer.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { RegistrationStore } from '../../util/RegistrationStore';
44
import { Permission } from '../../views/Permission';
55
import { Authorizer } from './Authorizer';
66

7-
const namespace = (resource: string) => new URL(resource).pathname.split('/')?.[2] ?? '';
8-
97
/**
108
* An authorizer delegating to different authorizers based on the namespaces in the request.
119
*/
@@ -19,11 +17,15 @@ export class NamespacedAuthorizer implements Authorizer {
1917
* and the value being the corresponding authorizer to use for that namespace.
2018
* @param fallback - Authorizer to use if there is no namespace match.
2119
* @param registrationStore - The key/value store containing the resource registrations.
20+
* @param namespacePosition - URL segment position to find the namespace, after removing the domain.
21+
* E.g., if URL is http://localhost:3000/alice/profile/card, `profile` has position 2.
22+
* Defaults to 2.
2223
*/
2324
constructor(
24-
protected authorizers: Record<string, Authorizer>,
25-
protected fallback: Authorizer,
26-
protected registrationStore: RegistrationStore,
25+
protected readonly authorizers: Record<string, Authorizer>,
26+
protected readonly fallback: Authorizer,
27+
protected readonly registrationStore: RegistrationStore,
28+
protected readonly namespacePosition = 2,
2729
) {}
2830

2931
/** @inheritdoc */
@@ -68,9 +70,9 @@ export class NamespacedAuthorizer implements Authorizer {
6870
const resourceIdentifier = registration.description.name;
6971
if (!resourceIdentifier) {
7072
this.logger.warn(`Resource ${resourceId} has no registered name.`);
71-
return
73+
return;
7274
}
7375

74-
return namespace(resourceIdentifier);
76+
return new URL(resourceIdentifier).pathname.split('/')?.[this.namespacePosition] ?? '';
7577
}
7678
}

packages/uma/test/unit/policies/authorizers/NamespacedAuthorizer.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,13 @@ describe('NamespacedAuthorizer', (): void => {
6464
expect(fallback.permissions).toHaveBeenCalledWith(claims, query2);
6565
});
6666
});
67+
68+
it('can be configured to use a different path segment.', async(): Promise<void> => {
69+
authorizers.res = { permissions: vi.fn().mockResolvedValue('perm-res'), };
70+
const authorizer = new NamespacedAuthorizer(authorizers, fallback, registrationStore, 3);
71+
const query = [{ resource_id: 'res1' }];
72+
await expect(authorizer.permissions(claims, query)).resolves.toEqual('perm-res');
73+
expect(authorizers.res.permissions).toHaveBeenCalledTimes(1);
74+
expect(authorizers.res.permissions).toHaveBeenLastCalledWith(claims, query);
75+
});
6776
});

0 commit comments

Comments
 (0)