@inkeep/agents-core 0.0.0-dev-20260118170655 → 0.0.0-dev-20260119163620
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/dist/auth/auth-validation-schemas.d.ts +17 -17
- package/dist/auth/auth.d.ts +103 -43
- package/dist/auth/auth.js +46 -0
- package/dist/auth/authz/client.d.ts +81 -0
- package/dist/auth/authz/client.js +189 -0
- package/dist/auth/authz/config.d.ts +76 -0
- package/dist/auth/authz/config.js +76 -0
- package/dist/auth/authz/index.d.ts +5 -0
- package/dist/auth/authz/index.js +6 -0
- package/dist/auth/authz/permissions.d.ts +57 -0
- package/dist/auth/authz/permissions.js +83 -0
- package/dist/auth/authz/sync.d.ts +85 -0
- package/dist/auth/authz/sync.js +237 -0
- package/dist/auth/permissions.d.ts +13 -13
- package/dist/auth/permissions.js +2 -181
- package/dist/data-access/manage/projectLifecycle.d.ts +1 -0
- package/dist/data-access/manage/projectLifecycle.js +3 -2
- package/dist/data-access/manage/projects.d.ts +4 -0
- package/dist/data-access/manage/projects.js +11 -4
- package/dist/data-access/runtime/messages.d.ts +12 -12
- package/dist/data-access/runtime/projects.d.ts +2 -0
- package/dist/data-access/runtime/projects.js +16 -2
- package/dist/data-access/runtime/tasks.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/validation/drizzle-schema-helpers.d.ts +3 -3
- package/dist/validation/schemas.d.ts +151 -151
- package/package.json +6 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { getSpiceDbConfig } from "./config.js";
|
|
2
|
+
import { v1 } from "@authzed/authzed-node";
|
|
3
|
+
|
|
4
|
+
//#region src/auth/authz/client.ts
|
|
5
|
+
/**
|
|
6
|
+
* SpiceDB Client Wrapper
|
|
7
|
+
*
|
|
8
|
+
* Provides a singleton SpiceDB client and helper functions for common operations.
|
|
9
|
+
*/
|
|
10
|
+
let client = null;
|
|
11
|
+
/**
|
|
12
|
+
* Get the SpiceDB client singleton.
|
|
13
|
+
* Creates a new client on first call.
|
|
14
|
+
*/
|
|
15
|
+
function getSpiceClient() {
|
|
16
|
+
if (!client) {
|
|
17
|
+
const config = getSpiceDbConfig();
|
|
18
|
+
client = v1.NewClient(config.token, config.endpoint, config.tlsEnabled ? v1.ClientSecurity.SECURE : v1.ClientSecurity.INSECURE_LOCALHOST_ALLOWED);
|
|
19
|
+
}
|
|
20
|
+
return client;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Reset the client (useful for testing)
|
|
24
|
+
*/
|
|
25
|
+
function resetSpiceClient() {
|
|
26
|
+
client = null;
|
|
27
|
+
}
|
|
28
|
+
const PERMISSIONSHIP_HAS_PERMISSION = 2;
|
|
29
|
+
const RELATIONSHIP_OPERATION_CREATE = 1;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a subject has a permission on a resource.
|
|
32
|
+
* Note: Caller must verify isAuthzEnabled(tenantId) before calling.
|
|
33
|
+
*/
|
|
34
|
+
async function checkPermission(params) {
|
|
35
|
+
return (await getSpiceClient().promises.checkPermission({
|
|
36
|
+
resource: {
|
|
37
|
+
objectType: params.resourceType,
|
|
38
|
+
objectId: params.resourceId
|
|
39
|
+
},
|
|
40
|
+
permission: params.permission,
|
|
41
|
+
subject: {
|
|
42
|
+
object: {
|
|
43
|
+
objectType: params.subjectType,
|
|
44
|
+
objectId: params.subjectId
|
|
45
|
+
},
|
|
46
|
+
optionalRelation: ""
|
|
47
|
+
},
|
|
48
|
+
consistency: { requirement: {
|
|
49
|
+
oneofKind: "minimizeLatency",
|
|
50
|
+
minimizeLatency: true
|
|
51
|
+
} },
|
|
52
|
+
context: void 0,
|
|
53
|
+
withTracing: false
|
|
54
|
+
})).permissionship === PERMISSIONSHIP_HAS_PERMISSION;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check multiple permissions on a resource in a single request.
|
|
58
|
+
* More efficient than multiple checkPermission calls.
|
|
59
|
+
*
|
|
60
|
+
* @returns Record mapping permission names to boolean results
|
|
61
|
+
*/
|
|
62
|
+
async function checkBulkPermissions(params) {
|
|
63
|
+
const spice = getSpiceClient();
|
|
64
|
+
const items = params.permissions.map((permission) => v1.CheckBulkPermissionsRequestItem.create({
|
|
65
|
+
resource: v1.ObjectReference.create({
|
|
66
|
+
objectType: params.resourceType,
|
|
67
|
+
objectId: params.resourceId
|
|
68
|
+
}),
|
|
69
|
+
permission,
|
|
70
|
+
subject: v1.SubjectReference.create({ object: v1.ObjectReference.create({
|
|
71
|
+
objectType: params.subjectType,
|
|
72
|
+
objectId: params.subjectId
|
|
73
|
+
}) })
|
|
74
|
+
}));
|
|
75
|
+
const response = await spice.promises.checkBulkPermissions(v1.CheckBulkPermissionsRequest.create({
|
|
76
|
+
items,
|
|
77
|
+
consistency: { requirement: {
|
|
78
|
+
oneofKind: "minimizeLatency",
|
|
79
|
+
minimizeLatency: true
|
|
80
|
+
} }
|
|
81
|
+
}));
|
|
82
|
+
const result = {};
|
|
83
|
+
for (let i = 0; i < params.permissions.length; i++) {
|
|
84
|
+
const permission = params.permissions[i];
|
|
85
|
+
const pair = response.pairs[i];
|
|
86
|
+
if (pair.response.oneofKind === "item") result[permission] = pair.response.item.permissionship === PERMISSIONSHIP_HAS_PERMISSION;
|
|
87
|
+
else result[permission] = false;
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Find all resources of a type that a subject has a permission on.
|
|
93
|
+
*/
|
|
94
|
+
async function lookupResources(params) {
|
|
95
|
+
return (await getSpiceClient().promises.lookupResources({
|
|
96
|
+
resourceObjectType: params.resourceType,
|
|
97
|
+
permission: params.permission,
|
|
98
|
+
subject: {
|
|
99
|
+
object: {
|
|
100
|
+
objectType: params.subjectType,
|
|
101
|
+
objectId: params.subjectId
|
|
102
|
+
},
|
|
103
|
+
optionalRelation: ""
|
|
104
|
+
},
|
|
105
|
+
consistency: { requirement: {
|
|
106
|
+
oneofKind: "minimizeLatency",
|
|
107
|
+
minimizeLatency: true
|
|
108
|
+
} },
|
|
109
|
+
context: void 0,
|
|
110
|
+
optionalLimit: 0,
|
|
111
|
+
optionalCursor: void 0
|
|
112
|
+
})).map((item) => item.resourceObjectId);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Write a relationship to SpiceDB.
|
|
116
|
+
*/
|
|
117
|
+
async function writeRelationship(params) {
|
|
118
|
+
await getSpiceClient().promises.writeRelationships({
|
|
119
|
+
updates: [{
|
|
120
|
+
operation: RELATIONSHIP_OPERATION_CREATE,
|
|
121
|
+
relationship: {
|
|
122
|
+
resource: {
|
|
123
|
+
objectType: params.resourceType,
|
|
124
|
+
objectId: params.resourceId
|
|
125
|
+
},
|
|
126
|
+
relation: params.relation,
|
|
127
|
+
subject: {
|
|
128
|
+
object: {
|
|
129
|
+
objectType: params.subjectType,
|
|
130
|
+
objectId: params.subjectId
|
|
131
|
+
},
|
|
132
|
+
optionalRelation: ""
|
|
133
|
+
},
|
|
134
|
+
optionalCaveat: void 0
|
|
135
|
+
}
|
|
136
|
+
}],
|
|
137
|
+
optionalPreconditions: [],
|
|
138
|
+
optionalTransactionMetadata: void 0
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Delete a relationship from SpiceDB.
|
|
143
|
+
*/
|
|
144
|
+
async function deleteRelationship(params) {
|
|
145
|
+
await getSpiceClient().promises.deleteRelationships({
|
|
146
|
+
relationshipFilter: {
|
|
147
|
+
resourceType: params.resourceType,
|
|
148
|
+
optionalResourceId: params.resourceId,
|
|
149
|
+
optionalResourceIdPrefix: "",
|
|
150
|
+
optionalRelation: params.relation,
|
|
151
|
+
optionalSubjectFilter: {
|
|
152
|
+
subjectType: params.subjectType,
|
|
153
|
+
optionalSubjectId: params.subjectId,
|
|
154
|
+
optionalRelation: void 0
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
optionalPreconditions: [],
|
|
158
|
+
optionalLimit: 0,
|
|
159
|
+
optionalAllowPartialDeletions: false,
|
|
160
|
+
optionalTransactionMetadata: void 0
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Read relationships for a resource to list subjects with access.
|
|
165
|
+
*/
|
|
166
|
+
async function readRelationships(params) {
|
|
167
|
+
return (await getSpiceClient().promises.readRelationships({
|
|
168
|
+
relationshipFilter: {
|
|
169
|
+
resourceType: params.resourceType,
|
|
170
|
+
optionalResourceId: params.resourceId,
|
|
171
|
+
optionalResourceIdPrefix: "",
|
|
172
|
+
optionalRelation: params.relation || "",
|
|
173
|
+
optionalSubjectFilter: void 0
|
|
174
|
+
},
|
|
175
|
+
consistency: { requirement: {
|
|
176
|
+
oneofKind: "minimizeLatency",
|
|
177
|
+
minimizeLatency: true
|
|
178
|
+
} },
|
|
179
|
+
optionalLimit: 0,
|
|
180
|
+
optionalCursor: void 0
|
|
181
|
+
})).map((item) => ({
|
|
182
|
+
subjectType: item.relationship?.subject?.object?.objectType || "",
|
|
183
|
+
subjectId: item.relationship?.subject?.object?.objectId || "",
|
|
184
|
+
relation: item.relationship?.relation || ""
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
export { checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, lookupResources, readRelationships, resetSpiceClient, v1, writeRelationship };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/auth/authz/config.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* SpiceDB Authorization Configuration
|
|
4
|
+
*
|
|
5
|
+
* Feature flag and configuration for the SpiceDB authorization system.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if authorization is enabled.
|
|
9
|
+
*
|
|
10
|
+
* When called without tenantId:
|
|
11
|
+
* - Returns true if ENABLE_AUTHZ=true
|
|
12
|
+
*
|
|
13
|
+
* When called with tenantId:
|
|
14
|
+
* - If ENABLE_AUTHZ=false → returns false
|
|
15
|
+
* - If ENABLE_AUTHZ=true and TENANT_ID is not set → returns true (all tenants)
|
|
16
|
+
* - If ENABLE_AUTHZ=true and TENANT_ID is set → returns true only if tenantId matches
|
|
17
|
+
*/
|
|
18
|
+
declare function isAuthzEnabled(tenantId: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get SpiceDB connection configuration from environment variables.
|
|
21
|
+
*/
|
|
22
|
+
declare function getSpiceDbConfig(): {
|
|
23
|
+
endpoint: string;
|
|
24
|
+
token: string;
|
|
25
|
+
tlsEnabled: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* SpiceDB resource types used in the schema
|
|
29
|
+
*/
|
|
30
|
+
declare const SpiceDbResourceTypes: {
|
|
31
|
+
readonly USER: "user";
|
|
32
|
+
readonly ORGANIZATION: "organization";
|
|
33
|
+
readonly PROJECT: "project";
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* SpiceDB relations used in the schema
|
|
37
|
+
*
|
|
38
|
+
* Relations are named as nouns (roles) per SpiceDB best practices.
|
|
39
|
+
* Project roles are prefixed for clarity when debugging/grepping.
|
|
40
|
+
*/
|
|
41
|
+
declare const SpiceDbRelations: {
|
|
42
|
+
readonly OWNER: "owner";
|
|
43
|
+
readonly ADMIN: "admin";
|
|
44
|
+
readonly MEMBER: "member";
|
|
45
|
+
readonly ORGANIZATION: "organization";
|
|
46
|
+
readonly PROJECT_ADMIN: "project_admin";
|
|
47
|
+
readonly PROJECT_MEMBER: "project_member";
|
|
48
|
+
readonly PROJECT_VIEWER: "project_viewer";
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* SpiceDB permissions used in the schema
|
|
52
|
+
*
|
|
53
|
+
* Permissions are named as verbs (actions) per SpiceDB best practices.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* SpiceDB permissions used in permission checks.
|
|
57
|
+
*
|
|
58
|
+
* Note: Organization-level permissions (manage) are handled via
|
|
59
|
+
* orgRole bypass in permission functions, not direct SpiceDB checks.
|
|
60
|
+
*/
|
|
61
|
+
declare const SpiceDbPermissions: {
|
|
62
|
+
readonly VIEW: "view";
|
|
63
|
+
readonly USE: "use";
|
|
64
|
+
readonly EDIT: "edit";
|
|
65
|
+
readonly DELETE: "delete";
|
|
66
|
+
};
|
|
67
|
+
type OrgRole = 'owner' | 'admin' | 'member';
|
|
68
|
+
/**
|
|
69
|
+
* Project roles hierarchy:
|
|
70
|
+
* - project_admin: Full access (view + use + edit + manage members + delete)
|
|
71
|
+
* - project_member: Operator access (view + use: invoke agents, create API keys)
|
|
72
|
+
* - project_viewer: Read-only access (view only)
|
|
73
|
+
*/
|
|
74
|
+
type ProjectRole = 'project_admin' | 'project_member' | 'project_viewer';
|
|
75
|
+
//#endregion
|
|
76
|
+
export { OrgRole, ProjectRole, SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, getSpiceDbConfig, isAuthzEnabled };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/auth/authz/config.ts
|
|
2
|
+
/**
|
|
3
|
+
* SpiceDB Authorization Configuration
|
|
4
|
+
*
|
|
5
|
+
* Feature flag and configuration for the SpiceDB authorization system.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Check if authorization is enabled.
|
|
9
|
+
*
|
|
10
|
+
* When called without tenantId:
|
|
11
|
+
* - Returns true if ENABLE_AUTHZ=true
|
|
12
|
+
*
|
|
13
|
+
* When called with tenantId:
|
|
14
|
+
* - If ENABLE_AUTHZ=false → returns false
|
|
15
|
+
* - If ENABLE_AUTHZ=true and TENANT_ID is not set → returns true (all tenants)
|
|
16
|
+
* - If ENABLE_AUTHZ=true and TENANT_ID is set → returns true only if tenantId matches
|
|
17
|
+
*/
|
|
18
|
+
function isAuthzEnabled(tenantId) {
|
|
19
|
+
if (process.env.ENABLE_AUTHZ !== "true") return false;
|
|
20
|
+
const configuredTenantId = process.env.TENANT_ID?.trim();
|
|
21
|
+
if (!configuredTenantId) return true;
|
|
22
|
+
return tenantId === configuredTenantId;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get SpiceDB connection configuration from environment variables.
|
|
26
|
+
*/
|
|
27
|
+
function getSpiceDbConfig() {
|
|
28
|
+
return {
|
|
29
|
+
endpoint: process.env.SPICEDB_ENDPOINT || "localhost:50051",
|
|
30
|
+
token: process.env.SPICEDB_PRESHARED_KEY || "",
|
|
31
|
+
tlsEnabled: process.env.SPICEDB_TLS_ENABLED === "true"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* SpiceDB resource types used in the schema
|
|
36
|
+
*/
|
|
37
|
+
const SpiceDbResourceTypes = {
|
|
38
|
+
USER: "user",
|
|
39
|
+
ORGANIZATION: "organization",
|
|
40
|
+
PROJECT: "project"
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* SpiceDB relations used in the schema
|
|
44
|
+
*
|
|
45
|
+
* Relations are named as nouns (roles) per SpiceDB best practices.
|
|
46
|
+
* Project roles are prefixed for clarity when debugging/grepping.
|
|
47
|
+
*/
|
|
48
|
+
const SpiceDbRelations = {
|
|
49
|
+
OWNER: "owner",
|
|
50
|
+
ADMIN: "admin",
|
|
51
|
+
MEMBER: "member",
|
|
52
|
+
ORGANIZATION: "organization",
|
|
53
|
+
PROJECT_ADMIN: "project_admin",
|
|
54
|
+
PROJECT_MEMBER: "project_member",
|
|
55
|
+
PROJECT_VIEWER: "project_viewer"
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* SpiceDB permissions used in the schema
|
|
59
|
+
*
|
|
60
|
+
* Permissions are named as verbs (actions) per SpiceDB best practices.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* SpiceDB permissions used in permission checks.
|
|
64
|
+
*
|
|
65
|
+
* Note: Organization-level permissions (manage) are handled via
|
|
66
|
+
* orgRole bypass in permission functions, not direct SpiceDB checks.
|
|
67
|
+
*/
|
|
68
|
+
const SpiceDbPermissions = {
|
|
69
|
+
VIEW: "view",
|
|
70
|
+
USE: "use",
|
|
71
|
+
EDIT: "edit",
|
|
72
|
+
DELETE: "delete"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, getSpiceDbConfig, isAuthzEnabled };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, lookupResources, readRelationships, resetSpiceClient, writeRelationship } from "./client.js";
|
|
2
|
+
import { OrgRole, ProjectRole, SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, getSpiceDbConfig, isAuthzEnabled } from "./config.js";
|
|
3
|
+
import { canEditProject, canUseProject, canViewProject, listAccessibleProjectIds } from "./permissions.js";
|
|
4
|
+
import { changeProjectRole, grantProjectAccess, listProjectMembers, removeProjectFromSpiceDb, revokeProjectAccess, syncOrgMemberToSpiceDb, syncProjectToSpiceDb } from "./sync.js";
|
|
5
|
+
export { type OrgRole, type ProjectRole, SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, canEditProject, canUseProject, canViewProject, changeProjectRole, checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, getSpiceDbConfig, grantProjectAccess, isAuthzEnabled, listAccessibleProjectIds, listProjectMembers, lookupResources, readRelationships, removeProjectFromSpiceDb, resetSpiceClient, revokeProjectAccess, syncOrgMemberToSpiceDb, syncProjectToSpiceDb, writeRelationship };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, getSpiceDbConfig, isAuthzEnabled } from "./config.js";
|
|
2
|
+
import { checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, lookupResources, readRelationships, resetSpiceClient, writeRelationship } from "./client.js";
|
|
3
|
+
import { canEditProject, canUseProject, canViewProject, listAccessibleProjectIds } from "./permissions.js";
|
|
4
|
+
import { changeProjectRole, grantProjectAccess, listProjectMembers, removeProjectFromSpiceDb, revokeProjectAccess, syncOrgMemberToSpiceDb, syncProjectToSpiceDb } from "./sync.js";
|
|
5
|
+
|
|
6
|
+
export { SpiceDbPermissions, SpiceDbRelations, SpiceDbResourceTypes, canEditProject, canUseProject, canViewProject, changeProjectRole, checkBulkPermissions, checkPermission, deleteRelationship, getSpiceClient, getSpiceDbConfig, grantProjectAccess, isAuthzEnabled, listAccessibleProjectIds, listProjectMembers, lookupResources, readRelationships, removeProjectFromSpiceDb, resetSpiceClient, revokeProjectAccess, syncOrgMemberToSpiceDb, syncProjectToSpiceDb, writeRelationship };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { OrgRole } from "./config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/auth/authz/permissions.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if a user can view a project.
|
|
7
|
+
*
|
|
8
|
+
* - If authz is disabled: returns true (current behavior)
|
|
9
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
10
|
+
* - Otherwise: checks SpiceDB
|
|
11
|
+
*/
|
|
12
|
+
declare function canViewProject(params: {
|
|
13
|
+
tenantId: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
orgRole: OrgRole;
|
|
17
|
+
}): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a user can use a project (invoke agents, create API keys, view traces).
|
|
20
|
+
*
|
|
21
|
+
* - If authz is disabled: returns true (current behavior)
|
|
22
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
23
|
+
* - Otherwise: checks SpiceDB for use permission
|
|
24
|
+
*/
|
|
25
|
+
declare function canUseProject(params: {
|
|
26
|
+
tenantId: string;
|
|
27
|
+
userId: string;
|
|
28
|
+
projectId: string;
|
|
29
|
+
orgRole: OrgRole;
|
|
30
|
+
}): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a user can edit a project (modify configurations).
|
|
33
|
+
*
|
|
34
|
+
* - If authz is disabled: only org owner/admin can edit
|
|
35
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
36
|
+
* - Otherwise: checks SpiceDB for edit permission
|
|
37
|
+
*/
|
|
38
|
+
declare function canEditProject(params: {
|
|
39
|
+
tenantId: string;
|
|
40
|
+
userId: string;
|
|
41
|
+
projectId: string;
|
|
42
|
+
orgRole: OrgRole;
|
|
43
|
+
}): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Get list of accessible project IDs for a user.
|
|
46
|
+
*
|
|
47
|
+
* - If authz is disabled: returns 'all' (no filtering needed)
|
|
48
|
+
* - If user is org owner/admin: returns 'all' (no filtering needed)
|
|
49
|
+
* - Otherwise: uses SpiceDB LookupResources
|
|
50
|
+
*/
|
|
51
|
+
declare function listAccessibleProjectIds(params: {
|
|
52
|
+
tenantId: string;
|
|
53
|
+
userId: string;
|
|
54
|
+
orgRole: OrgRole;
|
|
55
|
+
}): Promise<string[] | 'all'>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { canEditProject, canUseProject, canViewProject, listAccessibleProjectIds };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SpiceDbPermissions, SpiceDbResourceTypes, isAuthzEnabled } from "./config.js";
|
|
2
|
+
import { checkPermission, lookupResources } from "./client.js";
|
|
3
|
+
|
|
4
|
+
//#region src/auth/authz/permissions.ts
|
|
5
|
+
/**
|
|
6
|
+
* SpiceDB Permission Check Functions
|
|
7
|
+
*
|
|
8
|
+
* High-level functions for checking project-level permissions.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Check if a user can view a project.
|
|
12
|
+
*
|
|
13
|
+
* - If authz is disabled: returns true (current behavior)
|
|
14
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
15
|
+
* - Otherwise: checks SpiceDB
|
|
16
|
+
*/
|
|
17
|
+
async function canViewProject(params) {
|
|
18
|
+
if (!isAuthzEnabled(params.tenantId)) return true;
|
|
19
|
+
if (params.orgRole === "owner" || params.orgRole === "admin") return true;
|
|
20
|
+
return checkPermission({
|
|
21
|
+
resourceType: SpiceDbResourceTypes.PROJECT,
|
|
22
|
+
resourceId: params.projectId,
|
|
23
|
+
permission: SpiceDbPermissions.VIEW,
|
|
24
|
+
subjectType: SpiceDbResourceTypes.USER,
|
|
25
|
+
subjectId: params.userId
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a user can use a project (invoke agents, create API keys, view traces).
|
|
30
|
+
*
|
|
31
|
+
* - If authz is disabled: returns true (current behavior)
|
|
32
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
33
|
+
* - Otherwise: checks SpiceDB for use permission
|
|
34
|
+
*/
|
|
35
|
+
async function canUseProject(params) {
|
|
36
|
+
if (!isAuthzEnabled(params.tenantId)) return true;
|
|
37
|
+
if (params.orgRole === "owner" || params.orgRole === "admin") return true;
|
|
38
|
+
return checkPermission({
|
|
39
|
+
resourceType: SpiceDbResourceTypes.PROJECT,
|
|
40
|
+
resourceId: params.projectId,
|
|
41
|
+
permission: SpiceDbPermissions.USE,
|
|
42
|
+
subjectType: SpiceDbResourceTypes.USER,
|
|
43
|
+
subjectId: params.userId
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a user can edit a project (modify configurations).
|
|
48
|
+
*
|
|
49
|
+
* - If authz is disabled: only org owner/admin can edit
|
|
50
|
+
* - If user is org owner/admin: returns true (bypass)
|
|
51
|
+
* - Otherwise: checks SpiceDB for edit permission
|
|
52
|
+
*/
|
|
53
|
+
async function canEditProject(params) {
|
|
54
|
+
if (!isAuthzEnabled(params.tenantId)) return params.orgRole === "owner" || params.orgRole === "admin";
|
|
55
|
+
if (params.orgRole === "owner" || params.orgRole === "admin") return true;
|
|
56
|
+
return checkPermission({
|
|
57
|
+
resourceType: SpiceDbResourceTypes.PROJECT,
|
|
58
|
+
resourceId: params.projectId,
|
|
59
|
+
permission: SpiceDbPermissions.EDIT,
|
|
60
|
+
subjectType: SpiceDbResourceTypes.USER,
|
|
61
|
+
subjectId: params.userId
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get list of accessible project IDs for a user.
|
|
66
|
+
*
|
|
67
|
+
* - If authz is disabled: returns 'all' (no filtering needed)
|
|
68
|
+
* - If user is org owner/admin: returns 'all' (no filtering needed)
|
|
69
|
+
* - Otherwise: uses SpiceDB LookupResources
|
|
70
|
+
*/
|
|
71
|
+
async function listAccessibleProjectIds(params) {
|
|
72
|
+
if (!isAuthzEnabled(params.tenantId)) return "all";
|
|
73
|
+
if (params.orgRole === "owner" || params.orgRole === "admin") return "all";
|
|
74
|
+
return lookupResources({
|
|
75
|
+
resourceType: SpiceDbResourceTypes.PROJECT,
|
|
76
|
+
permission: SpiceDbPermissions.VIEW,
|
|
77
|
+
subjectType: SpiceDbResourceTypes.USER,
|
|
78
|
+
subjectId: params.userId
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { canEditProject, canUseProject, canViewProject, listAccessibleProjectIds };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { OrgRole, ProjectRole } from "./config.js";
|
|
2
|
+
|
|
3
|
+
//#region src/auth/authz/sync.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sync a user's org membership to SpiceDB.
|
|
7
|
+
* Call when: user joins org, role changes, user leaves org.
|
|
8
|
+
*/
|
|
9
|
+
declare function syncOrgMemberToSpiceDb(params: {
|
|
10
|
+
tenantId: string;
|
|
11
|
+
userId: string;
|
|
12
|
+
role: OrgRole;
|
|
13
|
+
action: 'add' | 'remove';
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Change a user's organization role.
|
|
17
|
+
* Removes the old role and adds the new one atomically in a single transaction.
|
|
18
|
+
* Call when: user's org role is updated (e.g., member -> admin).
|
|
19
|
+
*/
|
|
20
|
+
declare function changeOrgRole(params: {
|
|
21
|
+
tenantId: string;
|
|
22
|
+
userId: string;
|
|
23
|
+
oldRole: OrgRole;
|
|
24
|
+
newRole: OrgRole;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Sync a new project to SpiceDB.
|
|
28
|
+
* Links project to org and grants creator project_admin role.
|
|
29
|
+
* Call when: project is created.
|
|
30
|
+
*/
|
|
31
|
+
declare function syncProjectToSpiceDb(params: {
|
|
32
|
+
tenantId: string;
|
|
33
|
+
projectId: string;
|
|
34
|
+
creatorUserId: string;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Grant project access to a user.
|
|
38
|
+
*/
|
|
39
|
+
declare function grantProjectAccess(params: {
|
|
40
|
+
tenantId: string;
|
|
41
|
+
projectId: string;
|
|
42
|
+
userId: string;
|
|
43
|
+
role: ProjectRole;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Revoke project access from a user.
|
|
47
|
+
*/
|
|
48
|
+
declare function revokeProjectAccess(params: {
|
|
49
|
+
tenantId: string;
|
|
50
|
+
projectId: string;
|
|
51
|
+
userId: string;
|
|
52
|
+
role: ProjectRole;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Change a user's project role.
|
|
56
|
+
* Removes the old role and adds the new one atomically in a single transaction.
|
|
57
|
+
*/
|
|
58
|
+
declare function changeProjectRole(params: {
|
|
59
|
+
tenantId: string;
|
|
60
|
+
projectId: string;
|
|
61
|
+
userId: string;
|
|
62
|
+
oldRole: ProjectRole;
|
|
63
|
+
newRole: ProjectRole;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Remove a project from SpiceDB.
|
|
67
|
+
* Call when: project is deleted.
|
|
68
|
+
*/
|
|
69
|
+
declare function removeProjectFromSpiceDb(params: {
|
|
70
|
+
tenantId: string;
|
|
71
|
+
projectId: string;
|
|
72
|
+
}): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* List all explicit project members from SpiceDB.
|
|
75
|
+
* Returns users with project_admin, project_member, or project_viewer roles.
|
|
76
|
+
*/
|
|
77
|
+
declare function listProjectMembers(params: {
|
|
78
|
+
tenantId: string;
|
|
79
|
+
projectId: string;
|
|
80
|
+
}): Promise<Array<{
|
|
81
|
+
userId: string;
|
|
82
|
+
role: ProjectRole;
|
|
83
|
+
}>>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { changeOrgRole, changeProjectRole, grantProjectAccess, listProjectMembers, removeProjectFromSpiceDb, revokeProjectAccess, syncOrgMemberToSpiceDb, syncProjectToSpiceDb };
|