generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is the first in a series of PRs that enable users to use permission grants via the web5 agent. In a subsequent PR these features will be used within `@web5/api` to fetch and cache the grants, and use them accordingly. The same will happen for the `sync manager`. Each higher-level abstraction that calls `processMessage` will manage it's own fetching/caching and using of the grants themselves. Within this PR: - a `granteeDid` optional property has been added to `ProcessDwnRequest` representing the DID that is signing the message and has been granted a permission. - a `signAsOwnerDelegate` optional property has been added to `ProcessDwnRequest` showing intent for signing as an owner using a delegate grant. Some temporary helper methods have been added to the `dwn` api that will eventually move to it's own `permissions` api in a subsequent PR. These include: - `fetchGrants` - `isGrantRevoked` - `createGrant` - `createRevocation` A utility method `matchGrantFromArray` has been added to the general utilities, this may be moved to a static method within the `permissions` api in a subsequent PR.
- Loading branch information
1 parent
51ac075
commit 1fee7a2
Showing
14 changed files
with
2,052 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
"@web5/agent": patch | ||
"@web5/identity-agent": patch | ||
"@web5/proxy-agent": patch | ||
"@web5/user-agent": patch | ||
--- | ||
|
||
Apply logic to sign messages with grants, add utils for dealing with grants |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { DataEncodedRecordsWriteMessage, MessagesPermissionScope, PermissionGrant, PermissionScope, PermissionsProtocol, ProtocolPermissionScope, RecordsPermissionScope } from '@tbd54566975/dwn-sdk-js'; | ||
import { DwnInterface } from './types/dwn.js'; | ||
import { isRecordsType } from './dwn-api.js'; | ||
|
||
export class DwnPermissionsUtil { | ||
|
||
static permissionsProtocolParams(type: 'grant' | 'revoke' | 'request'): { protocol: string, protocolPath: string } { | ||
const protocolPath = type === 'grant' ? PermissionsProtocol.grantPath : | ||
type === 'revoke' ? PermissionsProtocol.revocationPath : PermissionsProtocol.requestPath; | ||
return { | ||
protocol: PermissionsProtocol.uri, | ||
protocolPath, | ||
}; | ||
} | ||
|
||
/** | ||
* Matches the appropriate grant from an array of grants based on the provided parameters. | ||
* | ||
* @param delegated if true, only delegated grants are turned, if false all grants are returned including delegated ones. | ||
*/ | ||
static async matchGrantFromArray<T extends DwnInterface>( | ||
grantor: string, | ||
grantee: string, | ||
messageParams: { | ||
messageType: T, | ||
protocol?: string, | ||
protocolPath?: string, | ||
contextId?: string, | ||
}, | ||
grants: DataEncodedRecordsWriteMessage[], | ||
delegated: boolean = false | ||
): Promise<{ message: DataEncodedRecordsWriteMessage, grant: PermissionGrant } | undefined> { | ||
for (const grant of grants) { | ||
const grantData = await PermissionGrant.parse(grant); | ||
// only delegated grants are returned | ||
if (delegated === true && grantData.delegated !== true) { | ||
continue; | ||
} | ||
const { messageType, protocol, protocolPath, contextId } = messageParams; | ||
|
||
if (this.matchScopeFromGrant(grantor, grantee, messageType, grantData, protocol, protocolPath, contextId)) { | ||
return { message: grant, grant: grantData }; | ||
} | ||
} | ||
} | ||
|
||
private static matchScopeFromGrant<T extends DwnInterface>( | ||
grantor: string, | ||
grantee: string, | ||
messageType: T, | ||
grant: PermissionGrant, | ||
protocol?: string, | ||
protocolPath?: string, | ||
contextId?: string | ||
): boolean { | ||
// Check if the grant matches the provided parameters | ||
if (grant.grantee !== grantee || grant.grantor !== grantor) { | ||
return false; | ||
} | ||
|
||
const scope = grant.scope; | ||
const scopeMessageType = scope.interface + scope.method; | ||
if (scopeMessageType === messageType) { | ||
if (isRecordsType(messageType)) { | ||
const recordScope = scope as RecordsPermissionScope; | ||
if (!this.matchesProtocol(recordScope, protocol)) { | ||
return false; | ||
} | ||
|
||
// If the grant scope is not restricted to a specific context or protocol path, it is unrestricted and can be used | ||
if (this.isUnrestrictedProtocolScope(recordScope)) { | ||
return true; | ||
} | ||
|
||
// protocolPath and contextId are mutually exclusive | ||
// If the permission is scoped to a protocolPath and the permissionParams matches that path, this grant can be used | ||
if (recordScope.protocolPath !== undefined && recordScope.protocolPath === protocolPath) { | ||
return true; | ||
} | ||
|
||
// If the permission is scoped to a contextId and the permissionParams starts with that contextId, this grant can be used | ||
if (recordScope.contextId !== undefined && contextId?.startsWith(recordScope.contextId)) { | ||
return true; | ||
} | ||
} else { | ||
const messagesScope = scope as MessagesPermissionScope | ProtocolPermissionScope; | ||
if (this.protocolScopeUnrestricted(messagesScope)) { | ||
return true; | ||
} | ||
|
||
if (!this.matchesProtocol(messagesScope, protocol)) { | ||
return false; | ||
} | ||
|
||
return this.isUnrestrictedProtocolScope(messagesScope); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private static matchesProtocol(scope: PermissionScope & { protocol?: string }, protocol?: string): boolean { | ||
return scope.protocol !== undefined && scope.protocol === protocol; | ||
} | ||
|
||
/** | ||
* Checks if the scope is restricted to a specific protocol | ||
*/ | ||
private static protocolScopeUnrestricted(scope: PermissionScope & { protocol?: string }): boolean { | ||
return scope.protocol === undefined; | ||
} | ||
|
||
private static isUnrestrictedProtocolScope(scope: PermissionScope & { contextId?: string, protocolPath?: string }): boolean { | ||
return scope.contextId === undefined && scope.protocolPath === undefined; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.