@loradb/lora-graphql 0.23.0 → 0.24.1
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/README.md +68 -13
- package/dist/analyze/access-lint.d.ts +7 -0
- package/dist/analyze/access.d.ts +13 -33
- package/dist/analyze/assert-schema.d.ts +19 -0
- package/dist/analyze/diff.d.ts +1 -1
- package/dist/analyze/plans.d.ts +1 -1
- package/dist/analyze/principals.d.ts +25 -0
- package/dist/analyze/verdicts.d.ts +51 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/compile/cache.d.ts +1 -1
- package/dist/compile/read/abstract.d.ts +19 -0
- package/dist/compile/read/aggregates.d.ts +34 -0
- package/dist/compile/read/by-keys.d.ts +10 -0
- package/dist/compile/read/common.d.ts +30 -0
- package/dist/compile/read/connection.d.ts +26 -0
- package/dist/compile/read/cypher-field.d.ts +21 -0
- package/dist/compile/read/match.d.ts +11 -0
- package/dist/compile/read/project.d.ts +15 -0
- package/dist/compile/read/root.d.ts +5 -0
- package/dist/compile/read/search.d.ts +16 -0
- package/dist/compile/read/sort.d.ts +42 -0
- package/dist/compile/read/types.d.ts +61 -0
- package/dist/{diff-B9iDdELF.js → diff-K8-GSeKH.js} +2 -2
- package/dist/diff-K8-GSeKH.js.map +1 -0
- package/dist/{driver-C5dSoSD8.js → driver-CXKPLfkz.js} +10796 -10254
- package/dist/driver-CXKPLfkz.js.map +1 -0
- package/dist/execute/cypher-mutation.d.ts +1 -1
- package/dist/execute/mutate/adjust.d.ts +5 -0
- package/dist/execute/mutate/checks.d.ts +21 -0
- package/dist/execute/mutate/delete.d.ts +7 -0
- package/dist/execute/mutate/env.d.ts +73 -0
- package/dist/execute/mutate/errors.d.ts +7 -0
- package/dist/execute/mutate/fragments.d.ts +37 -0
- package/dist/execute/mutate/plan.d.ts +95 -0
- package/dist/execute/mutate/runner.d.ts +129 -0
- package/dist/execute/mutate/unique-together.d.ts +18 -0
- package/dist/execute/mutate.d.ts +2 -79
- package/dist/index.d.ts +10 -6
- package/dist/index.js +2 -2
- package/dist/lora-graphql.d.ts +18 -328
- package/dist/model/build/authorization.d.ts +31 -0
- package/dist/model/build/cypher-field.d.ts +11 -0
- package/dist/model/build/directive-args.d.ts +16 -0
- package/dist/model/build/limits.d.ts +18 -0
- package/dist/model/build/relationship-field.d.ts +11 -0
- package/dist/model/build/rule-checks.d.ts +23 -0
- package/dist/model/build/rule-references.d.ts +28 -0
- package/dist/model/build/scalar-field.d.ts +7 -0
- package/dist/model/build/search.d.ts +4 -0
- package/dist/model/build/shapes.d.ts +18 -0
- package/dist/model/build/unique-together.d.ts +5 -0
- package/dist/model/build.d.ts +3 -20
- package/dist/model/directives.d.ts +1 -1
- package/dist/model/types.d.ts +12 -2
- package/dist/options.d.ts +255 -0
- package/dist/results.d.ts +70 -0
- package/dist/runtime/custom-requires.d.ts +7 -0
- package/dist/runtime/database-error.d.ts +10 -0
- package/dist/runtime/documents.d.ts +35 -0
- package/dist/runtime/limits.d.ts +37 -0
- package/dist/runtime/request.d.ts +33 -0
- package/dist/runtime/timing.d.ts +13 -0
- package/dist/schema/aggregates.d.ts +17 -0
- package/dist/schema/base.d.ts +17 -0
- package/dist/schema/build.d.ts +3 -42
- package/dist/schema/filters.d.ts +10 -0
- package/dist/schema/hooks.d.ts +42 -0
- package/dist/schema/objects.d.ts +36 -0
- package/dist/schema/query.d.ts +8 -0
- package/dist/schema/resolvers.d.ts +13 -0
- package/dist/schema/sorts.d.ts +5 -0
- package/dist/schema/subscriptions.d.ts +7 -0
- package/dist/subscriptions/change-hub.d.ts +50 -0
- package/dist/subscriptions/compiles.d.ts +23 -0
- package/dist/subscriptions/events.d.ts +9 -0
- package/dist/subscriptions/filter-cost.d.ts +15 -0
- package/dist/subscriptions/subscriptions.d.ts +73 -0
- package/dist/testing.d.ts +4 -1
- package/dist/testing.js +34 -29
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/dist/compile/read.d.ts +0 -102
- package/dist/diff-B9iDdELF.js.map +0 -1
- package/dist/driver-C5dSoSD8.js.map +0 -1
package/README.md
CHANGED
|
@@ -746,18 +746,24 @@ type Claims @jwt {
|
|
|
746
746
|
roles: [String!] @jwtClaim(path: "app_metadata.roles")
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
+
extend schema @authorizationDefaults(requireAuthentication: false)
|
|
750
|
+
|
|
749
751
|
type Post
|
|
750
752
|
@node
|
|
751
753
|
@mutation
|
|
752
754
|
@authentication(operations: [CREATE, UPDATE, DELETE])
|
|
753
755
|
@authorization(
|
|
754
756
|
filter: [
|
|
757
|
+
{ where: { node: { published: { eq: true } } } }
|
|
758
|
+
{ where: { node: { author: { key: { eq: "$jwt.sub" } } } } }
|
|
755
759
|
{
|
|
756
|
-
where: {
|
|
757
|
-
|
|
760
|
+
where: {
|
|
761
|
+
AND: [
|
|
762
|
+
{ jwt: { sub: { exists: true } } }
|
|
763
|
+
{ node: { tenant: { eq: "$context.tenant" } } }
|
|
764
|
+
]
|
|
765
|
+
}
|
|
758
766
|
}
|
|
759
|
-
{ where: { node: { author: { key: { eq: "$jwt.sub" } } } } }
|
|
760
|
-
{ where: { node: { tenant: { eq: "$context.tenant" } } } }
|
|
761
767
|
{ where: { jwt: { roles: { includes: "admin" } } } }
|
|
762
768
|
]
|
|
763
769
|
validate: [
|
|
@@ -791,19 +797,39 @@ type Post
|
|
|
791
797
|
}
|
|
792
798
|
```
|
|
793
799
|
|
|
800
|
+
- A rule takes `operations`, `when` (validate rules) and `where`; any
|
|
801
|
+
other field is a model error, so a misspelt `operations` cannot fall
|
|
802
|
+
back to the default unnoticed.
|
|
794
803
|
- A rule is `{ node, jwt, AND, OR, NOT }`. `node` is a filter over the type
|
|
795
804
|
(relationship properties included, through `<field>Connection: { some:
|
|
796
805
|
{ node, edge } }`), where `"$jwt.path"` strings become the caller's claims and
|
|
797
806
|
`"$context.path"` strings values from the GraphQL context. `jwt` tests
|
|
798
807
|
claims (`eq`, `in`, `includes`, `contains`, `startsWith`, `endsWith`,
|
|
799
808
|
`lt`, `lte`, `gt`, `gte`, `exists`).
|
|
809
|
+
- **Anonymous callers.** Without a token every rule denies, unless the
|
|
810
|
+
schema says `@authorizationDefaults(requireAuthentication: false)`. Then
|
|
811
|
+
a rule with a branch that reads no claims decides that branch for
|
|
812
|
+
anonymous callers too (the published posts above). A rule that needs
|
|
813
|
+
claims still denies them, and how depends on the kind of rule: a
|
|
814
|
+
validate rule asks for a token (`UNAUTHENTICATED`, not `FORBIDDEN`),
|
|
815
|
+
while a READ filter admits no row, so a signed-out caller reads an
|
|
816
|
+
empty list and the access matrix calls it `denied`.
|
|
817
|
+
It is one setting for the schema; a rule has no such field, and it
|
|
818
|
+
reaches every rule: a CREATE, UPDATE, DELETE or CONNECT rule that
|
|
819
|
+
reads no claims decides for signed-out callers as well. `check` names
|
|
820
|
+
each such write rule that no `@authentication` covers. To keep a
|
|
821
|
+
claim-free rule or branch for signed-in callers, test a claim beside it
|
|
822
|
+
(`{ jwt: { sub: { exists: true } } }`, as the tenant rule above does),
|
|
823
|
+
or put `@authentication` on the type or the relationship field.
|
|
800
824
|
- **The caller's own node.** Mark the claim that identifies the caller
|
|
801
825
|
with `@viewer(type: "Person", field: "subject")` (the field is `@key` or
|
|
802
826
|
`@unique`), so the key can stay a slug while the subject is an identity
|
|
803
827
|
provider's opaque id. Rules then say `{ node: { isViewer: true } }` on
|
|
804
828
|
the viewer type, or `{ node: { author: { isViewer: true } } }` through a
|
|
805
829
|
relationship; this expands to `{ subject: { eq: "$jwt.sub" } }` at
|
|
806
|
-
startup and compiles to exactly the same statement. `
|
|
830
|
+
startup and compiles to exactly the same statement. With `@viewer`
|
|
831
|
+
declared, writing that expansion out by hand is a model error: the
|
|
832
|
+
mapping lives in one place. `viewer: { verified:
|
|
807
833
|
{ eq: true } }` tests the caller's own node: one seek by the claim. Both
|
|
808
834
|
are unknown without the claim, so `NOT { isViewer: true }` never grants a
|
|
809
835
|
signed-out caller. `isViewer` takes `true` only; use `NOT` for the
|
|
@@ -1019,6 +1045,8 @@ deleted: Org.docs has onDelete: RESTRICT`. Replacing a single
|
|
|
1019
1045
|
covers an operation, it replaces the default for that operation, never
|
|
1020
1046
|
merges with it. `check()` fails on a `@mutation` type whose
|
|
1021
1047
|
writes nothing guards, unless it says `@authorization(public: [...])`.
|
|
1048
|
+
`requireAuthentication` (see "Anonymous callers" above) is the third
|
|
1049
|
+
setting here.
|
|
1022
1050
|
|
|
1023
1051
|
Relationship and `@cypher` fields take field-level `@authorization`
|
|
1024
1052
|
with READ validate rules: a row failing the rule reads the field as
|
|
@@ -1105,7 +1133,10 @@ type Trip @node @mutation {
|
|
|
1105
1133
|
- The rules hold whichever side the write comes from: a connect through
|
|
1106
1134
|
`Person.trips` (the same relationship type, the other direction)
|
|
1107
1135
|
answers to `Trip.members`' rules, with source and target as declared on
|
|
1108
|
-
`Trip.members`.
|
|
1136
|
+
`Trip.members`. Declare each operation's rules on one of the two
|
|
1137
|
+
fields: the same operation ruled on both is a model error (combine
|
|
1138
|
+
with `AND` where both must pass); different operations may sit on
|
|
1139
|
+
different sides.
|
|
1109
1140
|
- A relationship failing `READ_EDGE` reads its properties as `FORBIDDEN`.
|
|
1110
1141
|
Unless the claims alone settle the rule, nothing may filter, sort or
|
|
1111
1142
|
aggregate by that relationship's properties.
|
|
@@ -1417,17 +1448,32 @@ relationship property (READ, CREATE, UPDATE) and root `@cypher` field,
|
|
|
1417
1448
|
each operation as each kind of caller (anonymous, authenticated, and each role
|
|
1418
1449
|
the rules test, such as `roles:admin`), with the verdict (`allowed`,
|
|
1419
1450
|
`filtered`, `validated`, `masked`, `denied`, `unauthenticated`) and the
|
|
1420
|
-
rules that decide it.
|
|
1451
|
+
rules that decide it. When rules name the caller's node (`isViewer`),
|
|
1452
|
+
`viewer` is the caller on nodes that are theirs: every rule part that
|
|
1453
|
+
only names them passes, so it shows the most a related caller gets,
|
|
1454
|
+
where `authenticated` shows what any signed-in caller gets. A part under
|
|
1455
|
+
`NOT` stays as written and is decided per row, so `viewer` never reads
|
|
1456
|
+
below `authenticated`. A relationship field has a CONNECT row when a
|
|
1457
|
+
rule or `@authentication(operations: [CREATE_RELATIONSHIP])` guards it,
|
|
1458
|
+
and a DISCONNECT row for a rule or `DELETE_RELATIONSHIP`. A
|
|
1459
|
+
`@key(scope: VIEWER)` type's CREATE reads
|
|
1421
1460
|
`unauthenticated` for anonymous callers and `validated` by `key scope`
|
|
1422
1461
|
for the rest.
|
|
1423
1462
|
|
|
1424
|
-
`lora.operationAccess(document | persistedId, operationName?)`
|
|
1425
|
-
the same question for one operation: each root field (fragments
|
|
1463
|
+
`lora.operationAccess(document | persistedId, operationName?, variables?)`
|
|
1464
|
+
answers the same question for one operation: each root field (fragments
|
|
1426
1465
|
followed) with its type, the operations it needs (an upsert needs
|
|
1427
1466
|
`CREATE` and `UPDATE`) and the verdict per caller, plus the most
|
|
1428
1467
|
restrictive verdict per caller over all root fields. A field over
|
|
1429
1468
|
several types (`node`, a union) reports the most restrictive member.
|
|
1430
|
-
|
|
1469
|
+
A mutation's inputs are read for what they write through relationship
|
|
1470
|
+
fields: a nested connect or create counts as that field's CONNECT (and
|
|
1471
|
+
the target's CREATE), a disconnect or nested delete as its DISCONNECT, an
|
|
1472
|
+
`update: { edge }` as UPDATE_EDGE, each named in `by`
|
|
1473
|
+
(`Room.chan CONNECT: @authentication`). Pass `variables` for inputs the
|
|
1474
|
+
document takes as variables; a variable without a value is not read and
|
|
1475
|
+
is listed in the field's `unresolved`. Selections below a root field
|
|
1476
|
+
answer to their own types' READ rules.
|
|
1431
1477
|
To keep admin-only operations out of client bundles:
|
|
1432
1478
|
|
|
1433
1479
|
````ts
|
|
@@ -1441,10 +1487,19 @@ order is stable, so a snapshot in CI turns access changes into diffs.
|
|
|
1441
1487
|
`check` lints authorization too:
|
|
1442
1488
|
|
|
1443
1489
|
- a filter rule every signed-in caller passes;
|
|
1444
|
-
- a rule
|
|
1445
|
-
a
|
|
1490
|
+
- a rule that needs no claims, as a whole or in one `OR` branch, on a
|
|
1491
|
+
type or a field, in a schema that leaves
|
|
1492
|
+
`@authorizationDefaults(requireAuthentication:)` unset: anonymous
|
|
1493
|
+
callers are refused it by default, so say which was meant;
|
|
1494
|
+
- once that setting is `false`, each write rule (CREATE, UPDATE, DELETE,
|
|
1495
|
+
CONNECT, DISCONNECT, UPDATE_EDGE) a signed-out caller can pass and no
|
|
1496
|
+
`@authentication` covers. READ rules and operations the type lists in
|
|
1497
|
+
`public` are left alone: those are what the setting and the list are
|
|
1498
|
+
for;
|
|
1446
1499
|
- field rules the schema's bypass skips, on a type that says neither
|
|
1447
|
-
`@authorization(bypass: false)` nor `bypass: true
|
|
1500
|
+
`@authorization(bypass: false)` nor `bypass: true`, and one line naming
|
|
1501
|
+
every type with rules the bypass reaches that way (plus relationship
|
|
1502
|
+
property types, whose rules cannot opt out);
|
|
1448
1503
|
- an UPDATE rule testing a single relationship the update input can
|
|
1449
1504
|
re-point (`connect`, `disconnect`, `create`): the rule sees the node
|
|
1450
1505
|
before (and, validated `AFTER`, after) the write, so a caller passing it
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
import { GraphModel, ModelWarning } from '../model/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Authorization lints for `check`: valid rules that are probably not what
|
|
5
|
+
* was meant.
|
|
6
|
+
*/
|
|
7
|
+
export declare function accessLints(model: GraphModel, schema: GraphQLSchema): ModelWarning[];
|
package/dist/analyze/access.d.ts
CHANGED
|
@@ -1,29 +1,6 @@
|
|
|
1
1
|
import { FragmentDefinitionNode, GraphQLSchema, OperationDefinitionNode } from 'graphql';
|
|
2
|
-
import { AuthOperation, GraphModel
|
|
3
|
-
|
|
4
|
-
/** Every row, for this caller. */
|
|
5
|
-
"allowed"
|
|
6
|
-
/** Only rows a filter rule admits; others are invisible. */
|
|
7
|
-
| "filtered"
|
|
8
|
-
/** Checked per row; a failing row is FORBIDDEN. */
|
|
9
|
-
| "validated"
|
|
10
|
-
/** Rows failing a mask read a substitute value. */
|
|
11
|
-
| "masked"
|
|
12
|
-
/** Never, for this caller. */
|
|
13
|
-
| "denied"
|
|
14
|
-
/** Needs a token this caller does not have. */
|
|
15
|
-
| "unauthenticated";
|
|
16
|
-
export interface AccessEntry {
|
|
17
|
-
type: string;
|
|
18
|
-
/** The field, for field-level and relationship rules. */
|
|
19
|
-
field?: string;
|
|
20
|
-
/** `EXECUTE`: running a Mutation @cypher field. */
|
|
21
|
-
operation: AuthOperation | "EXECUTE";
|
|
22
|
-
principal: string;
|
|
23
|
-
verdict: AccessVerdict;
|
|
24
|
-
/** What decides it: `filter[0]`, `validate[1]`, `@authentication`, `bypass`, … */
|
|
25
|
-
by: string[];
|
|
26
|
-
}
|
|
2
|
+
import { AuthOperation, GraphModel } from '../model/types.js';
|
|
3
|
+
import { AccessEntry, AccessVerdict } from './verdicts.js';
|
|
27
4
|
/** One root field of an operation, as each kind of caller. */
|
|
28
5
|
export interface RootFieldAccess {
|
|
29
6
|
/** The root field. */
|
|
@@ -46,6 +23,11 @@ export interface RootFieldAccess {
|
|
|
46
23
|
verdict: AccessVerdict;
|
|
47
24
|
by: string[];
|
|
48
25
|
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Variables in the field's arguments that were given no value: what
|
|
28
|
+
* they would write through relationship fields is not in `access`.
|
|
29
|
+
*/
|
|
30
|
+
unresolved?: string[];
|
|
49
31
|
}
|
|
50
32
|
export interface OperationAccess {
|
|
51
33
|
operation: "query" | "mutation" | "subscription";
|
|
@@ -62,14 +44,12 @@ export interface OperationAccess {
|
|
|
62
44
|
}
|
|
63
45
|
/**
|
|
64
46
|
* Who may run `operation`: each root field's verdict per principal, read
|
|
65
|
-
* off the same rules as `accessMatrix`.
|
|
66
|
-
*
|
|
47
|
+
* off the same rules as `accessMatrix`. A mutation's inputs are read for
|
|
48
|
+
* the relationship writes they make, each a part of its root field's
|
|
49
|
+
* verdict; an input given as a variable is read when `variables` has its
|
|
50
|
+
* value and listed in `unresolved` when it does not. Selections below a
|
|
51
|
+
* root field answer to their own types' READ rules (see the matrix).
|
|
67
52
|
*/
|
|
68
|
-
export declare function operationAccess(model: GraphModel, schema: GraphQLSchema, operation: OperationDefinitionNode, fragments: ReadonlyMap<string, FragmentDefinitionNode
|
|
53
|
+
export declare function operationAccess(model: GraphModel, schema: GraphQLSchema, operation: OperationDefinitionNode, fragments: ReadonlyMap<string, FragmentDefinitionNode>, variables?: Readonly<Record<string, unknown>>): OperationAccess;
|
|
69
54
|
/** The access matrix of `model`, in a stable order. */
|
|
70
55
|
export declare function accessMatrix(model: GraphModel, schema: GraphQLSchema): AccessEntry[];
|
|
71
|
-
/**
|
|
72
|
-
* Authorization lints for `check`: valid rules that are probably not what
|
|
73
|
-
* was meant.
|
|
74
|
-
*/
|
|
75
|
-
export declare function accessLints(model: GraphModel, schema: GraphQLSchema): ModelWarning[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LoraDriver } from '../driver.js';
|
|
2
|
+
import { AssertSchemaOptions } from '../options.js';
|
|
3
|
+
import { CheckReport, SchemaAssertion } from '../results.js';
|
|
4
|
+
import { SchemaRequirement } from './indexes.js';
|
|
5
|
+
export interface SchemaEnv {
|
|
6
|
+
driver: LoraDriver;
|
|
7
|
+
/** Timeout for every statement, in milliseconds. */
|
|
8
|
+
timeoutMs: number;
|
|
9
|
+
/** Constraints and indexes the API needs. */
|
|
10
|
+
requirements(): SchemaRequirement[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Verify the database has every constraint and index the API needs;
|
|
14
|
+
* with `create`, add what is missing. Idempotent. Runs one DDL
|
|
15
|
+
* statement at a time: LoraDB rejects schema commands in transactions.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertSchema(env: SchemaEnv, options: AssertSchemaOptions): Promise<SchemaAssertion>;
|
|
18
|
+
/** Indexes in the database that no requirement of the API matches. */
|
|
19
|
+
export declare function unusedIndexes(env: SchemaEnv): Promise<CheckReport["unused"]>;
|
package/dist/analyze/diff.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DocumentNode } from 'graphql';
|
|
2
|
-
import { ModelOptions } from '../model/build.js';
|
|
2
|
+
import { ModelOptions } from '../model/build/limits.js';
|
|
3
3
|
import { SchemaRequirement } from './indexes.js';
|
|
4
4
|
export interface ApiChange {
|
|
5
5
|
/** graphql-js change type, e.g. `FIELD_REMOVED`. */
|
package/dist/analyze/plans.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LoraDriver, QueryPlan, Statement } from '../driver.js';
|
|
2
|
-
import { CompiledRead } from '../compile/read.js';
|
|
2
|
+
import { CompiledRead } from '../compile/read/types.js';
|
|
3
3
|
export interface PlanFinding {
|
|
4
4
|
rule: "full-scan" | "mutating-read" | "result-columns" | "scan-expand" | "row-budget";
|
|
5
5
|
message: string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Field, GraphModel, NodeType, RelationshipPropertiesType } from '../model/types.js';
|
|
2
|
+
export interface Principal {
|
|
3
|
+
name: string;
|
|
4
|
+
jwt: Record<string, unknown> | undefined;
|
|
5
|
+
/** The model this principal's verdicts are read off, when not the schema's. */
|
|
6
|
+
model?: GraphModel;
|
|
7
|
+
}
|
|
8
|
+
/** `node` (or its field, or a property type's field) as `p` sees the model. */
|
|
9
|
+
export declare const nodeAs: (p: Principal, node: NodeType) => NodeType;
|
|
10
|
+
export declare const fieldAs: <F extends Field>(p: Principal, node: NodeType, f: F) => F;
|
|
11
|
+
export declare const propertyAs: <F extends {
|
|
12
|
+
name: string;
|
|
13
|
+
}>(p: Principal, props: RelationshipPropertiesType, f: F) => F;
|
|
14
|
+
/**
|
|
15
|
+
* Anonymous, authenticated (a token with no roles), `viewer` when rules
|
|
16
|
+
* name the caller's node, and one principal per claim value the rules
|
|
17
|
+
* test with `includes`, `eq` or `in` (`roles:admin`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function principalsOf(model: GraphModel): Principal[];
|
|
20
|
+
/**
|
|
21
|
+
* A token carrying `claims` where the `@jwt` type says they live
|
|
22
|
+
* (`@jwtClaim(path: "app_metadata.roles")`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function token(model: GraphModel, claims: Record<string, unknown>): Record<string, unknown>;
|
|
25
|
+
export declare const isRecord: (x: unknown) => x is Record<string, unknown>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fieldReadGuard, propertyAccess, relationshipRules } from '../compile/auth.js';
|
|
2
|
+
import { CompileContext } from '../compile/context.js';
|
|
3
|
+
import { AuthOperation, CypherField, Field, NodeType, RelationshipOperation } from '../model/types.js';
|
|
4
|
+
export type AccessVerdict =
|
|
5
|
+
/** Every row, for this caller. */
|
|
6
|
+
"allowed"
|
|
7
|
+
/** Only rows a filter rule admits; others are invisible. */
|
|
8
|
+
| "filtered"
|
|
9
|
+
/** Checked per row; a failing row is FORBIDDEN. */
|
|
10
|
+
| "validated"
|
|
11
|
+
/** Rows failing a mask read a substitute value. */
|
|
12
|
+
| "masked"
|
|
13
|
+
/** Never, for this caller. */
|
|
14
|
+
| "denied"
|
|
15
|
+
/** Needs a token this caller does not have. */
|
|
16
|
+
| "unauthenticated";
|
|
17
|
+
export interface AccessEntry {
|
|
18
|
+
type: string;
|
|
19
|
+
/** The field, for field-level and relationship rules. */
|
|
20
|
+
field?: string;
|
|
21
|
+
/** `EXECUTE`: running a Mutation @cypher field. */
|
|
22
|
+
operation: AuthOperation | "EXECUTE";
|
|
23
|
+
principal: string;
|
|
24
|
+
verdict: AccessVerdict;
|
|
25
|
+
/** What decides it: `filter[0]`, `validate[1]`, `@authentication`, `bypass`, … */
|
|
26
|
+
by: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare function rootFieldVerdict(ctx: CompileContext, field: CypherField): Pick<AccessEntry, "verdict" | "by">;
|
|
29
|
+
export declare const PROPERTY_OPS: readonly ["READ", "CREATE", "UPDATE"];
|
|
30
|
+
/** `field` has rules (or `@authentication`) for `op`. */
|
|
31
|
+
export declare function fieldWriteGuarded(field: {
|
|
32
|
+
authentication?: ReadonlySet<AuthOperation> | undefined;
|
|
33
|
+
authorization?: NodeType["authorization"];
|
|
34
|
+
}, op: AuthOperation): boolean;
|
|
35
|
+
export declare function fieldWriteVerdict(ctx: CompileContext, node: NodeType, field: Field & {
|
|
36
|
+
kind: "scalar";
|
|
37
|
+
}, op: "CREATE" | "UPDATE"): Pick<AccessEntry, "verdict" | "by">;
|
|
38
|
+
export declare function propertyVerdict(ctx: CompileContext, field: Parameters<typeof propertyAccess>[1], op: AuthOperation): Pick<AccessEntry, "verdict" | "by">;
|
|
39
|
+
export declare function typeVerdict(ctx: CompileContext, node: NodeType, op: AuthOperation): Pick<AccessEntry, "verdict" | "by">;
|
|
40
|
+
export declare function fieldVerdict(ctx: CompileContext, node: NodeType, field: Parameters<typeof fieldReadGuard>[2] & {
|
|
41
|
+
name: string;
|
|
42
|
+
kind: string;
|
|
43
|
+
authorization?: NodeType["authorization"];
|
|
44
|
+
}): Pick<AccessEntry, "verdict" | "by">;
|
|
45
|
+
/**
|
|
46
|
+
* The `@authentication` operation a relationship operation answers to: a
|
|
47
|
+
* connect is checked as CREATE_RELATIONSHIP on the field, a disconnect as
|
|
48
|
+
* DELETE_RELATIONSHIP. Edge reads and updates have none of their own.
|
|
49
|
+
*/
|
|
50
|
+
export declare function relationshipAuthentication(op: RelationshipOperation): AuthOperation | undefined;
|
|
51
|
+
export declare function relationshipVerdict(ctx: CompileContext, node: NodeType, field: Parameters<typeof relationshipRules>[1], op: RelationshipOperation): Pick<AccessEntry, "verdict" | "by">;
|
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@ import { execFileSync as re } from "node:child_process";
|
|
|
3
3
|
import { stat as M, readFile as ce, readdir as ue, mkdir as le, writeFile as _ } from "node:fs/promises";
|
|
4
4
|
import { dirname as fe, basename as de, join as O, relative as pe } from "node:path";
|
|
5
5
|
import { parse as j, Kind as p, visit as Y, print as Z, typeFromAST as me, isNonNullType as G, isListType as he, getNamedType as ge, isEnumType as ve, isInputObjectType as we } from "graphql";
|
|
6
|
-
import { d as Ee } from "./diff-
|
|
7
|
-
import { q as K, e as be, M as Te, a as R, l as X, g as x, u as ee, c as Ne, i as ye, r as Se, w as Ie } from "./driver-
|
|
6
|
+
import { d as Ee } from "./diff-K8-GSeKH.js";
|
|
7
|
+
import { q as K, e as be, M as Te, a as R, l as X, g as x, u as ee, c as Ne, i as ye, r as Se, w as Ie } from "./driver-CXKPLfkz.js";
|
|
8
8
|
const $e = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]), W = {
|
|
9
9
|
coalesce: "@coalesce is not supported: it hides predicates from indexes; store a value instead",
|
|
10
10
|
subscriptionsAuthorization: "@subscriptionsAuthorization is not supported: SUBSCRIBE rules go in @authorization",
|