Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add grant to Record class methods #859

Merged
merged 16 commits into from
Sep 3, 2024
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
8 changes: 8 additions & 0 deletions .changeset/green-plums-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@web5/agent": minor
"@web5/identity-agent": minor
"@web5/proxy-agent": minor
"@web5/user-agent": minor
---

Tefactor getting permissions for grants into a single Permission API interface
5 changes: 5 additions & 0 deletions .changeset/plenty-brooms-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/api": patch
---

Consume single PermissionApi for dealing with permissions, fix bug for Record class not fetching delegate permissions for request.
66 changes: 0 additions & 66 deletions packages/agent/src/cached-permissions.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export type * from './types/vc.js';

export * from './agent-did-resolver-cache.js';
export * from './bearer-identity.js';
export * from './cached-permissions.js';
export * from './crypto-api.js';
export * from './did-api.js';
export * from './dwn-api.js';
Expand Down
51 changes: 49 additions & 2 deletions packages/agent/src/permissions-api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { PermissionGrant, PermissionGrantData, PermissionRequestData, PermissionRevocationData, PermissionsProtocol } from '@tbd54566975/dwn-sdk-js';
import { Web5Agent } from './types/agent.js';
import { DwnDataEncodedRecordsWriteMessage, DwnInterface, DwnMessageParams, DwnMessagesPermissionScope, DwnPermissionGrant, DwnPermissionRequest, DwnPermissionScope, DwnProtocolPermissionScope, DwnRecordsPermissionScope, ProcessDwnRequest } from './types/dwn.js';
import { Convert } from '@web5/common';
import { CreateGrantParams, CreateRequestParams, CreateRevocationParams, FetchPermissionRequestParams, FetchPermissionsParams, IsGrantRevokedParams, PermissionGrantEntry, PermissionRequestEntry, PermissionRevocationEntry, PermissionsApi } from './types/permissions.js';
import { Convert, TtlCache } from '@web5/common';
import { CreateGrantParams, CreateRequestParams, CreateRevocationParams, FetchPermissionRequestParams, FetchPermissionsParams, GetPermissionParams, IsGrantRevokedParams, PermissionGrantEntry, PermissionRequestEntry, PermissionRevocationEntry, PermissionsApi } from './types/permissions.js';
import { isRecordsType } from './dwn-api.js';

export class AgentPermissionsApi implements PermissionsApi {

/** cache for fetching a permission {@link PermissionGrant}, keyed by a specific MessageType and protocol */
private _cachedPermissions: TtlCache<string, PermissionGrantEntry> = new TtlCache({ ttl: 60 * 1000 });

private _agent?: Web5Agent;

get agent(): Web5Agent {
Expand All @@ -24,6 +27,46 @@ export class AgentPermissionsApi implements PermissionsApi {
this._agent = agent;
}

async getPermissionForRequest({
connectedDid,
delegateDid,
delegate,
messageType,
protocol,
cached = false
}: GetPermissionParams): Promise<PermissionGrantEntry> {
// Currently we only support finding grants based on protocols
// A different approach may be necessary when we introduce `protocolPath` and `contextId` specific impersonation
const cacheKey = [ connectedDid, delegateDid, messageType, protocol ].join('~');
const cachedGrant = cached ? this._cachedPermissions.get(cacheKey) : undefined;
if (cachedGrant) {
return cachedGrant;
}

const permissionGrants = await this.fetchGrants({
author : delegateDid,
target : delegateDid,
grantor : connectedDid,
grantee : delegateDid,
});

// get the delegate grants that match the messageParams and are associated with the connectedDid as the grantor
const grant = await AgentPermissionsApi.matchGrantFromArray(
connectedDid,
delegateDid,
{ messageType, protocol },
permissionGrants,
delegate
);

if (!grant) {
throw new Error(`CachedPermissions: No permissions found for ${messageType}: ${protocol}`);
}

this._cachedPermissions.set(cacheKey, grant);
return grant;
}

async fetchGrants({
author,
target,
Expand Down Expand Up @@ -269,6 +312,10 @@ export class AgentPermissionsApi implements PermissionsApi {
return { message: dataEncodedMessage };
}

async clear():Promise<void> {
this._cachedPermissions.clear();
}

/**
* Matches the appropriate grant from an array of grants based on the provided parameters.
*
Expand Down
20 changes: 12 additions & 8 deletions packages/agent/src/sync-engine-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import type { Web5Agent, Web5PlatformAgent } from './types/agent.js';

import { DwnInterface } from './types/dwn.js';
import { getDwnServiceEndpointUrls, isRecordsWrite } from './utils.js';
import { CachedPermissions } from './cached-permissions.js';
import { PermissionsApi } from './types/permissions.js';
import { AgentPermissionsApi } from './permissions-api.js';

export type SyncEngineLevelParams = {
agent?: Web5PlatformAgent;
Expand Down Expand Up @@ -64,15 +65,15 @@ export class SyncEngineLevel implements SyncEngine {
/**
* An instance of the `AgentPermissionsApi` that is used to interact with permissions grants used during sync
*/
private _cachedPermissionsApi: CachedPermissions;
private _permissionsApi: PermissionsApi;;

private _db: AbstractLevel<string | Buffer | Uint8Array>;
private _syncIntervalId?: ReturnType<typeof setInterval>;
private _ulidFactory: ULIDFactory;

constructor({ agent, dataPath, db }: SyncEngineLevelParams) {
this._agent = agent;
this._cachedPermissionsApi = new CachedPermissions({ agent: agent as Web5Agent, cachedDefault: true });
this._permissionsApi = new AgentPermissionsApi({ agent: agent as Web5Agent });
this._db = (db) ? db : new Level<string, string>(dataPath ?? 'DATA/AGENT/SYNC_STORE');
this._ulidFactory = monotonicFactory();
}
Expand All @@ -93,11 +94,11 @@ export class SyncEngineLevel implements SyncEngine {

set agent(agent: Web5PlatformAgent) {
this._agent = agent;
this._cachedPermissionsApi = new CachedPermissions({ agent: agent as Web5Agent, cachedDefault: true });
this._permissionsApi = new AgentPermissionsApi({ agent: agent as Web5Agent });
}

public async clear(): Promise<void> {
await this._cachedPermissionsApi.clear();
await this._permissionsApi.clear();
await this._db.clear();
}

Expand Down Expand Up @@ -133,11 +134,12 @@ export class SyncEngineLevel implements SyncEngine {
let granteeDid: string | undefined;
if (delegateDid) {
try {
const messagesReadGrant = await this._cachedPermissionsApi.getPermission({
const messagesReadGrant = await this._permissionsApi.getPermissionForRequest({
connectedDid : did,
messageType : DwnInterface.MessagesRead,
delegateDid,
protocol,
cached : true
});

permissionGrantId = messagesReadGrant.grant.id;
Expand Down Expand Up @@ -402,11 +404,12 @@ export class SyncEngineLevel implements SyncEngine {
if (delegateDid) {
// fetch the grants for the delegate DID
try {
const messagesQueryGrant = await this._cachedPermissionsApi.getPermission({
const messagesQueryGrant = await this._permissionsApi.getPermissionForRequest({
connectedDid : did,
messageType : DwnInterface.MessagesQuery,
delegateDid,
protocol,
cached : true
});

permissionGrantId = messagesQueryGrant.grant.id;
Expand Down Expand Up @@ -469,11 +472,12 @@ export class SyncEngineLevel implements SyncEngine {
let permissionGrantId: string | undefined;
if (delegateDid) {
try {
const messagesReadGrant = await this._cachedPermissionsApi.getPermission({
const messagesReadGrant = await this._permissionsApi.getPermissionForRequest({
connectedDid : author,
messageType : DwnInterface.MessagesRead,
delegateDid,
protocol,
cached : true
});

permissionGrantId = messagesReadGrant.grant.id;
Expand Down
1 change: 1 addition & 0 deletions packages/agent/src/test-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class PlatformAgentTestHarness {
await this.dwnResumableTaskStore.clear();
await this.syncStore.clear();
await this.vaultStore.clear();
await this.agent.permissions.clear();
this.dwnStores.clear();

// Reset the indexes and caches for the Agent's DWN data stores.
Expand Down
21 changes: 20 additions & 1 deletion packages/agent/src/types/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DwnDataEncodedRecordsWriteMessage, DwnPermissionGrant, DwnPermissionRequest, DwnPermissionScope } from './dwn.js';
import { DwnDataEncodedRecordsWriteMessage, DwnInterface, DwnPermissionGrant, DwnPermissionRequest, DwnPermissionScope } from './dwn.js';

export type FetchPermissionsParams = {
author: string;
Expand Down Expand Up @@ -63,7 +63,21 @@ export type CreateRevocationParams = {
description?: string;
}

export type GetPermissionParams = {
connectedDid: string;
delegateDid: string;
messageType: DwnInterface;
protocol?: string;
cached?: boolean;
delegate?: boolean;
}

export interface PermissionsApi {
/**
* Get the permission grant for a given author, target, and protocol. To be used when authoring delegated requests.
*/
getPermissionForRequest: (params: GetPermissionParams) => Promise<PermissionGrantEntry>;

/**
* Fetch all grants for a given author and target, optionally filtered by a specific grantee, grantor, or protocol.
*/
Expand Down Expand Up @@ -93,4 +107,9 @@ export interface PermissionsApi {
* Create a new permission revocation, optionally storing it in the DWN.
*/
createRevocation(params: CreateRevocationParams): Promise<PermissionRevocationEntry>;

/**
* Clears the cache of matched permissions.
*/
clear: () => Promise<void>;
}
Loading
Loading