Skip to content

Commit 09fcf9a

Browse files
committed
feat: Allow ImmediateAuthorizerStrategy to return partial results
1 parent de27523 commit 09fcf9a

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

packages/uma/src/ticketing/strategy/ImmediateAuthorizerStrategy.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ import { Authorizer } from "../../policies/authorizers/Authorizer";
1010
/**
1111
* A TicketingStrategy that simply stores provided Claims, and calculates all
1212
* available Permissions from them upon resolution.
13+
*
14+
* If `allowPartialSuccess` is set to true,
15+
* the strategy will return all available permissions,
16+
* even if not all required permissions are granted.
1317
*/
1418
export class ImmediateAuthorizerStrategy implements TicketingStrategy {
1519
protected readonly logger = getLoggerFor(this);
1620

1721
constructor(
1822
protected authorizer: Authorizer,
23+
protected readonly allowPartialSuccess = false,
1924
) {}
2025

2126
/** @inheritdoc */
@@ -45,9 +50,12 @@ export class ImmediateAuthorizerStrategy implements TicketingStrategy {
4550

4651
const permissions = await this.calculatePermissions(ticket);
4752

53+
// Always fail if no results at all, even with allowPartialSuccess
4854
if (permissions.length === 0) return Failure([]);
4955

50-
// TODO: if, in the future, we want to allow partial results, this will need to change
56+
// With partial success enabled, any non-empty authorization result is accepted
57+
if (this.allowPartialSuccess) return Success(permissions);
58+
5159
// Verify all required scopes have been granted
5260
const unmatchedPermissions: Permission[] = [];
5361
for (const required of ticket.permissions) {

packages/uma/test/unit/ticketing/strategy/ImmediateAuthorizerStrategy.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,29 @@ describe('ImmediateAuthorizerStrategy', (): void => {
6464
await expect(strategy.resolveTicket(ticket)).resolves
6565
.toEqual({ success: false, value: [{ resource_scopes: ['scopes'] }] });
6666
});
67+
68+
it('resolves with partial permissions when allowPartialSuccess is enabled.', async(): Promise<void> => {
69+
const partialStrategy = new ImmediateAuthorizerStrategy(authorizer, true);
70+
const ticket: Ticket = {
71+
permissions: [{ resource_id: 'id', resource_scopes: [ 'read', 'write' ] }],
72+
provided: {},
73+
};
74+
const authResponse: Permission[] = [
75+
{ resource_id: 'id', resource_scopes: [ 'read' ] },
76+
];
77+
authorizer.permissions.mockResolvedValueOnce(authResponse);
78+
await expect(partialStrategy.resolveTicket(ticket)).resolves
79+
.toEqual({ success: true, value: authResponse });
80+
});
81+
82+
it('rejects when no permissions are resolved, even with allowPartialSuccess enabled.', async(): Promise<void> => {
83+
const partialStrategy = new ImmediateAuthorizerStrategy(authorizer, true);
84+
const ticket: Ticket = {
85+
permissions,
86+
provided: {},
87+
};
88+
authorizer.permissions.mockResolvedValueOnce([]);
89+
await expect(partialStrategy.resolveTicket(ticket)).resolves
90+
.toEqual({ success: false, value: [] });
91+
});
6792
});

0 commit comments

Comments
 (0)