@synnaxlabs/client 0.30.0 → 0.31.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 (115) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/api/client.api.md +615 -261
  3. package/dist/access/client.d.ts +2 -7
  4. package/dist/access/client.d.ts.map +1 -1
  5. package/dist/access/payload.d.ts +7 -102
  6. package/dist/access/payload.d.ts.map +1 -1
  7. package/dist/access/policy/client.d.ts +17 -0
  8. package/dist/access/policy/client.d.ts.map +1 -0
  9. package/dist/access/policy/external.d.ts +3 -0
  10. package/dist/access/policy/external.d.ts.map +1 -0
  11. package/dist/access/policy/index.d.ts +2 -0
  12. package/dist/access/policy/index.d.ts.map +1 -0
  13. package/dist/access/policy/payload.d.ts +163 -0
  14. package/dist/access/policy/payload.d.ts.map +1 -0
  15. package/dist/access/policy/policy.spec.d.ts +2 -0
  16. package/dist/access/policy/policy.spec.d.ts.map +1 -0
  17. package/dist/access/policy/retriever.d.ts +36 -0
  18. package/dist/access/policy/retriever.d.ts.map +1 -0
  19. package/dist/access/policy/writer.d.ts +9 -0
  20. package/dist/access/policy/writer.d.ts.map +1 -0
  21. package/dist/auth/auth.d.ts +6 -30
  22. package/dist/auth/auth.d.ts.map +1 -1
  23. package/dist/channel/payload.d.ts +17 -17
  24. package/dist/channel/payload.d.ts.map +1 -1
  25. package/dist/channel/retriever.d.ts +8 -8
  26. package/dist/client.cjs +31 -21
  27. package/dist/client.js +2962 -2233
  28. package/dist/framer/client.d.ts +4 -1
  29. package/dist/framer/client.d.ts.map +1 -1
  30. package/dist/framer/frame.d.ts +27 -80
  31. package/dist/framer/frame.d.ts.map +1 -1
  32. package/dist/framer/streamer.d.ts +3 -1
  33. package/dist/framer/streamer.d.ts.map +1 -1
  34. package/dist/framer/writer.d.ts +24 -16
  35. package/dist/framer/writer.d.ts.map +1 -1
  36. package/dist/hardware/device/client.d.ts +2 -2
  37. package/dist/hardware/device/payload.d.ts +1 -1
  38. package/dist/hardware/device/payload.d.ts.map +1 -1
  39. package/dist/hardware/rack/payload.d.ts +1 -1
  40. package/dist/hardware/rack/payload.d.ts.map +1 -1
  41. package/dist/hardware/task/client.d.ts +2 -2
  42. package/dist/hardware/task/ni/types.d.ts +16 -16
  43. package/dist/hardware/task/payload.d.ts +13 -13
  44. package/dist/hardware/task/payload.d.ts.map +1 -1
  45. package/dist/index.d.ts +6 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/label/payload.d.ts +1 -1
  48. package/dist/label/payload.d.ts.map +1 -1
  49. package/dist/label/writer.d.ts +5 -5
  50. package/dist/ontology/client.d.ts +32 -30
  51. package/dist/ontology/client.d.ts.map +1 -1
  52. package/dist/ontology/payload.d.ts +62 -63
  53. package/dist/ontology/payload.d.ts.map +1 -1
  54. package/dist/ranger/payload.d.ts +2 -2
  55. package/dist/ranger/payload.d.ts.map +1 -1
  56. package/dist/ranger/writer.d.ts +5 -5
  57. package/dist/user/client.d.ts +13 -3
  58. package/dist/user/client.d.ts.map +1 -1
  59. package/dist/user/payload.d.ts +34 -3
  60. package/dist/user/payload.d.ts.map +1 -1
  61. package/dist/user/retriever.d.ts +21 -0
  62. package/dist/user/retriever.d.ts.map +1 -0
  63. package/dist/user/user.spec.d.ts +2 -0
  64. package/dist/user/user.spec.d.ts.map +1 -0
  65. package/dist/user/writer.d.ts +11 -0
  66. package/dist/user/writer.d.ts.map +1 -0
  67. package/dist/workspace/lineplot/payload.d.ts +1 -1
  68. package/dist/workspace/lineplot/payload.d.ts.map +1 -1
  69. package/dist/workspace/payload.d.ts +1 -1
  70. package/dist/workspace/payload.d.ts.map +1 -1
  71. package/dist/workspace/schematic/client.d.ts.map +1 -1
  72. package/dist/workspace/schematic/payload.d.ts +1 -1
  73. package/dist/workspace/schematic/payload.d.ts.map +1 -1
  74. package/examples/node/package-lock.json +963 -134
  75. package/examples/node/package.json +1 -1
  76. package/package.json +3 -3
  77. package/src/access/client.ts +4 -70
  78. package/src/access/payload.ts +14 -24
  79. package/src/access/policy/client.ts +65 -0
  80. package/src/access/policy/external.ts +11 -0
  81. package/src/access/policy/index.ts +10 -0
  82. package/src/access/policy/payload.ts +45 -0
  83. package/src/access/policy/policy.spec.ts +331 -0
  84. package/src/access/policy/retriever.ts +43 -0
  85. package/src/access/policy/writer.ts +65 -0
  86. package/src/auth/auth.ts +32 -10
  87. package/src/channel/payload.ts +2 -2
  88. package/src/framer/client.ts +7 -1
  89. package/src/framer/frame.spec.ts +21 -12
  90. package/src/framer/frame.ts +9 -24
  91. package/src/framer/streamer.spec.ts +48 -0
  92. package/src/framer/streamer.ts +7 -4
  93. package/src/framer/writer.ts +0 -2
  94. package/src/hardware/device/payload.ts +2 -2
  95. package/src/hardware/rack/payload.ts +2 -2
  96. package/src/hardware/task/payload.ts +2 -2
  97. package/src/index.ts +16 -13
  98. package/src/label/payload.ts +2 -2
  99. package/src/ontology/client.ts +35 -34
  100. package/src/ontology/payload.ts +28 -35
  101. package/src/ranger/payload.ts +5 -7
  102. package/src/setupspecs.ts +2 -2
  103. package/src/user/client.ts +63 -19
  104. package/src/user/payload.ts +14 -7
  105. package/src/user/retriever.ts +41 -0
  106. package/src/user/user.spec.ts +289 -0
  107. package/src/user/writer.ts +91 -0
  108. package/src/workspace/lineplot/payload.ts +2 -2
  109. package/src/workspace/payload.ts +2 -2
  110. package/src/workspace/schematic/client.ts +1 -1
  111. package/src/workspace/schematic/payload.ts +2 -2
  112. package/src/workspace/workspace.spec.ts +1 -1
  113. package/dist/access/access.spec.d.ts +0 -2
  114. package/dist/access/access.spec.d.ts.map +0 -1
  115. package/src/access/access.spec.ts +0 -276
@@ -11,6 +11,6 @@
11
11
  "author": "",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
- "@synnaxlabs/client": "^0.26.1"
14
+ "@synnaxlabs/client": "^0.31.0"
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synnaxlabs/client",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "The Synnax Client Library",
5
5
  "keywords": [
6
6
  "synnax",
@@ -26,8 +26,8 @@
26
26
  "dependencies": {
27
27
  "async-mutex": "^0.5.0",
28
28
  "zod": "3.23.8",
29
- "@synnaxlabs/freighter": "0.30.0",
30
- "@synnaxlabs/x": "0.30.0"
29
+ "@synnaxlabs/x": "0.31.0",
30
+ "@synnaxlabs/freighter": "0.31.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.5.4",
@@ -7,80 +7,14 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
- import { toArray } from "@synnaxlabs/x/toArray";
12
- import { z } from "zod";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
13
11
 
14
- import {
15
- Key,
16
- keyZ,
17
- NewPolicyPayload,
18
- newPolicyPayloadZ,
19
- Policy,
20
- policyZ,
21
- } from "@/access/payload";
22
- import { IDPayload, idZ } from "@/ontology/payload";
23
-
24
- const CREATE_ENDPOINT = "/access/policy/create";
25
- const DELETE_ENDPOINT = "/access/policy/delete";
26
- const RETRIEVE_ENDPOINT = "/access/policy/retrieve";
27
-
28
- const createReqZ = z.object({ policies: newPolicyPayloadZ.array() });
29
- const createResZ = z.object({ policies: policyZ.array() });
30
-
31
- const deleteReqZ = z.object({ keys: keyZ.array() });
32
- const deleteResZ = z.object({});
33
-
34
- const retrieveReqZ = z.object({ subject: idZ });
35
- const retrieveResZ = z.object({
36
- policies: policyZ.array().optional().default([]),
37
- });
12
+ import { policy } from "@/access/policy";
38
13
 
39
14
  export class Client {
40
- private readonly client: UnaryClient;
15
+ readonly policy: policy.Client;
41
16
 
42
17
  constructor(client: UnaryClient) {
43
- this.client = client;
44
- }
45
-
46
- async create(policies: NewPolicyPayload): Promise<Policy>;
47
-
48
- async create(policies: NewPolicyPayload[]): Promise<Policy[]>;
49
-
50
- async create(
51
- policies: NewPolicyPayload | NewPolicyPayload[],
52
- ): Promise<Policy | Policy[]> {
53
- const single = !Array.isArray(policies);
54
-
55
- const { policies: created } = await sendRequired<
56
- typeof createReqZ,
57
- typeof createResZ
58
- >(
59
- this.client,
60
- CREATE_ENDPOINT,
61
- { policies: toArray(policies) },
62
- createReqZ,
63
- createResZ,
64
- );
65
-
66
- return single ? created[0] : created;
67
- }
68
-
69
- async retrieve(subject: IDPayload): Promise<Policy[]> {
70
- const { policies: retrieved } = await sendRequired<
71
- typeof retrieveReqZ,
72
- typeof retrieveResZ
73
- >(this.client, RETRIEVE_ENDPOINT, { subject: subject }, retrieveReqZ, retrieveResZ);
74
- return retrieved;
75
- }
76
-
77
- async delete(keys: Key | Key[]): Promise<void> {
78
- await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
79
- this.client,
80
- DELETE_ENDPOINT,
81
- { keys: toArray(keys) },
82
- deleteReqZ,
83
- deleteResZ,
84
- );
18
+ this.policy = new policy.Client(client);
85
19
  }
86
20
  }
@@ -9,27 +9,17 @@
9
9
 
10
10
  import { z } from "zod";
11
11
 
12
- import { ontology } from "@/ontology";
13
- import { idZ } from "@/ontology/payload";
14
-
15
- export const keyZ = z.string().uuid();
16
-
17
- export type Key = z.infer<typeof keyZ>;
18
-
19
- export type Params = Key | Key[];
20
-
21
- export const policyZ = z.object({
22
- key: keyZ,
23
- subjects: idZ.array(),
24
- objects: idZ.array(),
25
- actions: z.string().array(),
26
- });
27
- export type Policy = z.infer<typeof policyZ>;
28
-
29
- export const newPolicyPayloadZ = policyZ.extend({ key: keyZ.optional() });
30
- export type NewPolicyPayload = z.infer<typeof newPolicyPayloadZ>;
31
-
32
- export const PolicyOntologyType = "policy" as ontology.ResourceType;
33
-
34
- export const ontologyID = (key: Key): ontology.ID =>
35
- new ontology.ID({ type: PolicyOntologyType, key });
12
+ export const actionZ = z.union([
13
+ z.literal("all"),
14
+ z.literal("create"),
15
+ z.literal("delete"),
16
+ z.literal("retrieve"),
17
+ z.literal("update"),
18
+ ]);
19
+ export type Action = z.infer<typeof actionZ>;
20
+
21
+ export const ALL_ACTION: Action = "all";
22
+ export const CREATE_ACTION: Action = "create";
23
+ export const DELETE_ACTION: Action = "delete";
24
+ export const RETRIEVE_ACTION: Action = "retrieve";
25
+ export const UPDATE_ACTION: Action = "update";
@@ -0,0 +1,65 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { toArray } from "@synnaxlabs/x/toArray";
12
+
13
+ import { type Key, type NewPolicy, type Policy } from "@/access/policy/payload";
14
+ import { Retriever } from "@/access/policy/retriever";
15
+ import { Writer } from "@/access/policy/writer";
16
+ import { ontology } from "@/ontology";
17
+
18
+ export class Client {
19
+ private readonly retriever: Retriever;
20
+ private readonly writer: Writer;
21
+
22
+ constructor(client: UnaryClient) {
23
+ this.retriever = new Retriever(client);
24
+ this.writer = new Writer(client);
25
+ }
26
+
27
+ async create(policy: NewPolicy): Promise<Policy>;
28
+
29
+ async create(policies: NewPolicy[]): Promise<Policy[]>;
30
+
31
+ async create(policies: NewPolicy | NewPolicy[]): Promise<Policy | Policy[]> {
32
+ const isMany = Array.isArray(policies);
33
+ const createdPolicies = await this.writer.create(policies);
34
+ return isMany ? createdPolicies : createdPolicies[0];
35
+ }
36
+
37
+ async retrieve(key: Key): Promise<Policy>;
38
+
39
+ async retrieve(keys: Key[]): Promise<Policy[]>;
40
+
41
+ async retrieve(keys: Key | Key[]): Promise<Policy | Policy[]> {
42
+ const isMany = Array.isArray(keys);
43
+ const res = await this.retriever.retrieve({ keys: toArray(keys) });
44
+ return isMany ? res : res[0];
45
+ }
46
+
47
+ async retrieveFor(subject: ontology.CrudeID): Promise<Policy[]>;
48
+
49
+ async retrieveFor(subjects: ontology.CrudeID[]): Promise<Policy[]>;
50
+
51
+ async retrieveFor(
52
+ subjects: ontology.CrudeID | ontology.CrudeID[],
53
+ ): Promise<Policy[]> {
54
+ const newIds = toArray(subjects).map((id) => new ontology.ID(id).payload);
55
+ return await this.retriever.retrieve({ subjects: newIds });
56
+ }
57
+
58
+ async delete(key: Key): Promise<void>;
59
+
60
+ async delete(keys: Key[]): Promise<void>;
61
+
62
+ async delete(keys: Key | Key[]): Promise<void> {
63
+ await this.writer.delete(keys);
64
+ }
65
+ }
@@ -0,0 +1,11 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ export * from "@/access/policy/client";
11
+ export * from "@/access/policy/payload";
@@ -0,0 +1,10 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ export * as policy from "@/access/policy/external";
@@ -0,0 +1,45 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { z } from "zod";
11
+
12
+ import { actionZ } from "@/access/payload";
13
+ import { ontology } from "@/ontology";
14
+ import { nullableArrayZ } from "@/util/zod";
15
+
16
+ export const keyZ = z.string().uuid();
17
+ export type Key = z.infer<typeof keyZ>;
18
+
19
+ export const newPolicyZ = z.object({
20
+ key: keyZ.optional(),
21
+ subjects: ontology.crudeIDZ.array().or(ontology.crudeIDZ),
22
+ objects: ontology.crudeIDZ.array().or(ontology.crudeIDZ),
23
+ actions: actionZ.array().or(actionZ),
24
+ });
25
+ export type NewPolicy = z.input<typeof newPolicyZ>;
26
+
27
+ export const policyZ = z.object({
28
+ key: keyZ,
29
+ subjects: nullableArrayZ(ontology.idZ),
30
+ objects: nullableArrayZ(ontology.idZ),
31
+ actions: nullableArrayZ(actionZ),
32
+ });
33
+ export type Policy = z.infer<typeof policyZ>;
34
+
35
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "policy";
36
+
37
+ export const ontologyID = (key: Key): ontology.ID =>
38
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
39
+
40
+ export const ALLOW_ALL_ONTOLOGY_TYPE: ontology.ResourceType = "allow_all";
41
+
42
+ export const ALLOW_ALL_ONTOLOGY_ID = new ontology.ID({
43
+ type: ALLOW_ALL_ONTOLOGY_TYPE,
44
+ key: "",
45
+ });
@@ -0,0 +1,331 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { id } from "@synnaxlabs/x";
11
+ import { describe, expect, test } from "vitest";
12
+
13
+ import { policy } from "@/access/policy";
14
+ import { channel } from "@/channel";
15
+ import Synnax from "@/client";
16
+ import { AuthError } from "@/errors";
17
+ import { label } from "@/label";
18
+ import { HOST, newClient, PORT } from "@/setupspecs";
19
+ import { user } from "@/user";
20
+ import { schematic } from "@/workspace/schematic";
21
+
22
+ const client = newClient();
23
+
24
+ describe("Policy", () => {
25
+ describe("create", () => {
26
+ describe("one", () => {
27
+ test("without key", async () => {
28
+ const policy = await client.access.policy.create({
29
+ subjects: user.ONTOLOGY_TYPE,
30
+ objects: [user.ONTOLOGY_TYPE, channel.ONTOLOGY_TYPE],
31
+ actions: "delete",
32
+ });
33
+ expect(policy.key).exist;
34
+ expect(policy.subjects.length).toEqual(1);
35
+ expect(policy.subjects[0].key).toEqual("");
36
+ expect(policy.subjects[0].type).toEqual(user.ONTOLOGY_TYPE);
37
+ expect(policy.objects.length).toEqual(2);
38
+ expect(policy.objects[0].key).toEqual("");
39
+ expect(policy.objects[1].key).toEqual("");
40
+ expect(policy.objects[0].type).toEqual(user.ONTOLOGY_TYPE);
41
+ expect(policy.objects[1].type).toEqual(channel.ONTOLOGY_TYPE);
42
+ expect(policy.actions).toEqual(["delete"]);
43
+ await client.access.policy.delete(policy.key);
44
+ });
45
+ test("missing subjects", async () => {
46
+ const policy = await client.access.policy.create({
47
+ subjects: [],
48
+ objects: [],
49
+ actions: [],
50
+ });
51
+ expect(policy.key).exist;
52
+ expect(policy.subjects).toHaveLength(0);
53
+ expect(policy.objects).toHaveLength(0);
54
+ expect(policy.actions).toHaveLength(0);
55
+ const retrievedPolicy = await client.access.policy.retrieve(policy.key);
56
+ expect(retrievedPolicy).toMatchObject(policy);
57
+ });
58
+ test("missing objects", async () => {
59
+ const policy = await client.access.policy.create({
60
+ subjects: user.ontologyID("1"),
61
+ objects: [],
62
+ actions: [],
63
+ });
64
+ expect(policy.key).exist;
65
+ expect(policy.subjects.length).toEqual(1);
66
+ expect(policy.subjects[0].key).toEqual("1");
67
+ expect(policy.subjects[0].type).toEqual(user.ONTOLOGY_TYPE);
68
+ expect(policy.objects).toHaveLength(0);
69
+ expect(policy.actions).toHaveLength(0);
70
+ const retrievedPolicy = await client.access.policy.retrieve(policy.key);
71
+ expect(retrievedPolicy).toMatchObject(policy);
72
+ });
73
+ test("with key", async () => {
74
+ const policy = await client.access.policy.create({
75
+ subjects: [
76
+ { type: user.ONTOLOGY_TYPE, key: "1" },
77
+ { type: channel.ONTOLOGY_TYPE, key: "2" },
78
+ ],
79
+ objects: { type: channel.ONTOLOGY_TYPE, key: "3" },
80
+ actions: ["delete", "retrieve"],
81
+ });
82
+ expect(policy.key).exist;
83
+ expect(policy.subjects.length).toEqual(2);
84
+ expect(policy.subjects[0].key).toEqual("1");
85
+ expect(policy.subjects[0].type).toEqual(user.ONTOLOGY_TYPE);
86
+ expect(policy.subjects[1].key).toEqual("2");
87
+ expect(policy.subjects[1].type).toEqual(channel.ONTOLOGY_TYPE);
88
+ expect(policy.objects.length).toEqual(1);
89
+ expect(policy.objects[0].key).toEqual("3");
90
+ expect(policy.objects[0].type).toEqual(channel.ONTOLOGY_TYPE);
91
+ expect(policy.actions).toEqual(["delete", "retrieve"]);
92
+ await client.access.policy.delete(policy.key);
93
+ });
94
+ });
95
+ describe("many", () => {
96
+ test("with keys", async () => {
97
+ const policiesToCreate: policy.NewPolicy[] = [
98
+ {
99
+ subjects: [{ type: user.ONTOLOGY_TYPE, key: "10" }],
100
+ objects: [
101
+ { type: user.ONTOLOGY_TYPE, key: "20" },
102
+ { type: schematic.ONTOLOGY_TYPE, key: "21" },
103
+ ],
104
+ actions: ["retrieve"],
105
+ },
106
+ {
107
+ subjects: [
108
+ { type: user.ONTOLOGY_TYPE, key: "20" },
109
+ { type: schematic.ONTOLOGY_TYPE, key: "21" },
110
+ ],
111
+ objects: [
112
+ { type: user.ONTOLOGY_TYPE, key: "20" },
113
+ { type: schematic.ONTOLOGY_TYPE, key: "30" },
114
+ ],
115
+ actions: ["delete"],
116
+ },
117
+ ];
118
+ const policies = await client.access.policy.create(policiesToCreate);
119
+ expect(policies[0]).toMatchObject(policiesToCreate[0]);
120
+ expect(policies[1]).toMatchObject(policiesToCreate[1]);
121
+ await client.access.policy.delete([policies[0].key, policies[1].key]);
122
+ });
123
+ test("without keys", async () => {
124
+ const policies = await client.access.policy.create([
125
+ {
126
+ subjects: user.ONTOLOGY_TYPE,
127
+ objects: [user.ONTOLOGY_TYPE, schematic.ONTOLOGY_TYPE],
128
+ actions: ["retrieve"],
129
+ },
130
+ {
131
+ subjects: [user.ONTOLOGY_TYPE, schematic.ONTOLOGY_TYPE],
132
+ objects: [channel.ONTOLOGY_TYPE],
133
+ actions: "retrieve",
134
+ },
135
+ ]);
136
+ expect(policies.length).toEqual(2);
137
+ expect(policies[0].key).exist;
138
+ expect(policies[0].subjects.length).toEqual(1);
139
+ expect(policies[0].subjects[0].key).toEqual("");
140
+ expect(policies[0].subjects[0].type).toEqual(user.ONTOLOGY_TYPE);
141
+ expect(policies[0].objects.length).toEqual(2);
142
+ expect(policies[0].objects[0].key).toEqual("");
143
+ expect(policies[0].objects[1].key).toEqual("");
144
+ expect(policies[0].objects[0].type).toEqual(user.ONTOLOGY_TYPE);
145
+ expect(policies[0].objects[1].type).toEqual(schematic.ONTOLOGY_TYPE);
146
+ expect(policies[0].actions).toEqual(["retrieve"]);
147
+ expect(policies[1].key).exist;
148
+ expect(policies[1].subjects.length).toEqual(2);
149
+ expect(policies[1].subjects[0].key).toEqual("");
150
+ expect(policies[1].subjects[1].key).toEqual("");
151
+ expect(policies[1].subjects[0].type).toEqual(user.ONTOLOGY_TYPE);
152
+ expect(policies[1].subjects[1].type).toEqual(schematic.ONTOLOGY_TYPE);
153
+ expect(policies[1].objects.length).toEqual(1);
154
+ expect(policies[1].objects[0].key).toEqual("");
155
+ expect(policies[1].objects[0].type).toEqual(channel.ONTOLOGY_TYPE);
156
+ expect(policies[1].actions).toEqual(["retrieve"]);
157
+ await client.access.policy.delete([policies[0].key, policies[1].key]);
158
+ });
159
+ });
160
+ });
161
+ describe("retrieve", async () => {
162
+ test("by key", async () => {
163
+ const policies = await client.access.policy.create([
164
+ {
165
+ subjects: user.ONTOLOGY_TYPE,
166
+ objects: [user.ONTOLOGY_TYPE, channel.ONTOLOGY_TYPE],
167
+ actions: "delete",
168
+ },
169
+ {
170
+ subjects: user.ONTOLOGY_TYPE,
171
+ objects: [schematic.ONTOLOGY_TYPE, channel.ONTOLOGY_TYPE],
172
+ actions: "retrieve",
173
+ },
174
+ ]);
175
+ const result = await client.access.policy.retrieve(policies[0].key);
176
+ expect(result).toMatchObject(policies[0]);
177
+ const results = await client.access.policy.retrieve([
178
+ policies[0].key,
179
+ policies[1].key,
180
+ ]);
181
+ expect(results).toHaveLength(2);
182
+ expect(results[0]).toMatchObject(policies[0]);
183
+ expect(results[1]).toMatchObject(policies[1]);
184
+ expect(results.sort()).toMatchObject(policies.sort());
185
+ await client.access.policy.delete([policies[0].key, policies[1].key]);
186
+ });
187
+ test("by subject", async () => {
188
+ const key1 = id.id();
189
+ const key2 = id.id();
190
+ const created = await client.access.policy.create([
191
+ {
192
+ subjects: [
193
+ { type: user.ONTOLOGY_TYPE, key: key1 },
194
+ { type: user.ONTOLOGY_TYPE, key: key2 },
195
+ ],
196
+ objects: [
197
+ { type: user.ONTOLOGY_TYPE, key: "234" },
198
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
199
+ ],
200
+ actions: ["retrieve"],
201
+ },
202
+ {
203
+ subjects: { type: user.ONTOLOGY_TYPE, key: key1 },
204
+ objects: [
205
+ { type: label.ONTOLOGY_TYPE, key: "23123" },
206
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
207
+ ],
208
+ actions: "delete",
209
+ },
210
+ ]);
211
+ const received = await client.access.policy.retrieveFor(user.ontologyID(key2));
212
+ const newReceived = received.filter((p) => {
213
+ return created.some((c) => c.key === p.key);
214
+ });
215
+ expect(created[0]).toMatchObject(newReceived[0]);
216
+ await client.access.policy.delete([created[0].key, created[1].key]);
217
+ });
218
+ });
219
+ describe("delete", async () => {
220
+ test("one", async () => {
221
+ const id1 = id.id();
222
+ const id2 = id.id();
223
+ const id3 = id.id();
224
+ const policies: policy.NewPolicy[] = [
225
+ {
226
+ subjects: [
227
+ { type: user.ONTOLOGY_TYPE, key: id1 },
228
+ { type: user.ONTOLOGY_TYPE, key: id2 },
229
+ ],
230
+ objects: [
231
+ { type: user.ONTOLOGY_TYPE, key: "20" },
232
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
233
+ ],
234
+ actions: ["retrieve"],
235
+ },
236
+ {
237
+ subjects: [
238
+ { type: user.ONTOLOGY_TYPE, key: id1 },
239
+ { type: user.ONTOLOGY_TYPE, key: id3 },
240
+ ],
241
+ objects: [
242
+ { type: label.ONTOLOGY_TYPE, key: "20" },
243
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
244
+ ],
245
+ actions: ["delete"],
246
+ },
247
+ ];
248
+
249
+ const created = await client.access.policy.create(policies);
250
+ await client.access.policy.delete(created[0].key);
251
+ await expect(client.access.policy.retrieve(created[0].key)).rejects.toThrow();
252
+ await client.access.policy.delete(created[1].key);
253
+ });
254
+ test("many", async () => {
255
+ const id1 = id.id();
256
+ const id2 = id.id();
257
+ const id3 = id.id();
258
+ const policies: policy.NewPolicy[] = [
259
+ {
260
+ subjects: [
261
+ { type: user.ONTOLOGY_TYPE, key: id1 },
262
+ { type: user.ONTOLOGY_TYPE, key: id2 },
263
+ ],
264
+ objects: [
265
+ { type: user.ONTOLOGY_TYPE, key: "20" },
266
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
267
+ ],
268
+ actions: ["retrieve"],
269
+ },
270
+ {
271
+ subjects: [
272
+ { type: user.ONTOLOGY_TYPE, key: id1 },
273
+ { type: user.ONTOLOGY_TYPE, key: id3 },
274
+ ],
275
+ objects: [
276
+ { type: label.ONTOLOGY_TYPE, key: "20" },
277
+ { type: channel.ONTOLOGY_TYPE, key: "30" },
278
+ ],
279
+ actions: ["delete"],
280
+ },
281
+ ];
282
+
283
+ const created = await client.access.policy.create(policies);
284
+ await client.access.policy.delete([created[0].key, created[1].key]);
285
+ await expect(client.access.policy.retrieve(created[0].key)).rejects.toThrow();
286
+ await expect(client.access.policy.retrieve(created[1].key)).rejects.toThrow();
287
+ });
288
+ });
289
+ });
290
+
291
+ describe("privilege", async () => {
292
+ test("new user", async () => {
293
+ const username = id.id();
294
+ const user2 = await client.user.create({ username, password: "pwd1" });
295
+ expect(user2).toBeDefined();
296
+ const client2 = new Synnax({
297
+ host: HOST,
298
+ port: PORT,
299
+ username: user2.username,
300
+ password: "pwd1",
301
+ });
302
+ await expect(
303
+ client2.user.create({
304
+ username: id.id(),
305
+ password: id.id(),
306
+ }),
307
+ ).rejects.toThrow(AuthError);
308
+
309
+ const policy = await client.access.policy.create({
310
+ subjects: user.ontologyID(user2.key),
311
+ objects: user.ONTOLOGY_TYPE,
312
+ actions: ["create"],
313
+ });
314
+
315
+ const newUsername = id.id();
316
+
317
+ const newUser = await client2.user.create({
318
+ username: newUsername,
319
+ password: id.id(),
320
+ });
321
+
322
+ expect(newUser.username).toEqual(newUsername);
323
+
324
+ // Remove privileges
325
+ await client.access.policy.delete(policy.key);
326
+
327
+ await expect(
328
+ client2.user.create({ username: id.id(), password: id.id() }),
329
+ ).rejects.toThrow(AuthError);
330
+ });
331
+ });
@@ -0,0 +1,43 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { z } from "zod";
12
+
13
+ import { keyZ, type Policy, policyZ } from "@/access/policy/payload";
14
+ import { ontology } from "@/ontology";
15
+ import { nullableArrayZ } from "@/util/zod";
16
+
17
+ const reqZ = z.object({
18
+ keys: keyZ.array().optional(),
19
+ subjects: ontology.idZ.array().optional(),
20
+ });
21
+ type Request = z.infer<typeof reqZ>;
22
+ const resZ = z.object({ policies: nullableArrayZ(policyZ) });
23
+
24
+ const ENDPOINT = "/access/policy/retrieve";
25
+
26
+ export class Retriever {
27
+ private readonly client: UnaryClient;
28
+
29
+ constructor(client: UnaryClient) {
30
+ this.client = client;
31
+ }
32
+
33
+ async retrieve(req: Request): Promise<Policy[]> {
34
+ const res = await sendRequired<typeof reqZ, typeof resZ>(
35
+ this.client,
36
+ ENDPOINT,
37
+ req,
38
+ reqZ,
39
+ resZ,
40
+ );
41
+ return res.policies;
42
+ }
43
+ }