@sasjs/api 0.6.0 → 0.7.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.
Files changed (40) hide show
  1. package/README.md +3 -2
  2. package/build/browser.js +2231 -118
  3. package/build/cjs/corpus/records.js +1222 -0
  4. package/build/cjs/corpus/runner.js +15 -6
  5. package/build/cjs/drive/deploy.js +49 -10
  6. package/build/cjs/host/dispatch.js +190 -2
  7. package/build/cjs/identity/memory-store.js +196 -11
  8. package/build/cjs/identity/records.js +398 -0
  9. package/build/cjs/index.js +8 -1
  10. package/build/cjs/routes/generated.js +184 -4
  11. package/build/cjs/routes/overlay.js +53 -58
  12. package/build/esm/corpus/records.d.ts +25 -0
  13. package/build/esm/corpus/records.d.ts.map +1 -0
  14. package/build/esm/corpus/records.js +1219 -0
  15. package/build/esm/corpus/runner.d.ts.map +1 -1
  16. package/build/esm/corpus/runner.js +15 -6
  17. package/build/esm/drive/deploy.d.ts +10 -0
  18. package/build/esm/drive/deploy.d.ts.map +1 -1
  19. package/build/esm/drive/deploy.js +48 -9
  20. package/build/esm/host/dispatch.d.ts +9 -0
  21. package/build/esm/host/dispatch.d.ts.map +1 -1
  22. package/build/esm/host/dispatch.js +190 -2
  23. package/build/esm/identity/fixture.d.ts +6 -0
  24. package/build/esm/identity/fixture.d.ts.map +1 -1
  25. package/build/esm/identity/memory-store.d.ts +29 -5
  26. package/build/esm/identity/memory-store.d.ts.map +1 -1
  27. package/build/esm/identity/memory-store.js +196 -11
  28. package/build/esm/identity/records.d.ts +102 -0
  29. package/build/esm/identity/records.d.ts.map +1 -0
  30. package/build/esm/identity/records.js +393 -0
  31. package/build/esm/identity/types.d.ts +102 -2
  32. package/build/esm/identity/types.d.ts.map +1 -1
  33. package/build/esm/index.d.ts +4 -1
  34. package/build/esm/index.d.ts.map +1 -1
  35. package/build/esm/index.js +2 -0
  36. package/build/esm/routes/generated.d.ts.map +1 -1
  37. package/build/esm/routes/generated.js +184 -4
  38. package/build/esm/routes/overlay.d.ts.map +1 -1
  39. package/build/esm/routes/overlay.js +53 -58
  40. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/corpus/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAQnD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC9B,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,UAAU,KAAG,QASjD,CAAA;AAkBF;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAClB,UAAU,UAAU,EACpB,WAAU,QAAgC,KACzC,OAAO,CAAC,UAAU,CAepB,CAAA"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/corpus/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AASnD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC9B,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,UAAU,KAAG,QAkBlD,CAAA;AAkBD;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAClB,UAAU,UAAU,EACpB,WAAU,QAAgC,KACzC,OAAO,CAAC,UAAU,CAepB,CAAA"}
@@ -2,6 +2,7 @@ import { dispatch } from '../host/dispatch.js';
2
2
  import { createDriveService } from '../drive/service.js';
3
3
  import { MemoryDriveStore } from '../drive/memory-store.js';
4
4
  import { createIdentityService } from '../identity/service.js';
5
+ import { createRecordService } from '../identity/records.js';
5
6
  import { MemoryIdentityStore } from '../identity/memory-store.js';
6
7
  import { DEFAULT_IDENTITY } from '../identity/fixture.js';
7
8
  import { isApiError } from '../types.js';
@@ -11,12 +12,20 @@ import { isApiError } from '../types.js';
11
12
  * The identity store is built for every case, drive or not, so a case that starts
12
13
  * mid-flow can declare the tokens and codes it begins with.
13
14
  */
14
- export const servicesFor = (testCase) => ({
15
- drive: createDriveService(testCase.unwritable
16
- ? new RefusingDriveStore(testCase.seed || {})
17
- : new MemoryDriveStore(testCase.seed || {})),
18
- identity: createIdentityService(new MemoryIdentityStore(testCase.identity || DEFAULT_IDENTITY))
19
- });
15
+ export const servicesFor = (testCase) => {
16
+ // one identity store, shared by both services: a token the sign-in flow issues
17
+ // has to be the token a record route reads, and a user a record route creates
18
+ // has to be one the flow can sign in. Two stores built from the same fixture
19
+ // would let a case assert across two independent states and still pass.
20
+ const identity = new MemoryIdentityStore(testCase.identity || DEFAULT_IDENTITY);
21
+ return {
22
+ drive: createDriveService(testCase.unwritable
23
+ ? new RefusingDriveStore(testCase.seed || {})
24
+ : new MemoryDriveStore(testCase.seed || {})),
25
+ identity: createIdentityService(identity),
26
+ records: createRecordService(identity)
27
+ };
28
+ };
20
29
  /**
21
30
  * A drive that reads but refuses every write.
22
31
  *
@@ -99,6 +99,16 @@ export interface DeployMeasurements {
99
99
  bytes: number;
100
100
  };
101
101
  }
102
+ /**
103
+ * How deep a tree may nest.
104
+ *
105
+ * A deployment is an app: `services/`, `web/`, a folder per service. A few dozen
106
+ * levels is generous, and the number matters because the walk recurses once per
107
+ * level - without a ceiling, a tree nested deeply enough exhausts the stack and
108
+ * throws a `RangeError`, which is neither the `400` nor the `413` this route
109
+ * documents, on input the cap exists to bound.
110
+ */
111
+ export declare const MAX_DEPTH = 32;
102
112
  /** The bytes a base64 body decodes to, for a store that writes bytes. */
103
113
  export declare const decodeBase64: (value: string) => Uint8Array;
104
114
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/drive/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;AAE5D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,YAAY,GACtB,kBAAkB,GAAG,mBAAmB,GAAG,gBAAgB,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,eAAO,MAAM,aAAa,EAAE,YAI3B,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,QAAoB,CAAA;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;CAGjB,CAAA;AAEV,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,GAAG,GAAG,GAAG,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACzC;AAiBD,yEAAyE;AACzE,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,UAmB5C,CAAA;AA8FD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,YAAY,EAAE,KACtB,kBAUF,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,YAAY,EAAE,EACvB,SAAQ,YAA4B,KACnC,aAAa,GAAG,IA6BlB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAMtD,CAAA;AAkBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,UAAU,EACjB,QAAQ,OAAO,EACf,MAAM,OAAO,EACb,SAAQ,YAA4B,KACnC,OAAO,CAAC,IAAI,CAiCd,CAAA"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/drive/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;AAE5D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,YAAY,GACtB,kBAAkB,GAAG,mBAAmB,GAAG,gBAAgB,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,eAAO,MAAM,aAAa,EAAE,YAI3B,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,QAAoB,CAAA;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;CAGjB,CAAA;AAEV,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,GAAG,GAAG,GAAG,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACzC;AASD;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,KAAK,CAAA;AAY3B,yEAAyE;AACzE,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,UAmB5C,CAAA;AA8HD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,YAAY,EAAE,KACtB,kBAUF,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,YAAY,EAAE,EACvB,SAAQ,YAA4B,KACnC,aAAa,GAAG,IAoClB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAMtD,CAAA;AAkBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,UAAU,EACjB,QAAQ,OAAO,EACf,MAAM,OAAO,EACb,SAAQ,YAA4B,KACnC,OAAO,CAAC,IAAI,CAiCd,CAAA"}
@@ -33,6 +33,16 @@ export const DEPLOY_SUCCESS = {
33
33
  status: 'success',
34
34
  message: 'Files deployed successfully to @sasjs/server.'
35
35
  };
36
+ /**
37
+ * How deep a tree may nest.
38
+ *
39
+ * A deployment is an app: `services/`, `web/`, a folder per service. A few dozen
40
+ * levels is generous, and the number matters because the walk recurses once per
41
+ * level - without a ceiling, a tree nested deeply enough exhausts the stack and
42
+ * throws a `RangeError`, which is neither the `400` nor the `413` this route
43
+ * documents, on input the cap exists to bound.
44
+ */
45
+ export const MAX_DEPTH = 32;
36
46
  const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
37
47
  /** A base64 body without its padding, which is what the arithmetic needs. */
38
48
  const base64Body = (value) => value.replace(/[^A-Za-z0-9+/]/g, '');
@@ -78,14 +88,28 @@ const utf8Length = (value) => {
78
88
  const memberName = (member) => member.type === 'service' ? `${member.name}.sas` : member.name;
79
89
  /** The bytes a member writes. */
80
90
  const memberBytes = (member) => member.type === 'file' ? base64Length(member.code) : utf8Length(member.code);
91
+ const MEGABYTE = 1024 * 1024;
81
92
  /**
82
- * Sizes as the messages state them: one decimal in binary megabytes, and bytes
83
- * below a megabyte - a limit small enough to be measured in bytes should not
84
- * read as "0 MB", which is what a stricter host would otherwise report.
93
+ * A MEASURED size, rounded UP to one decimal in binary megabytes.
94
+ *
95
+ * Up, not to nearest: a tree one byte over a 100 MB cap must not read as "100 MB
96
+ * (maximum 100 MB)", which is a refusal that states no overage at all. Rounding
97
+ * up is the direction that never understates what is being refused.
98
+ *
99
+ * Bytes below a megabyte are printed as bytes, because a limit small enough to
100
+ * be measured in bytes should not read as "0 MB" - which is what a host passing
101
+ * a tighter set would otherwise report.
85
102
  */
86
- const describeSize = (bytes) => bytes < 1024 * 1024
103
+ const describeSize = (bytes) => bytes < MEGABYTE
87
104
  ? `${bytes} bytes`
88
- : `${Math.round((bytes / (1024 * 1024)) * 10) / 10} MB`;
105
+ : `${Math.ceil((bytes / MEGABYTE) * 10) / 10} MB`;
106
+ /**
107
+ * A LIMIT, printed as it is stated: `DEPLOY_LIMITS` holds exact megabytes, so
108
+ * rounding cannot make it disagree with itself.
109
+ */
110
+ const describeLimit = (bytes) => bytes < MEGABYTE
111
+ ? `${bytes} bytes`
112
+ : `${Math.round((bytes / MEGABYTE) * 10) / 10} MB`;
89
113
  /** A walk's starting point. */
90
114
  const emptyWalk = () => ({
91
115
  entries: 0,
@@ -109,8 +133,12 @@ const isSafeSegment = (name) => name !== '' &&
109
133
  * a caller that only wants the numbers gets the size of the whole tree, and the
110
134
  * validator decides whether the name matters. Returning early would report a
111
135
  * truncated tree as if it were the real one.
136
+ *
137
+ * The one thing that stops the walk is depth: past `MAX_DEPTH` it does not
138
+ * descend, because descending is the work that would exhaust the stack. The
139
+ * level it reached is recorded so the refusal can state it.
112
140
  */
113
- const walk = (members, parentPath, acc) => {
141
+ const walk = (members, parentPath, acc, depth = 1) => {
114
142
  for (const member of members) {
115
143
  const name = memberName(member);
116
144
  const memberPath = parentPath ? `${parentPath}/${name}` : name;
@@ -122,7 +150,12 @@ const walk = (members, parentPath, acc) => {
122
150
  };
123
151
  acc.entries++;
124
152
  if (member.type === 'folder') {
125
- walk(member.members, memberPath, acc);
153
+ if (depth + 1 > MAX_DEPTH) {
154
+ if (!acc.tooDeep)
155
+ acc.tooDeep = depth + 1;
156
+ continue;
157
+ }
158
+ walk(member.members, memberPath, acc, depth + 1);
126
159
  continue;
127
160
  }
128
161
  const bytes = memberBytes(member);
@@ -161,6 +194,12 @@ export const checkDeployTree = (members, limits = DEPLOY_LIMITS) => {
161
194
  walk(members, '', measured);
162
195
  if (measured.nameRefusal)
163
196
  return measured.nameRefusal;
197
+ if (measured.tooDeep)
198
+ return {
199
+ code: 413,
200
+ status: 'failure',
201
+ message: `The file tree is nested too deeply: ${measured.tooDeep} levels (maximum ${MAX_DEPTH}).`
202
+ };
164
203
  if (measured.entries > limits.entries)
165
204
  return {
166
205
  code: 413,
@@ -171,13 +210,13 @@ export const checkDeployTree = (members, limits = DEPLOY_LIMITS) => {
171
210
  return {
172
211
  code: 413,
173
212
  status: 'failure',
174
- message: `The file tree exceeds the deploy limit: ${describeSize(measured.totalBytes)} (maximum ${describeSize(limits.totalBytes)}).`
213
+ message: `The file tree exceeds the deploy limit: ${describeSize(measured.totalBytes)} (maximum ${describeLimit(limits.totalBytes)}).`
175
214
  };
176
215
  if (measured.largest.bytes > limits.fileBytes)
177
216
  return {
178
217
  code: 413,
179
218
  status: 'failure',
180
- message: `A file in the tree exceeds the deploy limit: ${measured.largest.path} is ${describeSize(measured.largest.bytes)} (maximum ${describeSize(limits.fileBytes)} per file).`
219
+ message: `A file in the tree exceeds the deploy limit: ${measured.largest.path} is ${describeSize(measured.largest.bytes)} (maximum ${describeLimit(limits.fileBytes)} per file).`
181
220
  };
182
221
  return null;
183
222
  };
@@ -1,9 +1,16 @@
1
1
  import type { DriveService } from '../drive/service.js';
2
2
  import type { IdentityService } from '../identity/service.js';
3
+ import type { RecordService } from '../identity/records.js';
3
4
  import type { Subject } from '../identity/types.js';
4
5
  export interface ApiRequest {
5
6
  query?: Record<string, string | undefined>;
6
7
  body?: Record<string, unknown>;
8
+ /**
9
+ * The values the route's path parameters carried, by name - `{ uid: 'abc' }`
10
+ * for `/user/{uid}`. A host extracts them from its own router; the corpus
11
+ * states them.
12
+ */
13
+ params?: Record<string, string>;
7
14
  /** The multipart payload: the API names it `file` on every route that takes one. */
8
15
  file?: {
9
16
  name: string;
@@ -33,6 +40,8 @@ export interface Services {
33
40
  * implement - the difference is the message, and it names the capability.
34
41
  */
35
42
  identity?: IdentityService;
43
+ /** Users, groups, permission rules and clients. Absent, the record routes answer 501. */
44
+ records?: RecordService;
36
45
  }
37
46
  export declare const dispatch: (services: Services, routeId: string, request?: ApiRequest) => Promise<ApiResponse>;
38
47
  /** Whether a route is one `dispatch` can answer today. */
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAInD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,CAsIrB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAkBlC,CAAA"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAOnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,yFAAyF;IACzF,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,CA+VrB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAwClC,CAAA"}
@@ -1,6 +1,8 @@
1
1
  import { routeById } from '../routes/index.js';
2
2
  import { ApiError } from '../types.js';
3
3
  import { DEPLOY_SUCCESS } from '../drive/deploy.js';
4
+ /** The defaults sasjs/server's Client schema applies to an omitted lifetime. */
5
+ const SECONDS_IN_A_DAY = 86400;
4
6
  /**
5
7
  * The parameters the table marks required, checked before the handler runs.
6
8
  *
@@ -39,10 +41,36 @@ export const dispatch = async (services, routeId, request = {}) => {
39
41
  const route = routeById(routeId);
40
42
  if (!route)
41
43
  throw new ApiError(404, `No such operation: ${routeId}`);
44
+ const { drive, identity, records } = services;
45
+ // The server authenticates before its payload schema runs, so an anonymous
46
+ // request with an incomplete body is a 401 there rather than a 400. The order
47
+ // is only observable on the route groups that gate on a caller: the sign-in
48
+ // flow has no caller by definition, and the drive has no identity at all.
49
+ const identityScoped = ['User', 'Group', 'Permission', 'Client'].includes(route.tag) ||
50
+ routeId === 'PATCH /SASjsApi/auth/updatePassword';
51
+ if (identityScoped && records) {
52
+ const subject = request.subject ??
53
+ (identity ? await identity.authenticate(request.token) : undefined);
54
+ if (!subject) {
55
+ // A host may serve records without a sign-in flow - it resolves the caller
56
+ // itself and passes `subject`. Without one it cannot authenticate at all,
57
+ // and a 401 would send every request round the login loop for ever, so it
58
+ // is told which capability it is missing instead.
59
+ if (!identity)
60
+ throw new ApiError(501, `This host has no identity service, so it cannot answer ${routeId}.`);
61
+ throw new ApiError(401, 'Unauthorized');
62
+ }
63
+ }
42
64
  validate(route, request);
43
- const { drive, identity } = services;
44
65
  const query = request.query || {};
45
66
  const body = request.body || {};
67
+ const params = request.params || {};
68
+ /** The record service, or the 501 a host without one answers. */
69
+ const required = () => {
70
+ if (!records)
71
+ throw new ApiError(501, `This host has no record service, so it cannot answer ${routeId}.`);
72
+ return records;
73
+ };
46
74
  /** The caller, from the request's own subject or the token it carried. */
47
75
  const caller = async () => {
48
76
  if (request.subject)
@@ -135,6 +163,144 @@ export const dispatch = async (services, routeId, request = {}) => {
135
163
  }
136
164
  case 'GET /SASjsApi/drive/filetree':
137
165
  return { status: 200, body: await drive.tree(query._folderPath) };
166
+ // ------------------------------------------------------------- records
167
+ //
168
+ // The administration routes: users, groups, permission rules, clients and the
169
+ // caller's own password. Each handler resolves the caller and passes it on;
170
+ // the admin gate and the wording live in the service.
171
+ case 'GET /SASjsApi/user':
172
+ return { status: 200, body: await required().listUsers(await caller()) };
173
+ case 'GET /SASjsApi/user/{uid}':
174
+ return {
175
+ status: 200,
176
+ body: await required().getUser(await caller(), {
177
+ uid: String(params.uid ?? '')
178
+ })
179
+ };
180
+ case 'GET /SASjsApi/user/by/username/{username}':
181
+ return {
182
+ status: 200,
183
+ body: await required().getUser(await caller(), {
184
+ username: String(params.username ?? '')
185
+ })
186
+ };
187
+ case 'POST /SASjsApi/user':
188
+ return {
189
+ status: 200,
190
+ body: await required().createUser(await caller(), {
191
+ username: String(body.username ?? ''),
192
+ displayName: String(body.displayName ?? ''),
193
+ password: String(body.password ?? ''),
194
+ isAdmin: body.isAdmin,
195
+ isActive: body.isActive,
196
+ autoExec: body.autoExec
197
+ })
198
+ };
199
+ case 'PATCH /SASjsApi/user/{uid}':
200
+ return {
201
+ status: 200,
202
+ body: await required().updateUser(await caller(), { uid: String(params.uid ?? '') }, body)
203
+ };
204
+ case 'PATCH /SASjsApi/user/by/username/{username}':
205
+ return {
206
+ status: 200,
207
+ body: await required().updateUser(await caller(), { username: String(params.username ?? '') }, body)
208
+ };
209
+ case 'DELETE /SASjsApi/user/{uid}':
210
+ await required().deleteUser(await caller(), { uid: String(params.uid ?? '') }, { password: body.password });
211
+ return { status: 204 };
212
+ case 'DELETE /SASjsApi/user/by/username/{username}':
213
+ await required().deleteUser(await caller(), { username: String(params.username ?? '') }, { password: body.password });
214
+ return { status: 204 };
215
+ case 'GET /SASjsApi/group':
216
+ return { status: 200, body: await required().listGroups(await caller()) };
217
+ case 'GET /SASjsApi/group/{uid}':
218
+ return {
219
+ status: 200,
220
+ body: await required().getGroup(await caller(), {
221
+ uid: String(params.uid ?? '')
222
+ })
223
+ };
224
+ case 'GET /SASjsApi/group/by/groupname/{name}':
225
+ return {
226
+ status: 200,
227
+ body: await required().getGroup(await caller(), {
228
+ name: String(params.name ?? '')
229
+ })
230
+ };
231
+ case 'POST /SASjsApi/group':
232
+ return {
233
+ status: 200,
234
+ body: await required().createGroup(await caller(), {
235
+ name: String(body.name ?? ''),
236
+ description: String(body.description ?? ''),
237
+ isActive: body.isActive
238
+ })
239
+ };
240
+ case 'POST /SASjsApi/group/{groupUid}/{userUid}':
241
+ return {
242
+ status: 200,
243
+ body: await required().addUserToGroup(await caller(), String(params.groupUid ?? ''), String(params.userUid ?? ''))
244
+ };
245
+ case 'DELETE /SASjsApi/group/{groupUid}/{userUid}':
246
+ return {
247
+ status: 200,
248
+ body: await required().removeUserFromGroup(await caller(), String(params.groupUid ?? ''), String(params.userUid ?? ''))
249
+ };
250
+ case 'DELETE /SASjsApi/group/{uid}':
251
+ // the document states 200 for this one - the server answers with the
252
+ // driver's delete result rather than nothing
253
+ await required().deleteGroup(await caller(), String(params.uid ?? ''));
254
+ return { status: 200 };
255
+ case 'GET /SASjsApi/permission':
256
+ return {
257
+ status: 200,
258
+ body: await required().listPermissions(await caller())
259
+ };
260
+ case 'POST /SASjsApi/permission':
261
+ return {
262
+ status: 200,
263
+ body: await required().createPermission(await caller(), {
264
+ path: String(body.path ?? ''),
265
+ type: String(body.type ?? ''),
266
+ setting: String(body.setting ?? ''),
267
+ principalType: String(body.principalType ?? ''),
268
+ principalId: String(body.principalId ?? '')
269
+ })
270
+ };
271
+ case 'PATCH /SASjsApi/permission/{uid}':
272
+ return {
273
+ status: 200,
274
+ body: await required().updatePermission(await caller(), String(params.uid ?? ''), { setting: String(body.setting ?? '') })
275
+ };
276
+ case 'DELETE /SASjsApi/permission/{uid}':
277
+ await required().deletePermission(await caller(), String(params.uid ?? ''));
278
+ return { status: 204 };
279
+ case 'GET /SASjsApi/client':
280
+ return { status: 200, body: await required().listClients(await caller()) };
281
+ case 'POST /SASjsApi/client':
282
+ return {
283
+ status: 200,
284
+ // An omitted lifetime is the schema's default, not NaN: the server's
285
+ // Client model declares 86400 and 2592000, and a NaN lifetime would make
286
+ // every token the client buys expire on arrival.
287
+ body: await required().createClient(await caller(), {
288
+ clientId: String(body.clientId ?? ''),
289
+ clientSecret: String(body.clientSecret ?? ''),
290
+ accessTokenExpiration: body.accessTokenExpiration === undefined
291
+ ? SECONDS_IN_A_DAY
292
+ : Number(body.accessTokenExpiration),
293
+ refreshTokenExpiration: body.refreshTokenExpiration === undefined
294
+ ? SECONDS_IN_A_DAY * 30
295
+ : Number(body.refreshTokenExpiration)
296
+ })
297
+ };
298
+ case 'PATCH /SASjsApi/auth/updatePassword':
299
+ await required().updatePassword(await caller(), {
300
+ currentPassword: String(body.currentPassword ?? ''),
301
+ newPassword: String(body.newPassword ?? '')
302
+ });
303
+ return { status: 204 };
138
304
  default: {
139
305
  // The reason the table carries travels with the refusal: a host developer
140
306
  // reading a 501 wants to know what the operation waits on, not only that
@@ -162,5 +328,27 @@ export const HANDLED_ROUTES = [
162
328
  'POST /SASLogon/login',
163
329
  'POST /SASLogon/authorize',
164
330
  'GET /SASLogon/logout',
165
- 'GET /SASjsApi/session'
331
+ 'GET /SASjsApi/session',
332
+ 'GET /SASjsApi/user',
333
+ 'GET /SASjsApi/user/{uid}',
334
+ 'GET /SASjsApi/user/by/username/{username}',
335
+ 'POST /SASjsApi/user',
336
+ 'PATCH /SASjsApi/user/{uid}',
337
+ 'PATCH /SASjsApi/user/by/username/{username}',
338
+ 'DELETE /SASjsApi/user/{uid}',
339
+ 'DELETE /SASjsApi/user/by/username/{username}',
340
+ 'GET /SASjsApi/group',
341
+ 'GET /SASjsApi/group/{uid}',
342
+ 'GET /SASjsApi/group/by/groupname/{name}',
343
+ 'POST /SASjsApi/group',
344
+ 'POST /SASjsApi/group/{groupUid}/{userUid}',
345
+ 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
346
+ 'DELETE /SASjsApi/group/{uid}',
347
+ 'GET /SASjsApi/permission',
348
+ 'POST /SASjsApi/permission',
349
+ 'PATCH /SASjsApi/permission/{uid}',
350
+ 'DELETE /SASjsApi/permission/{uid}',
351
+ 'GET /SASjsApi/client',
352
+ 'POST /SASjsApi/client',
353
+ 'PATCH /SASjsApi/auth/updatePassword'
166
354
  ];
@@ -19,6 +19,10 @@ export interface IdentityFixtureUser {
19
19
  isAdmin: boolean;
20
20
  isActive: boolean;
21
21
  needsToUpdatePassword: boolean;
22
+ /** Set when the account is managed by an external auth provider. */
23
+ authProvider?: string;
24
+ /** The account's own auto-exec code, where the fixture declares one. */
25
+ autoExec?: string;
22
26
  }
23
27
  export interface IdentityFixtureGroup {
24
28
  uid: string;
@@ -27,6 +31,8 @@ export interface IdentityFixtureGroup {
27
31
  isActive: boolean;
28
32
  /** Usernames of the members. */
29
33
  users: string[];
34
+ /** Set when the group is managed by an external auth provider. */
35
+ authProvider?: string;
30
36
  }
31
37
  export interface IdentityFixturePermission {
32
38
  uid: string;
@@ -1 +1 @@
1
- {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../../src/identity/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,gCAAgC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,qCAAqC;IACrC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,qCAAqC;IACrC,sBAAsB,EAAE,MAAM,CAAA;CAC/B;AAED,wFAAwF;AACxF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,mBAAmB,EAAE,CAAA;IAC5B,MAAM,EAAE,oBAAoB,EAAE,CAAA;IAC9B,WAAW,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,sDAAsD;IACtD,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAA;IAC/B,mEAAmE;IACnE,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAC9B;AAID,eAAO,MAAM,gBAAgB,EAAE,eA6E9B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,KAAG,eA2B5C,CAAA"}
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../../src/identity/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,qBAAqB,EAAE,OAAO,CAAA;IAC9B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,gCAAgC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,qCAAqC;IACrC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,qCAAqC;IACrC,sBAAsB,EAAE,MAAM,CAAA;CAC/B;AAED,wFAAwF;AACxF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,mBAAmB,EAAE,CAAA;IAC5B,MAAM,EAAE,oBAAoB,EAAE,CAAA;IAC9B,WAAW,EAAE,yBAAyB,EAAE,CAAA;IACxC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,sDAAsD;IACtD,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAA;IAC/B,mEAAmE;IACnE,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAC9B;AAID,eAAO,MAAM,gBAAgB,EAAE,eA6E9B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,KAAG,eA2B5C,CAAA"}
@@ -8,7 +8,7 @@
8
8
  * resolving. A host with real credentials - a server - supplies its own store
9
9
  * with JWTs, bcrypt and a database behind it.
10
10
  */
11
- import type { IdentityClient, IdentityStore, IdentityUser, Subject, TokenPair } from './types.js';
11
+ import type { IdentityClient, IdentityClientRecord, IdentityGroupRecord, IdentityPermissionRecord, IdentityStore, IdentityUser, IdentityUserRecord, NewGroup, NewPermission, NewUser, Subject, TokenPair, UserChange } from './types.js';
12
12
  import { type IdentityFixture } from './fixture.js';
13
13
  /**
14
14
  * Whether a token minted for one subject names the same user and client.
@@ -17,17 +17,20 @@ import { type IdentityFixture } from './fixture.js';
17
17
  * the read paths compare rather than trust the reference.
18
18
  */
19
19
  export declare class MemoryIdentityStore implements IdentityStore {
20
- private readonly users;
20
+ private readonly userList;
21
21
  private readonly passwords;
22
- private readonly clients;
22
+ private readonly groupList;
23
+ private readonly permissionList;
24
+ private readonly clientRecords;
25
+ private readonly clientMap;
23
26
  private readonly pairs;
24
27
  private readonly access;
25
28
  private readonly refresh;
26
29
  private readonly codes;
27
30
  private readonly claimed;
28
31
  constructor(fixture?: IdentityFixture);
29
- userByName(username: string): Promise<IdentityUser | undefined>;
30
- userById(uid: string): Promise<IdentityUser | undefined>;
32
+ userByName(username: string): Promise<IdentityUserRecord | undefined>;
33
+ userById(uid: string): Promise<IdentityUserRecord | undefined>;
31
34
  /** A fixture holds the password in clear, so the check is an equality. */
32
35
  passwordMatches(user: IdentityUser, password: string): Promise<boolean>;
33
36
  client(clientId: string): Promise<IdentityClient | undefined>;
@@ -57,5 +60,26 @@ export declare class MemoryIdentityStore implements IdentityStore {
57
60
  issueCode(subject: Subject): Promise<string>;
58
61
  readCode(code: string): Promise<Subject | undefined>;
59
62
  claimCode(code: string): Promise<boolean>;
63
+ users(): Promise<IdentityUserRecord[]>;
64
+ /** A user as the administration routes report one: groups derived, not stored. */
65
+ private recordOf;
66
+ createUser(input: NewUser): Promise<IdentityUserRecord>;
67
+ updateUser(uid: string, change: UserChange): Promise<IdentityUserRecord | undefined>;
68
+ deleteUser(uid: string): Promise<void>;
69
+ setPassword(uid: string, password: string): Promise<void>;
70
+ groups(): Promise<IdentityGroupRecord[]>;
71
+ groupById(uid: string): Promise<IdentityGroupRecord | undefined>;
72
+ groupByName(name: string): Promise<IdentityGroupRecord | undefined>;
73
+ createGroup(input: NewGroup): Promise<IdentityGroupRecord>;
74
+ deleteGroup(uid: string): Promise<void>;
75
+ addToGroup(groupUid: string, userUid: string): Promise<IdentityGroupRecord | undefined>;
76
+ removeFromGroup(groupUid: string, userUid: string): Promise<IdentityGroupRecord | undefined>;
77
+ permissions(): Promise<IdentityPermissionRecord[]>;
78
+ permissionById(uid: string): Promise<IdentityPermissionRecord | undefined>;
79
+ createPermission(input: NewPermission): Promise<IdentityPermissionRecord>;
80
+ setPermissionSetting(uid: string, setting: string): Promise<IdentityPermissionRecord | undefined>;
81
+ deletePermission(uid: string): Promise<void>;
82
+ clients(): Promise<IdentityClientRecord[]>;
83
+ createClient(input: IdentityClientRecord): Promise<IdentityClientRecord>;
60
84
  }
61
85
  //# sourceMappingURL=memory-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/identity/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAyDrB;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,GAAE,eAAkC;IAsDjD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI9D,0EAA0E;IACpE,eAAe,CACnB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IAIb,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI7D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBzE;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIvD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAI9D,OAAO,CAAC,OAAO;IAef;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAWzD,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAS5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAKhD"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/identity/memory-store.ts"],"names":[],"mappings":"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"}