-
Notifications
You must be signed in to change notification settings - Fork 22
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
feat!: introduce new administrative capabilities #832
Merged
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
51bf9aa
feat: introduce new administrative capabilities
travis 56d1db1
feat: port blocking logic to this implementation
travis ba3cfcf
feat: add in-memory implementation of rate limits storage
travis 340ccd1
feat: add types for new capability service definitions
travis 2d1133a
feat: add new capability types to service definition
travis d2766fa
feat: two more typing tweaks
travis 23793d5
feat: small naming tweak
travis 15576fb
fix: get types checking and a simple test implemented
travis a92702f
Merge branch 'main' into feat/rate-limits
travis 58714b2
chore: pnpm format
travis ef9042c
fix: more type updates
travis 4979330
feat: implement new ProvisionsStorage methods
travis fcee024
feat: limit `rate-limit/remove` to a single ID
travis 05ba4f7
feat: move areAnyBlocked to a helper function
travis a83335b
fix: fix bug caught in manual testing
travis 333ae9f
feat: invoke Space.allocate in Store.add
travis 0cd9397
fix: fix logic bug in the new `areAnyBlocked` utility function
travis 44a28df
chore: add tests for new capabilities
travis fe1d5fc
chore: add tests for rate limit invocation handlers
travis c2e6ea5
feat: remove access verifier and get tests running
travis 1be4e8b
chore: minor cleanup from self-review
travis 80fbbae
chore: make code more prettier
travis 1142f36
feat: add test suite for RateLimitsStorage implementations
travis e34e206
fix: use context.id rather than context.signer
travis 6c0a7a3
Merge branch 'main' into feat/rate-limits
travis 6103531
Update packages/capabilities/src/rate-limit.js
travis 5c77c44
Update packages/capabilities/src/rate-limit.js
travis eb8a827
Update packages/capabilities/src/rate-limit.js
travis 9e76024
Merge branch 'main' into feat/rate-limits
travis 944eb4b
feat: add tests for rate limit capability escalation
travis dd23341
fix: add comments for blocking behavior
travis 261a2a4
fix: stylistic tweaks from PR
travis 6526b9f
feat: tweak rate limit checker semantics
travis 362a698
fix: use prettier to make the code more prettier
travis 1575b92
chore: add comment explaining RateLimitID
travis 22b7a69
fix: per PR feedback use allocate in upload/add for consistency
travis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,71 @@ | ||
/** | ||
* Rate Limit Capabilities | ||
* | ||
* These can be imported directly with: | ||
* ```js | ||
* import * as RateLimit from '@web3-storage/capabilities/rate-limit' | ||
* ``` | ||
* | ||
* @module | ||
*/ | ||
import { capability, DID, struct, Schema, ok } from '@ucanto/validator' | ||
import { equalWith, and, equal } from './utils.js' | ||
|
||
// e.g. did:web:web3.storage or did:web:staging.web3.storage | ||
export const Provider = DID | ||
|
||
/** | ||
* Capability can be invoked by the provider or an authorized delegate to add a rate limit to a subject. | ||
*/ | ||
export const add = capability({ | ||
can: 'rate-limit/add', | ||
with: Provider, | ||
nb: struct({ | ||
subject: Schema.string(), | ||
rate: Schema.number(), | ||
}), | ||
derives: (child, parent) => { | ||
return ( | ||
and(equalWith(child, parent)) || | ||
and(equal(child.nb.subject, parent.nb.subject, 'subject')) || | ||
and(equal(child.nb.rate, parent.nb.rate, 'rate')) || | ||
ok({}) | ||
) | ||
}, | ||
}) | ||
|
||
/** | ||
* Capability can be invoked by the provider are an authorized delegate to remove rate limits from a subject. | ||
*/ | ||
export const remove = capability({ | ||
can: 'rate-limit/remove', | ||
with: Provider, | ||
nb: struct({ | ||
id: Schema.string(), | ||
}), | ||
derives: (child, parent) => { | ||
return ( | ||
and(equalWith(child, parent)) || | ||
and(equal(child.nb.id, parent.nb.id, 'id')) || | ||
ok({}) | ||
) | ||
}, | ||
}) | ||
|
||
/** | ||
* Capability can be invoked by the provider or an authorized delegate to list rate limits on the given subject | ||
*/ | ||
export const list = capability({ | ||
can: 'rate-limit/list', | ||
with: Provider, | ||
nb: struct({ | ||
subject: Schema.string(), | ||
}), | ||
derives: (child, parent) => { | ||
return ( | ||
and(equalWith(child, parent)) || | ||
and(equal(child.nb.subject, parent.nb.subject, 'subject')) || | ||
ok({}) | ||
) | ||
}, | ||
}) |
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,23 @@ | ||
import { capability, DID, struct, ok, Schema } from '@ucanto/validator' | ||
import { equalWith, and, equal } from './utils.js' | ||
|
||
// e.g. did:web:web3.storage or did:web:staging.web3.storage | ||
export const ProviderDID = DID.match({ method: 'web' }) | ||
|
||
/** | ||
* Capability can be invoked by a provider to get information about a subscription. | ||
*/ | ||
export const get = capability({ | ||
can: 'subscription/get', | ||
with: ProviderDID, | ||
nb: struct({ | ||
subscription: Schema.string(), | ||
}), | ||
derives: (child, parent) => { | ||
return ( | ||
and(equalWith(child, parent)) || | ||
and(equal(child.nb.subscription, parent.nb.subscription, 'consumer')) || | ||
ok({}) | ||
) | ||
}, | ||
}) |
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 |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import type { TupleToUnion } from 'type-fest' | ||
import * as Ucanto from '@ucanto/interface' | ||
import type { Schema } from '@ucanto/core' | ||
import { InferInvokedCapability, Unit, DID } from '@ucanto/interface' | ||
import { InferInvokedCapability, Unit, DID, DIDKey } from '@ucanto/interface' | ||
import type { PieceLink } from '@web3-storage/data-segment' | ||
import { space, info, recover, recoverValidation } from './space.js' | ||
import * as provider from './provider.js' | ||
|
@@ -10,9 +10,16 @@ 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' | ||
import * as CustomerCaps from './customer.js' | ||
import * as ConsumerCaps from './consumer.js' | ||
import * as SubscriptionCaps from './subscription.js' | ||
import * as RateLimitCaps from './rate-limit.js' | ||
import * as FilecoinCaps from './filecoin.js' | ||
|
||
export type { Unit } | ||
|
||
export type AccountDID = DID<'mailto'> | ||
|
||
/** | ||
* failure due to a resource not having enough storage capacity. | ||
*/ | ||
|
@@ -21,6 +28,11 @@ export interface InsufficientStorage { | |
message: string | ||
} | ||
|
||
export interface UnknownProvider extends Ucanto.Failure { | ||
name: 'UnknownProvider' | ||
did: DID | ||
} | ||
|
||
export type PieceLinkSchema = Schema.Schema<PieceLink> | ||
|
||
// Access | ||
|
@@ -67,6 +79,75 @@ export interface InvalidProvider extends Ucanto.Failure { | |
name: 'InvalidProvider' | ||
} | ||
|
||
// Customer | ||
export type CustomerGet = InferInvokedCapability<typeof CustomerCaps.get> | ||
export interface CustomerGetSuccess { | ||
did: AccountDID | ||
} | ||
export interface CustomerNotFound extends Ucanto.Failure { | ||
name: 'CustomerNotFound' | ||
} | ||
export type CustomerGetFailure = CustomerNotFound | Ucanto.Failure | ||
|
||
// Consumer | ||
export type ConsumerHas = InferInvokedCapability<typeof ConsumerCaps.has> | ||
export type ConsumerHasSuccess = boolean | ||
export type ConsumerHasFailure = Ucanto.Failure | ||
export type ConsumerGet = InferInvokedCapability<typeof ConsumerCaps.get> | ||
export interface ConsumerGetSuccess { | ||
did: DIDKey | ||
allocated: number | ||
total: number | ||
subscription: string | ||
} | ||
export interface ConsumerNotFound extends Ucanto.Failure { | ||
name: 'ConsumerNotFound' | ||
} | ||
export type ConsumerGetFailure = ConsumerNotFound | Ucanto.Failure | ||
|
||
// Subscription | ||
export type SubscriptionGet = InferInvokedCapability< | ||
typeof SubscriptionCaps.get | ||
> | ||
export interface SubscriptionGetSuccess { | ||
customer: AccountDID | ||
consumer: DIDKey | ||
} | ||
export interface SubscriptionNotFound extends Ucanto.Failure { | ||
name: 'SubscriptionNotFound' | ||
} | ||
export type SubscriptionGetFailure = | ||
| SubscriptionNotFound | ||
| UnknownProvider | ||
| Ucanto.Failure | ||
|
||
// Rate Limit | ||
export type RateLimitAdd = InferInvokedCapability<typeof RateLimitCaps.add> | ||
export interface RateLimitAddSuccess { | ||
id: string | ||
} | ||
export type RateLimitAddFailure = Ucanto.Failure | ||
|
||
export type RateLimitRemove = InferInvokedCapability< | ||
typeof RateLimitCaps.remove | ||
> | ||
export type RateLimitRemoveSuccess = Unit | ||
|
||
export interface RateLimitsNotFound extends Ucanto.Failure { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: RateLimits is plural here but singular in all other types |
||
name: 'RateLimitsNotFound' | ||
} | ||
export type RateLimitRemoveFailure = RateLimitsNotFound | Ucanto.Failure | ||
|
||
export type RateLimitList = InferInvokedCapability<typeof RateLimitCaps.list> | ||
export interface RateLimitSubject { | ||
id: string | ||
rate: number | ||
} | ||
export interface RateLimitListSuccess { | ||
limits: RateLimitSubject[] | ||
} | ||
export type RateLimitListFailure = Ucanto.Failure | ||
|
||
// Space | ||
export type Space = InferInvokedCapability<typeof space> | ||
export type SpaceInfo = InferInvokedCapability<typeof info> | ||
|
@@ -170,6 +251,13 @@ export type AbilitiesArray = [ | |
Access['can'], | ||
AccessAuthorize['can'], | ||
AccessSession['can'], | ||
CustomerGet['can'], | ||
ConsumerHas['can'], | ||
ConsumerGet['can'], | ||
SubscriptionGet['can'], | ||
RateLimitAdd['can'], | ||
RateLimitRemove['can'], | ||
RateLimitList['can'], | ||
FilecoinAdd['can'], | ||
AggregateAdd['can'], | ||
DealAdd['can'], | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capability can be invoked by the provider **or** an authorized delegate