@statewalker/webrun-biscuit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/authorizer.d.ts +112 -0
- package/dist/authorizer.d.ts.map +1 -0
- package/dist/base64.d.ts +3 -0
- package/dist/base64.d.ts.map +1 -0
- package/dist/builder.d.ts +52 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/crypto.d.ts +22 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/datalog.d.ts +203 -0
- package/dist/datalog.d.ts.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3130 -0
- package/dist/parser.d.ts +80 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/print.d.ts +12 -0
- package/dist/print.d.ts.map +1 -0
- package/dist/proto.d.ts +132 -0
- package/dist/proto.d.ts.map +1 -0
- package/dist/version.d.ts +42 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/authorizer.ts +581 -0
- package/src/base64.ts +42 -0
- package/src/builder.ts +360 -0
- package/src/crypto.ts +208 -0
- package/src/datalog.ts +722 -0
- package/src/index.ts +93 -0
- package/src/parser.ts +606 -0
- package/src/print.ts +147 -0
- package/src/proto.ts +770 -0
- package/src/version.ts +158 -0
package/src/version.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Datalog block versioning.
|
|
3
|
+
*
|
|
4
|
+
* Every block declares the Datalog version it was generated at. An
|
|
5
|
+
* implementation must reject versions outside the supported window, and must
|
|
6
|
+
* reject a block that uses a feature newer than the version it declares —
|
|
7
|
+
* otherwise a token could smuggle newer semantics past an older verifier.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `get_schema_version` / `check_compatibility` in the reference
|
|
10
|
+
* implementation (biscuit-auth `src/datalog/mod.rs`).
|
|
11
|
+
*/
|
|
12
|
+
import {
|
|
13
|
+
BinaryOp as B,
|
|
14
|
+
type Check,
|
|
15
|
+
type Fact,
|
|
16
|
+
type Op,
|
|
17
|
+
type Predicate,
|
|
18
|
+
type Rule,
|
|
19
|
+
type Scope,
|
|
20
|
+
type Term,
|
|
21
|
+
UnaryOp as U,
|
|
22
|
+
} from "./datalog.js";
|
|
23
|
+
import type { CheckMsg } from "./proto.js";
|
|
24
|
+
|
|
25
|
+
export const MIN_SCHEMA_VERSION = 3;
|
|
26
|
+
export const MAX_SCHEMA_VERSION = 6;
|
|
27
|
+
export const DATALOG_3_1 = 4;
|
|
28
|
+
export const DATALOG_3_2 = 5;
|
|
29
|
+
export const DATALOG_3_3 = 6;
|
|
30
|
+
|
|
31
|
+
export class VersionError extends Error {}
|
|
32
|
+
|
|
33
|
+
export interface BlockContentLike {
|
|
34
|
+
facts: Fact[];
|
|
35
|
+
rules: Rule[];
|
|
36
|
+
checks: Check[];
|
|
37
|
+
scopes: Scope[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ----------------------------------------------------- feature detection */
|
|
41
|
+
|
|
42
|
+
/** null, or a set containing null — arrays and maps are deliberately not
|
|
43
|
+
* flagged here, matching the reference implementation */
|
|
44
|
+
const isV33Term = (t: Term): boolean =>
|
|
45
|
+
t.t === "null" || (t.t === "set" && t.v.some((x) => x.t === "null"));
|
|
46
|
+
|
|
47
|
+
const hasV33Predicate = (p: Predicate): boolean => p.terms.some(isV33Term);
|
|
48
|
+
|
|
49
|
+
const hasV31Op = (expressions: Op[][]): boolean =>
|
|
50
|
+
expressions.some((ops) =>
|
|
51
|
+
ops.some(
|
|
52
|
+
(op) =>
|
|
53
|
+
op.kind === "binary" &&
|
|
54
|
+
(op.op === B.BitwiseAnd ||
|
|
55
|
+
op.op === B.BitwiseOr ||
|
|
56
|
+
op.op === B.BitwiseXor ||
|
|
57
|
+
op.op === B.NotEqual),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const hasV33Op = (expressions: Op[][]): boolean =>
|
|
62
|
+
expressions.some((ops) =>
|
|
63
|
+
ops.some((op) => {
|
|
64
|
+
if (op.kind === "value") return isV33Term(op.value);
|
|
65
|
+
if (op.kind === "closure") return true;
|
|
66
|
+
if (op.kind === "unary") return op.op === U.TypeOf || op.op === U.Ffi;
|
|
67
|
+
return (
|
|
68
|
+
op.op === B.HeterogeneousEqual ||
|
|
69
|
+
op.op === B.HeterogeneousNotEqual ||
|
|
70
|
+
op.op === B.LazyAnd ||
|
|
71
|
+
op.op === B.LazyOr ||
|
|
72
|
+
op.op === B.All ||
|
|
73
|
+
op.op === B.Any ||
|
|
74
|
+
op.op === B.Ffi
|
|
75
|
+
);
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export interface BlockFeatures {
|
|
80
|
+
scopes: boolean;
|
|
81
|
+
v31: boolean;
|
|
82
|
+
checkAll: boolean;
|
|
83
|
+
v33: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function blockFeatures(c: BlockContentLike): BlockFeatures {
|
|
87
|
+
const queries = c.checks.flatMap((ch) => ch.queries);
|
|
88
|
+
|
|
89
|
+
const scopes =
|
|
90
|
+
c.scopes.length > 0 ||
|
|
91
|
+
c.rules.some((r) => r.scopes.length > 0) ||
|
|
92
|
+
queries.some((q) => q.scopes.length > 0);
|
|
93
|
+
|
|
94
|
+
const checkAll = c.checks.some((ch) => ch.kind === "all");
|
|
95
|
+
let v33 = c.checks.some((ch) => ch.kind === "reject");
|
|
96
|
+
|
|
97
|
+
const v31 =
|
|
98
|
+
c.rules.some((r) => hasV31Op(r.expressions)) || queries.some((q) => hasV31Op(q.expressions));
|
|
99
|
+
|
|
100
|
+
if (!v33)
|
|
101
|
+
v33 =
|
|
102
|
+
c.rules.some(
|
|
103
|
+
(r) => hasV33Predicate(r.head) || r.body.some(hasV33Predicate) || hasV33Op(r.expressions),
|
|
104
|
+
) || queries.some((q) => q.body.some(hasV33Predicate) || hasV33Op(q.expressions));
|
|
105
|
+
if (!v33) v33 = c.facts.some((f) => hasV33Predicate(f.predicate));
|
|
106
|
+
|
|
107
|
+
return { scopes, v31, checkAll, v33 };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** the lowest version that can legally carry this block's content */
|
|
111
|
+
export function requiredVersion(c: BlockContentLike): number {
|
|
112
|
+
const f = blockFeatures(c);
|
|
113
|
+
if (f.v33) return DATALOG_3_3;
|
|
114
|
+
if (f.scopes || f.v31 || f.checkAll) return DATALOG_3_1;
|
|
115
|
+
return MIN_SCHEMA_VERSION;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* -------------------------------------------------------------- validation */
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Rejects a block whose declared version is out of range, or which uses a
|
|
122
|
+
* feature newer than that version. `rawChecks` is needed because the gate on
|
|
123
|
+
* check kinds distinguishes an absent kind from an explicit `One`.
|
|
124
|
+
*/
|
|
125
|
+
export function validateBlockVersion(
|
|
126
|
+
declared: number | undefined,
|
|
127
|
+
rawChecks: CheckMsg[],
|
|
128
|
+
hasExternalKey: boolean,
|
|
129
|
+
content: BlockContentLike,
|
|
130
|
+
): void {
|
|
131
|
+
const version = declared ?? 0;
|
|
132
|
+
if (version < MIN_SCHEMA_VERSION || version > MAX_SCHEMA_VERSION)
|
|
133
|
+
throw new VersionError(
|
|
134
|
+
`unsupported datalog version ${version}: supported versions are ${MIN_SCHEMA_VERSION} to ${MAX_SCHEMA_VERSION}`,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (version < MAX_SCHEMA_VERSION) {
|
|
138
|
+
for (const c of rawChecks) {
|
|
139
|
+
if (version < DATALOG_3_1 && c.kind !== undefined)
|
|
140
|
+
throw new VersionError("check kinds are only supported on datalog v3.1+ blocks");
|
|
141
|
+
if (version < DATALOG_3_3 && c.kind === 2)
|
|
142
|
+
throw new VersionError("reject if is only supported in datalog v3.3+");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (version < DATALOG_3_2 && hasExternalKey)
|
|
147
|
+
throw new VersionError("third-party blocks are only supported in datalog v3.2+");
|
|
148
|
+
|
|
149
|
+
const f = blockFeatures(content);
|
|
150
|
+
if (version < DATALOG_3_1) {
|
|
151
|
+
if (f.scopes) throw new VersionError("scopes are only supported in datalog v3.1+");
|
|
152
|
+
if (f.v31)
|
|
153
|
+
throw new VersionError("bitwise operators and != are only supported in datalog v3.1+");
|
|
154
|
+
if (f.checkAll) throw new VersionError("check all is only supported in datalog v3.1+");
|
|
155
|
+
} else if (version < DATALOG_3_3 && f.v33) {
|
|
156
|
+
throw new VersionError("maps, arrays, null, closures are only supported in datalog v3.3+");
|
|
157
|
+
}
|
|
158
|
+
}
|