Skip to content

Commit 5146541

Browse files
authored
Merge pull request #266 from weaviate/1.29/align-roles-api-with-py-client
Update roles API to use latest schema and align with recent py changes
2 parents 3f440ad + a5b3c49 commit 5146541

File tree

6 files changed

+110
-36
lines changed

6 files changed

+110
-36
lines changed

.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ env:
1212
WEAVIATE_126: 1.26.14
1313
WEAVIATE_127: 1.27.11
1414
WEAVIATE_128: 1.28.4
15-
WEAVIATE_129: 1.29.0-rc.1
15+
WEAVIATE_129: 1.29.0-rc.2
1616

1717
jobs:
1818
checks:

src/openapi/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ export interface definitions {
349349
| 'update_collections'
350350
| 'delete_collections'
351351
| 'assign_and_revoke_users'
352+
| 'read_users'
352353
| 'create_tenants'
353354
| 'read_tenants'
354355
| 'update_tenants'

src/roles/index.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
PermissionsInput,
1111
Role,
1212
RolesPermission,
13+
UsersPermission,
1314
} from './types.js';
1415
import { Map } from './util.js';
1516

@@ -166,21 +167,28 @@ export const permissions = {
166167
return out;
167168
});
168169
},
169-
nodes: (args: {
170-
collection: string | string[];
171-
verbosity?: 'verbose' | 'minimal';
172-
read?: boolean;
173-
}): NodesPermission[] => {
174-
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
175-
return collections.flatMap((collection) => {
170+
nodes: {
171+
minimal: (args: { read?: boolean }): NodesPermission[] => {
176172
const out: NodesPermission = {
177-
collection,
173+
collection: '*',
178174
actions: [],
179-
verbosity: args.verbosity || 'verbose',
175+
verbosity: 'minimal',
180176
};
181177
if (args.read) out.actions.push('read_nodes');
182-
return out;
183-
});
178+
return [out];
179+
},
180+
verbose: (args: { collection: string | string[]; read?: boolean }): NodesPermission[] => {
181+
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
182+
return collections.flatMap((collection) => {
183+
const out: NodesPermission = {
184+
collection,
185+
actions: [],
186+
verbosity: 'verbose',
187+
};
188+
if (args.read) out.actions.push('read_nodes');
189+
return out;
190+
});
191+
},
184192
},
185193
roles: (args: {
186194
role: string | string[];
@@ -199,6 +207,19 @@ export const permissions = {
199207
return out;
200208
});
201209
},
210+
users: (args: {
211+
user: string | string[];
212+
assign_and_revoke?: boolean;
213+
read?: boolean;
214+
}): UsersPermission[] => {
215+
const users = Array.isArray(args.user) ? args.user : [args.user];
216+
return users.flatMap((user) => {
217+
const out: UsersPermission = { users: user, actions: [] };
218+
if (args.assign_and_revoke) out.actions.push('assign_and_revoke_users');
219+
if (args.read) out.actions.push('read_users');
220+
return out;
221+
});
222+
},
202223
};
203224

204225
export default roles;

src/roles/integration.test.ts

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import weaviate, { ApiKey, Permission, Role, WeaviateClient } from '..';
2-
import {
3-
WeaviateInsufficientPermissionsError,
4-
WeaviateStartUpError,
5-
WeaviateUnexpectedStatusCodeError,
6-
} from '../errors';
2+
import { WeaviateStartUpError, WeaviateUnexpectedStatusCodeError } from '../errors';
73
import { DbVersion } from '../utils/dbVersion';
84

95
const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 29, 0)
@@ -34,17 +30,6 @@ only('Integration testing of the roles namespace', () => {
3430
})
3531
).rejects.toThrowError(WeaviateStartUpError));
3632

37-
it('should fail with insufficient permissions if permission-less key provided', async () => {
38-
const unauthenticatedClient = await weaviate.connectToLocal({
39-
port: 8091,
40-
grpcPort: 50062,
41-
authCredentials: new ApiKey('custom-key'),
42-
});
43-
await expect(unauthenticatedClient.roles.listAll()).rejects.toThrowError(
44-
WeaviateInsufficientPermissionsError
45-
);
46-
});
47-
4833
it('should check the existance of a real role', async () => {
4934
const exists = await client.roles.exists('admin');
5035
expect(exists).toBeTruthy();
@@ -73,6 +58,7 @@ only('Integration testing of the roles namespace', () => {
7358
dataPermissions: [],
7459
nodesPermissions: [],
7560
rolesPermissions: [],
61+
usersPermissions: [],
7662
},
7763
},
7864
{
@@ -86,6 +72,7 @@ only('Integration testing of the roles namespace', () => {
8672
dataPermissions: [],
8773
nodesPermissions: [],
8874
rolesPermissions: [],
75+
usersPermissions: [],
8976
},
9077
},
9178
{
@@ -110,6 +97,7 @@ only('Integration testing of the roles namespace', () => {
11097
dataPermissions: [],
11198
nodesPermissions: [],
11299
rolesPermissions: [],
100+
usersPermissions: [],
113101
},
114102
},
115103
{
@@ -134,17 +122,17 @@ only('Integration testing of the roles namespace', () => {
134122
],
135123
nodesPermissions: [],
136124
rolesPermissions: [],
125+
usersPermissions: [],
137126
},
138127
},
139128
{
140-
roleName: 'nodes',
141-
permissions: weaviate.permissions.nodes({
129+
roleName: 'nodes-verbose',
130+
permissions: weaviate.permissions.nodes.verbose({
142131
collection: 'Some-collection',
143-
verbosity: 'verbose',
144132
read: true,
145133
}),
146134
expected: {
147-
name: 'nodes',
135+
name: 'nodes-verbose',
148136
backupsPermissions: [],
149137
clusterPermissions: [],
150138
collectionsPermissions: [],
@@ -153,6 +141,23 @@ only('Integration testing of the roles namespace', () => {
153141
{ collection: 'Some-collection', verbosity: 'verbose', actions: ['read_nodes'] },
154142
],
155143
rolesPermissions: [],
144+
usersPermissions: [],
145+
},
146+
},
147+
{
148+
roleName: 'nodes-minimal',
149+
permissions: weaviate.permissions.nodes.minimal({
150+
read: true,
151+
}),
152+
expected: {
153+
name: 'nodes-minimal',
154+
backupsPermissions: [],
155+
clusterPermissions: [],
156+
collectionsPermissions: [],
157+
dataPermissions: [],
158+
nodesPermissions: [{ collection: '*', verbosity: 'minimal', actions: ['read_nodes'] }],
159+
rolesPermissions: [],
160+
usersPermissions: [],
156161
},
157162
},
158163
{
@@ -174,6 +179,25 @@ only('Integration testing of the roles namespace', () => {
174179
rolesPermissions: [
175180
{ role: 'some-role', actions: ['create_roles', 'read_roles', 'update_roles', 'delete_roles'] },
176181
],
182+
usersPermissions: [],
183+
},
184+
},
185+
{
186+
roleName: 'users',
187+
permissions: weaviate.permissions.users({
188+
user: 'some-user',
189+
assign_and_revoke: true,
190+
read: true,
191+
}),
192+
expected: {
193+
name: 'users',
194+
backupsPermissions: [],
195+
clusterPermissions: [],
196+
collectionsPermissions: [],
197+
dataPermissions: [],
198+
nodesPermissions: [],
199+
rolesPermissions: [],
200+
usersPermissions: [{ users: 'some-user', actions: ['assign_and_revoke_users', 'read_users'] }],
177201
},
178202
},
179203
];
@@ -194,7 +218,9 @@ only('Integration testing of the roles namespace', () => {
194218

195219
afterAll(() =>
196220
Promise.all(
197-
['backups', 'cluster', 'collections', 'data', 'nodes', 'roles'].map((n) => client.roles.delete(n))
221+
['backups', 'cluster', 'collections', 'data', 'nodes-verbose', 'nodes-minimal', 'roles', 'users'].map(
222+
(n) => client.roles.delete(n)
223+
)
198224
)
199225
);
200226
});

src/roles/types.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type DataAction = Extract<
1616
>;
1717
export type NodesAction = Extract<Action, 'read_nodes'>;
1818
export type RolesAction = Extract<Action, 'create_roles' | 'read_roles' | 'update_roles' | 'delete_roles'>;
19+
export type UsersAction = Extract<Action, 'read_users' | 'assign_and_revoke_users'>;
1920

2021
export type BackupsPermission = {
2122
collection: string;
@@ -47,6 +48,11 @@ export type RolesPermission = {
4748
actions: RolesAction[];
4849
};
4950

51+
export type UsersPermission = {
52+
users: string;
53+
actions: UsersAction[];
54+
};
55+
5056
export type Role = {
5157
name: string;
5258
backupsPermissions: BackupsPermission[];
@@ -55,6 +61,7 @@ export type Role = {
5561
dataPermissions: DataPermission[];
5662
nodesPermissions: NodesPermission[];
5763
rolesPermissions: RolesPermission[];
64+
usersPermissions: UsersPermission[];
5865
};
5966

6067
export type Permission =
@@ -63,6 +70,7 @@ export type Permission =
6370
| CollectionsPermission
6471
| DataPermission
6572
| NodesPermission
66-
| RolesPermission;
73+
| RolesPermission
74+
| UsersPermission;
6775

6876
export type PermissionsInput = Permission | Permission[] | Permission[][] | (Permission | Permission[])[];

src/roles/util.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
Role,
1616
RolesAction,
1717
RolesPermission,
18+
UsersAction,
19+
UsersPermission,
1820
} from './types.js';
1921

2022
export class PermissionGuards {
@@ -46,6 +48,8 @@ export class PermissionGuards {
4648
PermissionGuards.includes(permission, 'read_nodes');
4749
static isRoles = (permission: Permission): permission is RolesPermission =>
4850
PermissionGuards.includes(permission, 'create_role', 'read_roles', 'update_roles', 'delete_roles');
51+
static isUsers = (permission: Permission): permission is UsersPermission =>
52+
PermissionGuards.includes(permission, 'read_users', 'assign_and_revoke_users');
4953
static isPermission = (permissions: PermissionsInput): permissions is Permission =>
5054
!Array.isArray(permissions);
5155
static isPermissionArray = (permissions: PermissionsInput): permissions is Permission[] =>
@@ -89,6 +93,8 @@ export class Map {
8993
}));
9094
} else if (PermissionGuards.isRoles(permission)) {
9195
return Array.from(permission.actions).map((action) => ({ roles: { role: permission.role }, action }));
96+
} else if (PermissionGuards.isUsers(permission)) {
97+
return Array.from(permission.actions).map((action) => ({ users: { users: permission.users }, action }));
9298
} else {
9399
throw new Error(`Unknown permission type: ${JSON.stringify(permission, null, 2)}`);
94100
}
@@ -102,6 +108,7 @@ export class Map {
102108
data: {} as Record<string, DataPermission>,
103109
nodes: {} as Record<string, NodesPermission>,
104110
roles: {} as Record<string, RolesPermission>,
111+
users: {} as Record<string, UsersPermission>,
105112
};
106113
role.permissions.forEach((permission) => {
107114
if (permission.backups !== undefined) {
@@ -123,9 +130,14 @@ export class Map {
123130
if (perms.data[key] === undefined) perms.data[key] = { collection: key, actions: [] };
124131
perms.data[key].actions.push(permission.action as DataAction);
125132
} else if (permission.nodes !== undefined) {
126-
const { collection, verbosity } = permission.nodes;
127-
if (collection === undefined) throw new Error('Nodes permission missing collection');
133+
let { collection } = permission.nodes;
134+
const { verbosity } = permission.nodes;
128135
if (verbosity === undefined) throw new Error('Nodes permission missing verbosity');
136+
if (verbosity === 'verbose') {
137+
if (collection === undefined) throw new Error('Nodes permission missing collection');
138+
} else if (verbosity === 'minimal') collection = '*';
139+
else throw new Error('Nodes permission missing verbosity');
140+
129141
const key = `${collection}#${verbosity}`;
130142
if (perms.nodes[key] === undefined) perms.nodes[key] = { collection, verbosity, actions: [] };
131143
perms.nodes[key].actions.push(permission.action as NodesAction);
@@ -134,6 +146,11 @@ export class Map {
134146
if (key === undefined) throw new Error('Roles permission missing role');
135147
if (perms.roles[key] === undefined) perms.roles[key] = { role: key, actions: [] };
136148
perms.roles[key].actions.push(permission.action as RolesAction);
149+
} else if (permission.users !== undefined) {
150+
const key = permission.users.users;
151+
if (key === undefined) throw new Error('Users permission missing user');
152+
if (perms.users[key] === undefined) perms.users[key] = { users: key, actions: [] };
153+
perms.users[key].actions.push(permission.action as UsersAction);
137154
}
138155
});
139156
return {
@@ -144,6 +161,7 @@ export class Map {
144161
dataPermissions: Object.values(perms.data),
145162
nodesPermissions: Object.values(perms.nodes),
146163
rolesPermissions: Object.values(perms.roles),
164+
usersPermissions: Object.values(perms.users),
147165
};
148166
};
149167

0 commit comments

Comments
 (0)