-
Notifications
You must be signed in to change notification settings - Fork 12
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
Design & Implement Validation Helpers #21
Comments
@matheus23 thoughts? (Other than the fact that I guarantee that the above code will fail to typecheck) |
So I've been noodling a bit on this (as well as trying to implement e.g. the wnfs capability as defined in wnfs2 in some ways). There's two difficult things to deal with when it comes to custom capabilities:
|
cc @bgins |
Yeah, good point. This is a whole "parse don't validate". We should make the type return a |
Great idea — we can pass that into the checking interface by threading it for the validation functions. 👍 ...alternately, we could automatically upgrade the format while validating. Feels like there may be more edge cases there, but it's less manual for the end developer |
Yeah my thinking on this was that we can't update the format inside For now, I've tackled a reduced version of this. I.e. a "compare any" version. I can see ways of abstracting this further, but I'll wait for a good concrete use case before I'll abstract it in some possibly weird way 😛
I'll close this until we're ready to expand the API with some additional building blocks for custom capabilities ✌️ |
Reopening, because it's still a live discussion with @Gozala |
I'm bit confused about this given the following function signature:
Is it meant to be following instead ? function compareAny(targetCap: MyCapability, proofCaps: Capability[]): ValidationStatus |
I'll focus on Specifically I would imagine that in some cases Additionally I like how @matheus23's design introduced |
I end up writing what I think would be a best API for this also including inline below /**
* Function checks if the claimed capability is met by given set of capaibilites. Note that function takes capability
* views as opposed to raw capaibilites (This implies that raw JSON capabilites were succesfully parsed into known
* capability with specific semantics).
*
* Function returns either succesfull sets of proofs that support claimed capability or a set of escalation errors.
* If claimed capability is unfunded, meaning give capabilities are not comparable result is still an error of empty
* escalation set (Maybe we should consider union with 3 variants instead to make this more explicit)
*
* Please note that it is possible that claim may be met by some capaibilites while at the same time it may
* escalate constraint of others. In this case function still returns succesfull set of proofs discarding
* violations (maybe it should not ?).
*
* Note: succesfull result provides iterator of proofs. That allows constraint solver to be lazy as in
* return succesfully as soon as first proof is found and defer finding other proofs until constraint
* solver deciedes to explore other paths.
*/
declare function claim<C extends CapabilityView> (capability:C, given:C[]): Result<EscalationError<C>[], IterableIterator<Proof<C>>>
/**
* Represents succesfully parsed capability. Idea is that user could provide capability parser that UCAN
* library will use to filter out capaibilites that can be compared from the ones it is unable to
* recognize.
*/
interface CapabilityView<C extends Capability = Capability> {
capability: C
}
/**
* Basically any JSON value, but could be refined further if desired
*/
interface Capability { [key:string]: string|number|boolean|null|Capability|Capability[] }
type Result<X, T> =
| { ok: true, value: T }
| { ok: false, value: X }
/**
* Proof of a claimed capability, contains claimed capability (view) and subset of available
* capaibilites that satisfy it.
*/
interface Proof<C> {
claimed: C
capaibilites: C[]
}
/**
* Represents capability escalation and contains non empty set of
* contstraint violations.
*/
export interface EscalationError<C> extends RangeError {
readonly name: "EscalationError"
/**
* claimed capability
*/
readonly claimed: C
/**
* escalated capability
*/
readonly escalated: C
/**
* non empty set of constraint violations
*/
readonly violations: ConstraintViolationError<C>[]
}
/**
* Represents specific constraint violation by the claimed capability.
*/
export interface ConstraintViolationError<C> extends RangeError {
readonly name: "ConstraintViolationError"
/**
* Constraint that was violated.
*/
readonly claimed: Constraint<C>
/**
* Claim that exceeds imposed constraint.
*/
readonly violated: Constraint<C>
}
/**
* Represents violated constraint in the claim. Carrynig information about
* which specific constraint.
*/
export interface Constraint<C> {
readonly capability: C
readonly name: string
readonly value: unknown
}
// -----------------
/**
* Function attempt to access capability from given UCAN view (which in nutshell is UCAN with attached capability parser
* so it can map known capabilites to richer views and surface unknown capabilities). It returns is either successful result
* with capability view and authorization proof chain or an error describing reason why requested capability is invaid.
*
* Access internally utilized `claim` function and walks up the proof chain until it is able to proove that claim is unfounded.
*/
declare function access <C extends CapabilityView>(capability:Capability, ucan:UCANView<C>):Result<InvalidClaim<C>, Access<C>>
interface InvalidClaim<C> {
readonly name: 'ExpriedClaim'
readonly claim: C
readonly by: DID
// I know to is broken english but "from" is too ambigius as it can be "claim from gozala" or "gozala claimed car from robox"
readonly to: DID
reason: UnfundedClaim<C> | ExpriedClaim<C> | InactiveClaim<C> | ViolatingClaim<C> | InvalidClaim<C>
}
interface ExpriedClaim<C> {
readonly name: 'ExpriedClaim'
readonly by: DID
readonly to: DID
readonly claim: C
expiredAt: Time
}
interface InactiveClaim<C> {
readonly name: 'InactiveClaim'
readonly from: DID
readonly to: DID
readonly claim: C
activeAt: Time
}
interface UnfundedClaim<C> {
readonly name: 'UnfundedClaim'
readonly claim: C
readonly by: DID
readonly to: DID
}
interface ViolatingClaim<C> {
readonly name: 'ViolatingClaim'
readonly from: DID
readonly to: DID
readonly claim: C
readonly escalates: EscalationError<C>[]
}
interface Access<C> {
ok: true
capability: C
to: DID
proof: Authorization<C>
}
interface Authorization<C> {
by: DID
granted: C[]
proof: Authorization<C> | null
}
interface DID {}
interface UCANView<C extends CapabilityView> {
ucan: UCAN
capabilityParser: CapabilityParser<C>
// capabilities that parser was unable to parse
unkownCapabilities: IterableIterator<Capability>
}
interface UCAN {}
interface CapabilityParser<C> {
/**
* Returns either succesfully parsed capability or unknown capability back
*/
parse(capability:Capability): Result<Capability, C>
}
type Time = number With such a claim in place Neither deal with unknown capabilities, I think it's better done in layer above which in my sketch is |
One other thing that I would like to try is to define if (validate(request.ucan).ok && canWriteTo(request.path, request.ucan)) {
fs.write(request.path, request.content)
} it could turn into const access = access({ with: request.path, can: 'write' }, ucan)
if (access.ok) {
access.capability.write(request.path, request.content)
} |
Yeah, the naming could likely be better. This is very much a high level sketch. From the initial description:
Or to put it another way:
|
Hey @Gozala. First of all: Thank you for the work you're putting into this, I really appreciate it! It's good to have more eyes & brains on this problem. I'm currently working on upgrading our haskell code to UCAN version 0.7/0.8, and I'm collecting thoughts from my experience there & from talking with you into another round of iteration on ts-ucan. Some ideas I'm sure I'll straight up steal from you (:stuck_out_tongue_winking_eye:):
Looking at your proposal, it looks like these are the things you really want to have in ts-ucan:
I'll be looking at these things, especially in the bigger view of where Is there anything in particular that's blocking you from proceeding @Gozala? The current API should be sufficient for implementing a service that accepts UCANs, although I might be missing something. Should we maybe get on a call to discuss this in real-time? |
This is being implemented in PR #79 |
Manual validation is a bad experience. We should provide a top level
Here,
compare
can be expressed in terms ofcompareAll
.Compare All
Compares the focused capability against the entire list of proof capabilities. This is especially useful for cases of rights amplification, where more than one proof is needed.
All of the compare functions should get translated into
compareAll
under the hoodCompare Any
Especially useful when there's a version update or some backwards compatible change. Compares the focused capability one-at-a-time with the proof's capabilities, but checks against all of them.
Compare (Exact)
The usual case. Compare only if the capabilities are in the same namespace/semantics.
The text was updated successfully, but these errors were encountered: