diff --git a/src/attenuation.ts b/src/attenuation.ts
index 828dfcd..fd9ace9 100644
--- a/src/attenuation.ts
+++ b/src/attenuation.ts
@@ -21,7 +21,7 @@ export interface CapabilitySemantics {
* - `null`: The capabilities from `parentCap` and `childCap` are unrelated and can't be compared nor delegated.
* - `CapabilityEscalation`: It's clear that `childCap` is meant to be delegated from `parentCap`, but there's a rights escalation.
*/
- tryDelegating(parentCap: T, childCap: T): T | null | CapabilityEscalation
+ tryDelegating(parentCap: A, childCap: A): A | null | CapabilityEscalation
// TODO builders
}
@@ -33,6 +33,17 @@ export interface CapabilityInfo {
}
+export interface CapabilityWithInfo {
+ info: CapabilityInfo
+ capability: A
+}
+
+
+export type CapabilityResult
+ = CapabilityWithInfo
+ | CapabilityEscalation
+
+
export interface CapabilityEscalation {
escalation: string // reason
capability: A // the capability that escalated rights
@@ -45,21 +56,16 @@ function isCapabilityEscalation(obj: unknown): obj is CapabilityEscalation
}
-export type CapabilityResult
- = A & CapabilityInfo
- | CapabilityEscalation
-
-
export function capabilities(
ucan: Chained,
capability: CapabilitySemantics,
): Iterable> {
- function* findParsingCaps(ucan: Ucan): Iterable {
+ function* findParsingCaps(ucan: Ucan): Iterable> {
const capInfo = parseCapabilityInfo(ucan)
for (const cap of ucan.payload.att) {
const parsedCap = capability.tryParsing(cap)
- if (parsedCap != null) yield { ...parsedCap, ...capInfo }
+ if (parsedCap != null) yield { info: capInfo, capability: parsedCap }
}
}
@@ -74,7 +80,7 @@ export function capabilities(
yield parsedParentCap
} else {
// try figuring out whether we can delegate the capabilities from this to the parent
- const delegated = capability.tryDelegating(parsedParentCap, parsedChildCap)
+ const delegated = capability.tryDelegating(parsedParentCap.capability, parsedChildCap.capability)
// if the capabilities *are* related, then this will be non-null
// otherwise we just continue looking
if (delegated != null) {
@@ -84,7 +90,10 @@ export function capabilities(
if (isCapabilityEscalation(delegated)) {
yield delegated // which is an escalation
} else {
- yield delegateCapabilityInfo({ ...parsedChildCap, ...delegated }, parsedParentCap)
+ yield {
+ info: delegateCapabilityInfo(parsedChildCap.info, parsedParentCap.info),
+ capability: delegated
+ }
}
}
}
@@ -103,19 +112,18 @@ export function capabilities(
return ucan.reduce(delegate)()
}
-function delegateCapabilityInfo(childCap: A, parentCap: A): A {
+function delegateCapabilityInfo(childInfo: CapabilityInfo, parentInfo: CapabilityInfo): CapabilityInfo {
let notBefore = {}
- if (childCap.notBefore != null && parentCap.notBefore != null) {
- notBefore = { notBefore: Math.max(childCap.notBefore, parentCap.notBefore) }
- } else if (parentCap.notBefore != null) {
- notBefore = { notBefore: parentCap.notBefore }
+ if (childInfo.notBefore != null && parentInfo.notBefore != null) {
+ notBefore = { notBefore: Math.max(childInfo.notBefore, parentInfo.notBefore) }
+ } else if (parentInfo.notBefore != null) {
+ notBefore = { notBefore: parentInfo.notBefore }
} else {
- notBefore = { notBefore: childCap.notBefore }
+ notBefore = { notBefore: childInfo.notBefore }
}
return {
- ...childCap,
- originator: parentCap.originator,
- expiresAt: Math.min(childCap.expiresAt, parentCap.expiresAt),
+ originator: parentInfo.originator,
+ expiresAt: Math.min(childInfo.expiresAt, parentInfo.expiresAt),
...notBefore,
}
}
diff --git a/src/capability/wnfs.ts b/src/capability/wnfs.ts
index 5714a45..eca89bc 100644
--- a/src/capability/wnfs.ts
+++ b/src/capability/wnfs.ts
@@ -57,23 +57,32 @@ export const wnfsPublicSemantics: CapabilitySemantics = {
}
},
- tryDelegating(parentCap: T, childCap: T): T | null | CapabilityEscalation {
+ tryDelegating(parentCap: WnfsPublicCapability, childCap: WnfsPublicCapability): WnfsPublicCapability | null | CapabilityEscalation {
// need to delegate the same user's file system
if (childCap.user !== parentCap.user) return null
// must not escalate capability level
if (wnfsCapLevels[childCap.cap] > wnfsCapLevels[parentCap.cap]) {
- return escalationPublic("Capability level escalation", childCap)
+ return {
+ escalation: "Capability level escalation",
+ capability: childCap,
+ }
}
// parentCap path must be a prefix of childCap path
if (childCap.publicPath.length < parentCap.publicPath.length) {
- return escalationPublic("WNFS Public path access escalation", childCap)
+ return {
+ escalation: "WNFS Public path access escalation",
+ capability: childCap,
+ }
}
for (let i = 0; i < parentCap.publicPath.length; i++) {
if (childCap.publicPath[i] !== parentCap.publicPath[i]) {
- return escalationPublic("WNFS Public path access escalation", childCap)
+ return {
+ escalation: "WNFS Public path access escalation",
+ capability: childCap,
+ }
}
}
@@ -134,7 +143,10 @@ const wnfsPrivateSemantics: CapabilitySemantics = {
// This escalation *could* be wrong, but we shouldn't assume they're unrelated either.
if (wnfsCapLevels[childCap.cap] > wnfsCapLevels[parentCap.cap]) {
- return escalationPrivate("Capability level escalation", childCap)
+ return {
+ escalation: "Capability level escalation",
+ capability: childCap,
+ }
}
return {
@@ -148,23 +160,3 @@ const wnfsPrivateSemantics: CapabilitySemantics = {
export function wnfsPrivateCapabilities(ucan: Chained) {
return capabilities(ucan, wnfsPrivateSemantics)
}
-
-
-
-// ㊙️
-
-
-function escalationPublic(reason: string, cap: T): CapabilityEscalation {
- return {
- escalation: reason,
- capability: { user: cap.user, publicPath: cap.publicPath, cap: cap.cap }
- }
-}
-
-
-function escalationPrivate(reason: string, cap: T): CapabilityEscalation {
- return {
- escalation: reason,
- capability: { user: cap.user, requiredINumbers: cap.requiredINumbers, cap: cap.cap }
- }
-}
diff --git a/tests/attenuation.test.ts b/tests/attenuation.test.ts
index 96637ce..cacbcc9 100644
--- a/tests/attenuation.test.ts
+++ b/tests/attenuation.test.ts
@@ -33,11 +33,15 @@ describe("attenuation.emailCapabilities", () => {
const emailCaps = Array.from(emailCapabilities(await Chained.fromToken(token.encode(ucan))))
expect(emailCaps).toEqual([{
- originator: alice.did(),
- expiresAt: Math.min(leafUcan.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafUcan.payload.nbf, ucan.payload.nbf),
- email: "alice@email.com",
- cap: "SEND"
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leafUcan.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafUcan.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ email: "alice@email.com",
+ cap: "SEND"
+ }
}])
})
@@ -62,11 +66,15 @@ describe("attenuation.emailCapabilities", () => {
// we implicitly expect the originator to become bob
expect(Array.from(emailCapabilities(await Chained.fromToken(token.encode(ucan))))).toEqual([{
- originator: bob.did(),
- expiresAt: ucan.payload.exp,
- notBefore: ucan.payload.nbf,
- email: "bob@email.com",
- cap: "SEND"
+ info: {
+ originator: bob.did(),
+ expiresAt: ucan.payload.exp,
+ notBefore: ucan.payload.nbf,
+ },
+ capability: {
+ email: "bob@email.com",
+ cap: "SEND"
+ }
}])
})
@@ -112,18 +120,26 @@ describe("attenuation.emailCapabilities", () => {
expect(Array.from(emailCapabilities(chained))).toEqual([
{
- originator: alice.did(),
- expiresAt: Math.min(leafUcanAlice.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafUcanAlice.payload.nbf, ucan.payload.nbf),
- email: "alice@email.com",
- cap: "SEND",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leafUcanAlice.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafUcanAlice.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ email: "alice@email.com",
+ cap: "SEND",
+ }
},
{
- originator: bob.did(),
- expiresAt: Math.min(leafUcanBob.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafUcanBob.payload.nbf, ucan.payload.nbf),
- email: "bob@email.com",
- cap: "SEND",
+ info: {
+ originator: bob.did(),
+ expiresAt: Math.min(leafUcanBob.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafUcanBob.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ email: "bob@email.com",
+ cap: "SEND",
+ }
}
])
})
@@ -162,18 +178,26 @@ describe("attenuation.emailCapabilities", () => {
expect(Array.from(emailCapabilities(chained))).toEqual([
{
- originator: alice.did(),
- expiresAt: Math.min(leafUcanAlice.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafUcanAlice.payload.nbf, ucan.payload.nbf),
- email: "alice@email.com",
- cap: "SEND",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leafUcanAlice.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafUcanAlice.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ email: "alice@email.com",
+ cap: "SEND",
+ }
},
{
- originator: bob.did(),
- expiresAt: Math.min(leafUcanBob.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafUcanBob.payload.nbf, ucan.payload.nbf),
- email: "alice@email.com",
- cap: "SEND",
+ info: {
+ originator: bob.did(),
+ expiresAt: Math.min(leafUcanBob.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafUcanBob.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ email: "alice@email.com",
+ cap: "SEND",
+ }
}
])
})
diff --git a/tests/capabilitiy/wnfs.test.ts b/tests/capabilitiy/wnfs.test.ts
index 5bc8672..3bf19ed 100644
--- a/tests/capabilitiy/wnfs.test.ts
+++ b/tests/capabilitiy/wnfs.test.ts
@@ -24,12 +24,16 @@ describe("wnfs public capability", () => {
expect(Array.from(wnfsPublicCapabilities(chain))).toEqual([
{
- originator: alice.did(),
- expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- publicPath: ["Apps", "appinator"],
- cap: "REVISE",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ publicPath: ["Apps", "appinator"],
+ cap: "REVISE",
+ }
}
])
})
@@ -81,12 +85,16 @@ describe("wnfs public capability", () => {
}
},
{
- originator: alice.did(),
- expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- publicPath: ["Apps", "appinator"],
- cap: "OVERWRITE",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ publicPath: ["Apps", "appinator"],
+ cap: "OVERWRITE",
+ }
}
])
})
@@ -109,12 +117,16 @@ describe("wnfs private capability", () => {
expect(Array.from(wnfsPrivateCapabilities(chain))).toEqual([
{
- originator: alice.did(),
- expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- requiredINumbers: new Set(["abc", "def"]),
- cap: "REVISE",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ requiredINumbers: new Set(["abc", "def"]),
+ cap: "REVISE",
+ }
}
])
})
@@ -171,12 +183,16 @@ describe("wnfs private capability", () => {
}
},
{
- originator: alice.did(),
- expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- requiredINumbers: new Set(["abc", "ghi"]),
- cap: "CREATE",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leaf.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leaf.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ requiredINumbers: new Set(["abc", "ghi"]),
+ cap: "CREATE",
+ }
}
])
})
@@ -201,20 +217,28 @@ describe("wnfs private capability", () => {
expect(Array.from(wnfsPrivateCapabilities(chain))).toEqual([
{
- originator: alice.did(),
- expiresAt: Math.min(leafAlice.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafAlice.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- requiredINumbers: new Set(["inumalice", "subinum"]),
- cap: "OVERWRITE",
+ info: {
+ originator: alice.did(),
+ expiresAt: Math.min(leafAlice.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafAlice.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ requiredINumbers: new Set(["inumalice", "subinum"]),
+ cap: "OVERWRITE",
+ }
},
{
- originator: bob.did(),
- expiresAt: Math.min(leafBob.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafBob.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- requiredINumbers: new Set(["inumbob", "subinum"]),
- cap: "OVERWRITE",
+ info: {
+ originator: bob.did(),
+ expiresAt: Math.min(leafBob.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafBob.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ requiredINumbers: new Set(["inumbob", "subinum"]),
+ cap: "OVERWRITE",
+ }
}
])
})
@@ -247,12 +271,16 @@ describe("wnfs private capability", () => {
}
},
{
- originator: bob.did(),
- expiresAt: Math.min(leafBob.payload.exp, ucan.payload.exp),
- notBefore: maxNbf(leafBob.payload.nbf, ucan.payload.nbf),
- user: "boris.fission.name",
- requiredINumbers: new Set(["inumbob", "subinum"]),
- cap: "OVERWRITE",
+ info: {
+ originator: bob.did(),
+ expiresAt: Math.min(leafBob.payload.exp, ucan.payload.exp),
+ notBefore: maxNbf(leafBob.payload.nbf, ucan.payload.nbf),
+ },
+ capability: {
+ user: "boris.fission.name",
+ requiredINumbers: new Set(["inumbob", "subinum"]),
+ cap: "OVERWRITE",
+ }
}
])
})