Skip to content

Commit

Permalink
feat(capabilities): implement access/authorize and ./update caps (#387)
Browse files Browse the repository at this point in the history
closes #385
  • Loading branch information
hugomrdias authored Jan 25, 2023
1 parent 7895a8a commit ebe1032
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 8 deletions.
17 changes: 10 additions & 7 deletions packages/capabilities/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
},
"exports": {
".": "./src/index.js",
"./types": "./dist/src/types.d.ts",
"./*": "./src/*.js"
"./*": "./src/*.js",
"./types": "./dist/src/types.d.ts"
},
"typesVersions": {
"*": {
Expand All @@ -34,20 +34,23 @@
"store": [
"dist/src/store"
],
"types": [
"dist/src/types"
],
"top": [
"dist/src/top"
],
"upload": [
"dist/src/upload"
],
"voucher": [
"dist/src/voucher"
],
"access": [
"dist/src/access"
],
"utils": [
"dist/src/utils"
],
"voucher": [
"dist/src/voucher"
"types": [
"dist/src/types"
]
}
},
Expand Down
102 changes: 102 additions & 0 deletions packages/capabilities/src/access.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Access Capabilities
*
* These can be imported directly with:
* ```js
* import * as Access from '@web3-storage/capabilities/access'
* ```
*
* @module
*/
import { capability, URI, DID } from '@ucanto/validator'
// @ts-ignore
// eslint-disable-next-line no-unused-vars
import * as Types from '@ucanto/interface'
import { equalWith, fail, equal } from './utils.js'
import { top } from './top.js'

export { top }

/**
* Account identifier.
*/
export const As = DID.match({ method: 'mailto' })

/**
* Capability can only be delegated (but not invoked) allowing audience to
* derived any `access/` prefixed capability for the agent identified
* by did:key in the `with` field.
*/
export const access = top.derive({
to: capability({
can: 'access/*',
with: URI.match({ protocol: 'did:' }),
derives: equalWith,
}),
derives: equalWith,
})

const base = top.or(access)

/**
* Capability can be invoked by an agent to request a `./update` for an account.
*
* `with` field identifies requesting agent, which MAY be different from iss field identifying issuing agent.
*/
export const authorize = base.derive({
to: capability({
can: 'access/authorize',
with: URI.match({ protocol: 'did:' }),
nb: {
/**
* Value MUST be a did:mailto identifier of the account
* that the agent wishes to represent via did:key in the `with` field.
* It MUST be a valid did:mailto identifier.
*/
as: As,
},
derives: (child, parent) => {
return (
fail(equalWith(child, parent)) ||
fail(equal(child.nb.as, parent.nb.as, 'as')) ||
true
)
},
}),
/**
* `access/authorize` can be derived from the `access/*` & `*` capability
* as long as the `with` fields match.
*/
derives: equalWith,
})

/**
* Issued by trusted authority (usually the one handling invocation that contains this proof)
* to the account (aud) to update invocation local state of the document.
*
* @see https://github.com/web3-storage/specs/blob/main/w3-account.md#update
*
* @example
* ```js
* {
iss: "did:web:web3.storage",
aud: "did:mailto:[email protected]",
att: [{
with: "did:web:web3.storage",
can: "./update",
nb: { key: "did:key:zAgent" }
}],
exp: null
sig: "..."
}
* ```
*/
export const session = capability({
can: './update',
// Should be web3.storage DID
with: URI.match({ protocol: 'did:' }),
nb: {
// Agent DID so it can sign UCANs as did:mailto if it matches this delegation `aud`
key: DID.match({ method: 'key' }),
},
})
4 changes: 4 additions & 0 deletions packages/capabilities/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Top from './top.js'
import * as Store from './store.js'
import * as Upload from './upload.js'
import * as Voucher from './voucher.js'
import * as Access from './access.js'
import * as Utils from './utils.js'

export { Space, Top, Store, Upload, Voucher, Utils }
Expand All @@ -24,4 +25,7 @@ export const abilitiesAsStrings = [
Store.list.can,
Voucher.claim.can,
Voucher.redeem.can,
Access.access.can,
Access.authorize.can,
Access.session.can,
]
13 changes: 12 additions & 1 deletion packages/capabilities/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { top } from './top.js'
import { add, list, remove, store } from './store.js'
import * as UploadCaps from './upload.js'
import { claim, redeem } from './voucher.js'
import * as AccessCaps from './access.js'

// Access
export type Access = InferInvokedCapability<typeof AccessCaps.access>
export type AccessAuthorize = InferInvokedCapability<
typeof AccessCaps.authorize
>
export type AccessSession = InferInvokedCapability<typeof AccessCaps.session>

// Space
export type Space = InferInvokedCapability<typeof space>
Expand Down Expand Up @@ -47,5 +55,8 @@ export type AbilitiesArray = [
StoreRemove['can'],
StoreList['can'],
VoucherClaim['can'],
VoucherRedeem['can']
VoucherRedeem['can'],
Access['can'],
AccessAuthorize['can'],
AccessSession['can']
]
Loading

0 comments on commit ebe1032

Please sign in to comment.