Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions packages/uma/src/policies/authorizers/SimpleOdrlAuthorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,28 @@ export class SimpleOdrlAuthorizer implements Authorizer {

let permissions: Permission[] = [];
for (const { resource_id, resource_scopes } of query) {
const allowedScopes: string[] = [];
for (const scope of resource_scopes) {
const result = this.getPermissions(store, claims, resource_id, scope);
if (!result) {
// Too difficult to handle internally so need to call complete authorizer
return this.authorizer.permissions(claims, query);
}

permissions.push(...result);
allowedScopes.push(...result);
}
if (allowedScopes.length > 0) {
permissions.push({
resource_id,
resource_scopes: allowedScopes,
});
}
}
return permissions;
}

protected getPermissions(policies: ReadOnlyStore, claims: ClaimSet, resource: string, scope: string):
Permission[] | undefined {
string[] | undefined {
this.logger.info(`Evaluating Request ${scope}, ${resource} with claims ${JSON.stringify(claims)}`);
const targets = [ DF.namedNode(resource), ...policies.getObjects(resource, ODRL.terms.partOf, null)];
let rules = targets.flatMap(target => policies.getSubjects(ODRL.terms.target, target, null));
Expand Down Expand Up @@ -145,10 +152,7 @@ export class SimpleOdrlAuthorizer implements Authorizer {
}
}

return [{
resource_id: resource,
resource_scopes: [ oldScope ],
}];
return [ oldScope ];
}

// TODO: 3 modes: valid, not valid, too complicated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,16 +243,17 @@ describe('SimpleOdrlAuthorizer', () => {
const scope2 = 'urn:example:css:modes:write';
const odrlScope2 = 'http://www.w3.org/ns/odrl/2/modify';
const multiQuery: Permission[] = [
{ resource_id: resource, resource_scopes: [scope] },
{ resource_id: resource, resource_scopes: [scope, odrlScope] },
{ resource_id: resource2, resource_scopes: [scope2] },
];
addRule({});
addRule({ action: odrlScope });
addRule({ target: resource2, action: odrlScope2 });

const result = await authorizer.permissions({}, multiQuery);

expect(result).toEqual([
{ resource_id: resource, resource_scopes: [scope] },
{ resource_id: resource, resource_scopes: [scope, odrlScope] },
{ resource_id: resource2, resource_scopes: [scope2] },
]);
expect(fallback.permissions).not.toHaveBeenCalled();
Expand Down
65 changes: 60 additions & 5 deletions test/integration/Partial.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { App } from '@solid/community-server';
import { setGlobalLoggerFactory, WinstonLoggerFactory } from 'global-logger-factory';
import { decodeJwt } from 'jose';
import { createServer, Server } from 'node:http';
import path from 'node:path';
import { getPorts, instantiateFromConfig } from '../util/ServerUtil';
Expand All @@ -18,10 +19,12 @@ interface UmaConfig {
describe('A server with partial results enabled', (): void => {
const owner = 'http://example.com/alice#me';
const user = `http://example.com/bob`;
const aliceContainer = `http://localhost:${rsPort}/alice/`;
const aliceResource = `http://localhost:${rsPort}/alice/data`;
const bobResource = `http://localhost:${rsPort}/bob/data`;
const readScope = 'http://www.w3.org/ns/odrl/2/read';
const writeScope = 'http://www.w3.org/ns/odrl/2/write';
const createScope = 'http://www.w3.org/ns/odrl/2/create';
const modifyScope = 'http://www.w3.org/ns/odrl/2/modify';
let umaApp: App;
let rsServer: Server;
let umaConfig: UmaConfig;
Expand Down Expand Up @@ -77,6 +80,20 @@ describe('A server with partial results enabled', (): void => {
pat = `${patJson.token_type} ${patJson.access_token}`;

let resourceRegistrationResponse = await fetch(umaConfig.resource_registration_endpoint, {
method: 'POST',
headers: {
Authorization: pat,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
name: aliceContainer,
resource_scopes: [ createScope, modifyScope ],
}),
});
expect(resourceRegistrationResponse.status).toBe(201);

resourceRegistrationResponse = await fetch(umaConfig.resource_registration_endpoint, {
method: 'POST',
headers: {
Authorization: pat,
Expand All @@ -85,7 +102,7 @@ describe('A server with partial results enabled', (): void => {
},
body: JSON.stringify({
name: aliceResource,
resource_scopes: [ readScope, writeScope ],
resource_scopes: [ createScope, modifyScope ],
}),
});
expect(resourceRegistrationResponse.status).toBe(201);
Expand All @@ -99,7 +116,7 @@ describe('A server with partial results enabled', (): void => {
},
body: JSON.stringify({
name: bobResource,
resource_scopes: [ readScope, writeScope ],
resource_scopes: [ readScope, modifyScope ],
}),
});
expect(resourceRegistrationResponse.status).toBe(201);
Expand Down Expand Up @@ -187,7 +204,7 @@ describe('A server with partial results enabled', (): void => {
odrl:assignee <${owner}> ;
odrl:assigner <${owner}> ;
odrl:action odrl:create, odrl:modify ;
odrl:target <http://localhost:${rsPort}/alice/> ,
odrl:target <${aliceContainer}> ,
<${aliceResource}> .

ex:userPermission a odrl:Permission ;
Expand All @@ -208,6 +225,44 @@ describe('A server with partial results enabled', (): void => {
expect(response.status).toBe(200);
});

it('returns all requested scopes when possible.', async(): Promise<void> => {
const response = await fetch(umaConfig.permission_endpoint, {
method: 'POST',
headers: {
Authorization: pat,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify([
{
resource_id: aliceResource,
resource_scopes: [ createScope, modifyScope ],
},
{
resource_id: aliceContainer,
resource_scopes: [ createScope, modifyScope ],
}
]),
});
expect(response.status).toBe(201);
const { ticket } = await response.json() as { ticket: string };

const token = await getToken(ticket, umaConfig.token_endpoint, owner);
expect((token as unknown as { partial?: boolean }).partial).toBeUndefined();

const decodedToken = decodeJwt(token.access_token);
expect(decodedToken.permissions).toEqual([
{
resource_id: aliceResource,
resource_scopes: [createScope, modifyScope],
},
{
resource_id: aliceContainer,
resource_scopes: [ createScope, modifyScope ],
}
]);
});

it('returns partial=true for mixed namespaces when not all scopes are granted.', async(): Promise<void> => {
const permissionResponse = await fetch(umaConfig.permission_endpoint, {
method: 'POST',
Expand All @@ -219,7 +274,7 @@ describe('A server with partial results enabled', (): void => {
body: JSON.stringify([
{
resource_id: aliceResource,
resource_scopes: [ readScope, writeScope ],
resource_scopes: [ readScope, modifyScope ],
},
{
resource_id: bobResource,
Expand Down
Loading