Skip to content

Commit 4273a30

Browse files
committed
feat: add permissions for OIDC groups
Note: the OpenAPI schema is manually modified to exclude 'db' group type, because it is not currently supported on the server. It will be removed from the next RC release (and GA too). Regenerate the schema once this PR is merged: #343
1 parent 25e9687 commit 4273a30

File tree

6 files changed

+106
-9
lines changed

6 files changed

+106
-9
lines changed

src/groups/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,29 @@ export interface GroupsOIDC {
1313
/**
1414
* Get the roles assigned to a group specific to the configured OIDC's dynamic auth functionality.
1515
*
16-
* @param groupID [string] The group ID to get the roles for.
17-
* @return A list of roles assigned tot he group.
16+
* @param {string} groupID The group ID to get the roles for.
17+
* @returns {Promise<Record<string, Role>>} A map of roles assigned to the group.
1818
*/
1919
getAssignedRoles(groupID: string, includePermissions?: boolean): Promise<Record<string, Role>>;
2020

2121
/**
2222
* Assign roles to a group specific to the configured OIDC's dynamic auth functionality.
2323
*
24-
* @param group_id [string] The group to assign the roles to.
25-
* @param role_names [string[]] The names of the roles to assign to the group.
24+
* @param {string} groupID The group ID to get the roles for.
25+
* @param {string | string[]} roles The names of the roles to assign to the group.
2626
*/
2727
assignRoles(groupID: string, roles: string | string[]): Promise<void>;
2828
/**
2929
* Revoke roles from a group specific to the configured OIDC's dynamic auth functionality.
3030
*
31-
* @param group_id [string] The group to assign the roles to.
32-
* @param role_names [string[]] The names of the roles to assign to the group.
31+
* @param {string} groupID The group ID to get the roles for.
32+
* @param {string | string[]} roles The names of the roles to revoke from the group.
3333
*/
3434
revokeRoles(groupID: string, roles: string | string[]): Promise<void>;
3535
/**
3636
* Get the known group names specific to the configured OIDC's dynamic auth functionality.
3737
*
38-
* @return A list of known group names.
38+
* @returns {Promise<string[]>} A list of known group names.
3939
*/
4040
getKnownGroupNames(): Promise<string[]>;
4141
}

src/openapi/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export interface definitions {
319319
* @description If the group contains OIDC or database users.
320320
* @enum {string}
321321
*/
322-
GroupType: 'db' | 'oidc';
322+
GroupType: 'oidc';
323323
/**
324324
* @description the type of user
325325
* @enum {string}

src/roles/index.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
CollectionsPermission,
1313
DataPermission,
1414
GroupAssignment,
15+
GroupsAction,
16+
GroupsPermission,
1517
NodesPermission,
1618
Permission,
1719
PermissionsInput,
@@ -109,7 +111,7 @@ export interface Roles {
109111
* Get the IDs and group type of groups that assigned this role.
110112
*
111113
* @param {string} roleName The name of the role to check.
112-
* @return {Promise<GroupAssignment[]>} A promise that resolves to an array of group names assigned to this role.
114+
* @returns {Promise<GroupAssignment[]>} A promise that resolves to an array of group names assigned to this role.
113115
*/
114116
getGroupAssignments: (roleName: string) => Promise<GroupAssignment[]>;
115117
}
@@ -285,6 +287,49 @@ export const permissions = {
285287
return out;
286288
});
287289
},
290+
/**
291+
* This namespace contains methods to create permissions specific to RBAC groups.
292+
*/
293+
groups: {
294+
/**
295+
* Create a set of permissions for 'oidc' groups.
296+
*
297+
* @param {string | string[]} args.groupID IDs of the groups with permissions.
298+
* @param {boolean} [args.read] Whether to allow reading groups. Defaults to `false`.
299+
* @param {boolean} [args.assignAndRevoke] Whether to allow changing group assignements. Defaults to `false`.
300+
* @returns {GroupsPermission[]} The permissions for managing groups.
301+
*/
302+
oidc: (args: {
303+
groupID: string | string[];
304+
read?: boolean;
305+
assignAndRevoke?: boolean;
306+
}): GroupsPermission[] => {
307+
const groups = Array.isArray(args.groupID) ? args.groupID : [args.groupID];
308+
const actions: GroupsAction[] = [];
309+
if (args.read) actions.push('read_groups');
310+
if (args.assignAndRevoke) actions.push('assign_and_revoke_groups');
311+
return groups.map((gid) => ({ groupID: gid, groupType: 'oidc', actions }));
312+
},
313+
/**
314+
* Create a set of permissions for 'db' groups.
315+
*
316+
* @param {string | string[]} args.groupID IDs of the groups with permissions.
317+
* @param {boolean} [args.read] Whether to allow reading groups. Defaults to `false`.
318+
* @param {boolean} [args.assignAndRevoke] Whether to allow changing group assignements. Defaults to `false`.
319+
* @returns {GroupsPermission[]} The permissions for managing groups.
320+
*/
321+
// db: (args: {
322+
// groupID: string | string[];
323+
// read?: boolean;
324+
// assignAndRevoke?: boolean;
325+
// }): GroupsPermission[] => {
326+
// const groups = Array.isArray(args.groupID) ? args.groupID : [args.groupID];
327+
// const actions: GroupsAction[] = [];
328+
// if (args.read) actions.push('read_groups');
329+
// if (args.assignAndRevoke) actions.push('assign_and_revoke_groups');
330+
// return groups.map((gid) => ({ groupID: gid, groupType: 'db', actions }));
331+
// },
332+
},
288333
/**
289334
* This namespace contains methods to create permissions specific to nodes.
290335
*/

src/roles/integration.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const emptyPermissions = {
2525
clusterPermissions: [],
2626
collectionsPermissions: [],
2727
dataPermissions: [],
28+
groupsPermissions: [],
2829
nodesPermissions: [],
2930
rolesPermissions: [],
3031
tenantsPermissions: [],
@@ -160,6 +161,23 @@ const testCases: TestCase[] = [
160161
],
161162
},
162163
},
164+
{
165+
roleName: 'groups-oidc',
166+
requireVersion: [1, 33, 0],
167+
permissions: weaviate.permissions.groups.oidc({
168+
groupID: ['G1', 'G2'],
169+
read: true,
170+
assignAndRevoke: true,
171+
}),
172+
expected: {
173+
name: 'groups-oidc',
174+
...emptyPermissions,
175+
groupsPermissions: [
176+
{ groupID: 'G1', groupType: 'oidc', actions: ['read_groups', 'assign_and_revoke_groups'] },
177+
{ groupID: 'G2', groupType: 'oidc', actions: ['read_groups', 'assign_and_revoke_groups'] },
178+
],
179+
},
180+
},
163181
{
164182
roleName: 'nodes-verbose',
165183
permissions: weaviate.permissions.nodes.verbose({

src/roles/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type DataAction = Extract<
1818
Action,
1919
'create_data' | 'delete_data' | 'read_data' | 'update_data' | 'manage_data'
2020
>;
21+
export type GroupsAction = Extract<Action, 'read_groups' | 'assign_and_revoke_groups'>;
2122
export type NodesAction = Extract<Action, 'read_nodes'>;
2223
export type RolesAction = Extract<Action, 'create_roles' | 'read_roles' | 'update_roles' | 'delete_roles'>;
2324
export type TenantsAction = Extract<
@@ -62,6 +63,12 @@ export type DataPermission = {
6263
actions: DataAction[];
6364
};
6465

66+
export type GroupsPermission = {
67+
groupID: string;
68+
groupType: WeaviateGroupType;
69+
actions: GroupsAction[];
70+
};
71+
6572
export type NodesPermission = {
6673
collection: string;
6774
verbosity: 'verbose' | 'minimal';
@@ -91,6 +98,7 @@ export type Role = {
9198
clusterPermissions: ClusterPermission[];
9299
collectionsPermissions: CollectionsPermission[];
93100
dataPermissions: DataPermission[];
101+
groupsPermissions: GroupsPermission[];
94102
nodesPermissions: NodesPermission[];
95103
rolesPermissions: RolesPermission[];
96104
tenantsPermissions: TenantsPermission[];
@@ -103,6 +111,7 @@ export type Permission =
103111
| ClusterPermission
104112
| CollectionsPermission
105113
| DataPermission
114+
| GroupsPermission
106115
| NodesPermission
107116
| RolesPermission
108117
| TenantsPermission

src/roles/util.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
DataAction,
2020
DataPermission,
2121
GroupAssignment,
22+
GroupsAction,
23+
GroupsPermission,
2224
NodesAction,
2325
NodesPermission,
2426
Permission,
@@ -67,6 +69,8 @@ export class PermissionGuards {
6769
'read_data',
6870
'update_data'
6971
);
72+
static isGroups = (permission: Permission): permission is GroupsPermission =>
73+
PermissionGuards.includes<GroupsAction>(permission, 'read_groups', 'assign_and_revoke_groups');
7074
static isNodes = (permission: Permission): permission is NodesPermission =>
7175
PermissionGuards.includes<NodesAction>(permission, 'read_nodes');
7276
static isRoles = (permission: Permission): permission is RolesPermission =>
@@ -129,6 +133,11 @@ export class Map {
129133
data: permission,
130134
action,
131135
}));
136+
} else if (PermissionGuards.isGroups(permission)) {
137+
return Array.from(permission.actions).map((action) => ({
138+
groups: { group: permission.groupID, groupType: permission.groupType },
139+
action,
140+
}));
132141
} else if (PermissionGuards.isNodes(permission)) {
133142
return Array.from(permission.actions).map((action) => ({
134143
nodes: permission,
@@ -207,6 +216,7 @@ class PermissionsMapping {
207216
cluster: {},
208217
collections: {},
209218
data: {},
219+
groups: {},
210220
nodes: {},
211221
roles: {},
212222
tenants: {},
@@ -230,6 +240,7 @@ class PermissionsMapping {
230240
clusterPermissions: Object.values(this.mappings.cluster),
231241
collectionsPermissions: Object.values(this.mappings.collections),
232242
dataPermissions: Object.values(this.mappings.data),
243+
groupsPermissions: Object.values(this.mappings.groups),
233244
nodesPermissions: Object.values(this.mappings.nodes),
234245
rolesPermissions: Object.values(this.mappings.roles),
235246
tenantsPermissions: Object.values(this.mappings.tenants),
@@ -286,6 +297,18 @@ class PermissionsMapping {
286297
}
287298
};
288299

300+
private groups = (permission: WeaviatePermission) => {
301+
if (permission.groups !== undefined) {
302+
const { group, groupType } = permission.groups;
303+
if (group === undefined) throw new Error('Group permission missing groupID');
304+
if (groupType === undefined) throw new Error('Group permission missing groupType');
305+
const key = `${groupType}#${group}`;
306+
if (this.mappings.groups[key] === undefined)
307+
this.mappings.groups[key] = { groupType, groupID: group, actions: [] };
308+
this.mappings.groups[key].actions.push(permission.action as GroupsAction);
309+
}
310+
};
311+
289312
private nodes = (permission: WeaviatePermission) => {
290313
if (permission.nodes !== undefined) {
291314
let { collection } = permission.nodes;
@@ -337,6 +360,7 @@ class PermissionsMapping {
337360
this.cluster(permission);
338361
this.collections(permission);
339362
this.data(permission);
363+
this.groups(permission);
340364
this.nodes(permission);
341365
this.roles(permission);
342366
this.tenants(permission);
@@ -350,6 +374,7 @@ type PermissionMappings = {
350374
cluster: Record<string, ClusterPermission>;
351375
collections: Record<string, CollectionsPermission>;
352376
data: Record<string, DataPermission>;
377+
groups: Record<string, GroupsPermission>;
353378
nodes: Record<string, NodesPermission>;
354379
roles: Record<string, RolesPermission>;
355380
tenants: Record<string, TenantsPermission>;

0 commit comments

Comments
 (0)