Skip to content

Commit

Permalink
feat: Add the POST /api/v2/records/entitlements/revoke endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
KallynGowdy committed Feb 7, 2025
1 parent a2f0478 commit a5c944b
Show file tree
Hide file tree
Showing 3 changed files with 371 additions and 17 deletions.
214 changes: 214 additions & 0 deletions src/aux-records/RecordsServer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16329,6 +16329,220 @@ describe('RecordsServer', () => {
);
});

describe('POST /api/v2/records/entitlement/revoke', () => {
beforeEach(async () => {
await packageStore.createItem(recordName, {
id: 'packageId',
address: 'address',
markers: [PUBLIC_READ_MARKER],
});

await packageVersionsStore.createItem(recordName, {
id: `packageVersionId`,
address: 'address',
key: {
major: 1,
minor: 0,
patch: 0,
tag: '',
},
auxFileName: 'test.aux',
auxSha256: 'auxSha256',
sizeInBytes: 123,
createdAtMs: 999,
createdFile: true,
entitlements: [],
readme: '',
requiresReview: false,
sha256: 'sha256',
});

await store.saveGrantedPackageEntitlement({
id: 'grantId',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'data',
scope: 'designated',
expireTimeMs: Date.now() + 1000 * 60,
revokeTimeMs: null,
createdAtMs: Date.now(),
});

await store.saveGrantedPackageEntitlement({
id: 'grantId2',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'file',
scope: 'designated',
expireTimeMs: Date.now() + 1000 * 60,
revokeTimeMs: null,
createdAtMs: Date.now(),
});
});

it('should revoke the given entitlement grant', async () => {
const result = await server.handleHttpRequest(
httpPost(
`/api/v2/records/entitlement/revoke`,
JSON.stringify({
grantId: 'grantId',
}),
apiHeaders
)
);

await expectResponseBodyToEqual(result, {
statusCode: 200,
body: {
success: true,
},
headers: apiCorsHeaders,
});

expect(store.grantedPackageEntitlements).toEqual([
{
id: 'grantId',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'data',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: expect.any(Number),
createdAtMs: expect.any(Number),
},
{
id: 'grantId2',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'file',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: null,
createdAtMs: expect.any(Number),
},
]);
});

it('should revoke the grant for other users if the current user is a super user', async () => {
const owner = await store.findUser(ownerId);
await store.saveUser({
...owner,
role: 'superUser',
});

const result = await server.handleHttpRequest(
httpPost(
`/api/v2/records/entitlement/revoke`,
JSON.stringify({
grantId: 'grantId',
}),
{
...apiHeaders,
authorization: `Bearer ${ownerSessionKey}`,
}
)
);

await expectResponseBodyToEqual(result, {
statusCode: 200,
body: {
success: true,
},
headers: apiCorsHeaders,
});

expect(store.grantedPackageEntitlements).toEqual([
{
id: 'grantId',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'data',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: expect.any(Number),
createdAtMs: expect.any(Number),
},
{
id: 'grantId2',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'file',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: null,
createdAtMs: expect.any(Number),
},
]);
});

it('should return not_authorized if the user is trying to revoke a grant for someone else', async () => {
const result = await server.handleHttpRequest(
httpPost(
`/api/v2/records/entitlement/revoke`,
JSON.stringify({
grantId: 'grantId',
}),
{
...apiHeaders,
authorization: `Bearer ${ownerSessionKey}`,
}
)
);

await expectResponseBodyToEqual(result, {
statusCode: 403,
body: {
success: false,
errorCode: 'not_authorized',
errorMessage:
'You are not authorized to perform this action.',
},
headers: apiCorsHeaders,
});

expect(store.grantedPackageEntitlements).toEqual([
{
id: 'grantId',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'data',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: null,
createdAtMs: expect.any(Number),
},
{
id: 'grantId2',
userId: userId,
recordName,
packageId: 'packageId',
feature: 'file',
scope: 'designated',
expireTimeMs: expect.any(Number),
revokeTimeMs: null,
createdAtMs: expect.any(Number),
},
]);
});

testUrl(
'POST',
'/api/v2/records/entitlement/revoke',
() =>
JSON.stringify({
grantId: 'grantId',
}),
() => apiHeaders
);
});

describe('GET /api/v2/records/insts/list', () => {
const inst1 = 'myInst';
const inst2 = 'myInst2';
Expand Down
52 changes: 35 additions & 17 deletions src/aux-records/RecordsServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3024,26 +3024,13 @@ export class RecordsServer {
return sessionKeyValidation;
}

if (!userId) {
userId = sessionKeyValidation.userId;
}

if (
userId !== sessionKeyValidation.userId &&
sessionKeyValidation.role !== 'superUser'
) {
return {
success: false,
errorCode: 'not_authorized',
errorMessage:
'You are not authorized to perform this action.',
};
}

const result =
await this._policyController.grantEntitlement({
packageId,
userId,
userId: sessionKeyValidation.userId,
userRole: sessionKeyValidation.role,
grantingUserId:
userId ?? sessionKeyValidation.userId,
recordName,
feature,
scope,
Expand All @@ -3054,6 +3041,37 @@ export class RecordsServer {
}
),

revokeEntitlement: procedure()
.origins('api')
.http('POST', '/api/v2/records/entitlement/revoke')
.inputs(
z.object({
grantId: z.string(),
})
)
.handler(async ({ grantId }, context) => {
const sessionKeyValidation = await this._validateSessionKey(
context.sessionKey
);
if (sessionKeyValidation.success === false) {
if (
sessionKeyValidation.errorCode === 'no_session_key'
) {
return NOT_LOGGED_IN_RESULT;
}
return sessionKeyValidation;
}

const result =
await this._policyController.revokeEntitlement({
userId: sessionKeyValidation.userId,
userRole: sessionKeyValidation.role,
grantId,
});

return result;
}),

listGrantedEntitlements: procedure()
.origins('api')
.http('GET', '/api/v2/records/entitlement/grants/list')
Expand Down
Loading

0 comments on commit a5c944b

Please sign in to comment.