Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ env:
WEAVIATE_126: 1.26.14
WEAVIATE_127: 1.27.11
WEAVIATE_128: 1.28.4
WEAVIATE_129: 1.29.0-rc.0
WEAVIATE_129: 1.29.0-rc.0-a8c0bce

jobs:
checks:
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { LiveChecker, OpenidConfigurationGetter, ReadyChecker } from './misc/ind
import weaviateV2 from './v2/index.js';

import { ConsistencyLevel } from './data/replication.js';
import users, { Users } from './users/index.js';

export type ProtocolParams = {
/**
Expand Down Expand Up @@ -105,6 +106,7 @@ export interface WeaviateClient {
collections: Collections;
oidcAuth?: OidcAuthenticator;
roles: Roles;
users: Users;

close: () => Promise<void>;
getMeta: () => Promise<Meta>;
Expand Down Expand Up @@ -224,6 +226,7 @@ async function client(params: ClientParams): Promise<WeaviateClient> {
cluster: cluster(connection),
collections: collections(connection, dbVersionSupport),
roles: roles(connection),
users: users(connection),
close: () => Promise.resolve(connection.close()), // hedge against future changes to add I/O to .close()
getMeta: () => new MetaGetter(connection).do(),
getOpenIDConfig: () => new OpenidConfigurationGetter(connection.http).do(),
Expand Down
1 change: 1 addition & 0 deletions src/openapi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type WeaviateMultiTenancyConfig = WeaviateClass['multiTenancyConfig'];
export type WeaviateReplicationConfig = WeaviateClass['replicationConfig'];
export type WeaviateShardingConfig = WeaviateClass['shardingConfig'];
export type WeaviateShardStatus = definitions['ShardStatusGetResponse'];
export type WeaviateUser = definitions['UserInfo'];
export type WeaviateVectorIndexConfig = WeaviateClass['vectorIndexConfig'];
export type WeaviateVectorsConfig = WeaviateClass['vectorConfig'];
export type WeaviateVectorConfig = definitions['VectorConfig'];
Expand Down
107 changes: 36 additions & 71 deletions src/roles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,29 @@ import {
PermissionsInput,
Role,
RolesPermission,
User,
} from './types.js';
import { Map } from './util.js';

export interface Roles {
listAll: () => Promise<Record<string, Role>>;
ofCurrentUser: () => Promise<Record<string, Role>>;
byName: (roleName: string) => Promise<Role | null>;
byUser: (user: string) => Promise<Record<string, Role>>;
assignedUsers: (roleName: string) => Promise<Record<string, User>>;
assignedUserIds: (roleName: string) => Promise<string[]>;
delete: (roleName: string) => Promise<void>;
create: (roleName: string, permissions: PermissionsInput) => Promise<Role>;
assignToUser: (roleNames: string | string[], user: string) => Promise<void>;
exists: (roleName: string) => Promise<boolean>;
revokeFromUser: (roleNames: string | string[], user: string) => Promise<void>;
addPermissions: (roleName: string, permissions: PermissionsInput) => Promise<void>;
removePermissions: (roleName: string, permissions: PermissionsInput) => Promise<void>;
hasPermission: (roleName: string, permission: Permission) => Promise<boolean>;
hasPermissions: (roleName: string, permission: Permission) => Promise<boolean>;
}

const roles = (connection: ConnectionREST): Roles => {
return {
listAll: () => connection.get<WeaviateRole[]>('/authz/roles').then(Map.roles),
ofCurrentUser: () => connection.get<WeaviateRole[]>('/authz/users/own-roles').then(Map.roles),
byName: (roleName: string) =>
connection.get<WeaviateRole>(`/authz/roles/${roleName}`).then(Map.roleFromWeaviate),
byUser: (user: string) => connection.get<WeaviateRole[]>(`/authz/users/${user}/roles`).then(Map.roles),
assignedUsers: (roleName: string) =>
connection.get<string[]>(`/authz/roles/${roleName}/users`).then(Map.users),
assignedUserIds: (roleName: string) => connection.get<string[]>(`/authz/roles/${roleName}/users`),
create: (roleName: string, permissions: PermissionsInput) => {
const perms = Map.flattenPermissions(permissions).map(Map.permissionToWeaviate);
const perms = Map.flattenPermissions(permissions).flatMap(Map.permissionToWeaviate);
return connection
.postEmpty<WeaviateRole>('/authz/roles', {
name: roleName,
Expand All @@ -54,43 +46,34 @@ const roles = (connection: ConnectionREST): Roles => {
.get(`/authz/roles/${roleName}`)
.then(() => true)
.catch(() => false),
assignToUser: (roleNames: string | string[], user: string) =>
connection.postEmpty(`/authz/users/${user}/assign`, {
roles: Array.isArray(roleNames) ? roleNames : [roleNames],
}),
revokeFromUser: (roleNames: string | string[], user: string) =>
connection.postEmpty(`/authz/users/${user}/revoke`, {
roles: Array.isArray(roleNames) ? roleNames : [roleNames],
}),
addPermissions: (roleName: string, permissions: PermissionsInput) =>
connection.postEmpty(`/authz/roles/${roleName}/add-permissions`, { permissions }),
removePermissions: (roleName: string, permissions: PermissionsInput) =>
connection.postEmpty(`/authz/roles/${roleName}/remove-permissions`, { permissions }),
hasPermission: (roleName: string, permission: Permission) =>
connection.postReturn<WeaviatePermission, boolean>(
`/authz/roles/${roleName}/has-permission`,
Map.permissionToWeaviate(permission)
),
hasPermissions: (roleName: string, permission: Permission | Permission[]) =>
Promise.all(
(Array.isArray(permission) ? permission : [permission])
.flatMap((p) => Map.permissionToWeaviate(p))
.map((p) =>
connection.postReturn<WeaviatePermission, boolean>(`/authz/roles/${roleName}/has-permission`, p)
)
).then((r) => r.every((b) => b)),
};
};

export const permissions = {
backup: (args: { collection: string | string[]; manage?: boolean }): BackupsPermission[] => {
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
return collections.flatMap((collection) => {
const out: BackupsPermission[] = [];
if (args.manage) {
out.push({ collection, action: 'manage_backups' });
}
const out: BackupsPermission = { collection, actions: [] };
if (args.manage) out.actions.push('manage_backups');
return out;
});
},
cluster: (args: { read?: boolean }): ClusterPermission[] => {
const out: ClusterPermission[] = [];
if (args.read) {
out.push({ action: 'read_cluster' });
}
return out;
const out: ClusterPermission = { actions: [] };
if (args.read) out.actions.push('read_cluster');
return [out];
},
collections: (args: {
collection: string | string[];
Expand All @@ -101,19 +84,11 @@ export const permissions = {
}): CollectionsPermission[] => {
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
return collections.flatMap((collection) => {
const out: CollectionsPermission[] = [];
if (args.create_collection) {
out.push({ collection, action: 'create_collections' });
}
if (args.read_config) {
out.push({ collection, action: 'read_collections' });
}
if (args.update_config) {
out.push({ collection, action: 'update_collections' });
}
if (args.delete_collection) {
out.push({ collection, action: 'delete_collections' });
}
const out: CollectionsPermission = { collection, actions: [] };
if (args.create_collection) out.actions.push('create_collections');
if (args.read_config) out.actions.push('read_collections');
if (args.update_config) out.actions.push('update_collections');
if (args.delete_collection) out.actions.push('delete_collections');
return out;
});
},
Expand All @@ -126,19 +101,11 @@ export const permissions = {
}): DataPermission[] => {
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
return collections.flatMap((collection) => {
const out: DataPermission[] = [];
if (args.create) {
out.push({ collection, action: 'create_data' });
}
if (args.read) {
out.push({ collection, action: 'read_data' });
}
if (args.update) {
out.push({ collection, action: 'update_data' });
}
if (args.delete) {
out.push({ collection, action: 'delete_data' });
}
const out: DataPermission = { collection, actions: [] };
if (args.create) out.actions.push('create_data');
if (args.read) out.actions.push('read_data');
if (args.update) out.actions.push('update_data');
if (args.delete) out.actions.push('delete_data');
return out;
});
},
Expand All @@ -149,23 +116,21 @@ export const permissions = {
}): NodesPermission[] => {
const collections = Array.isArray(args.collection) ? args.collection : [args.collection];
return collections.flatMap((collection) => {
const out: NodesPermission[] = [];
if (args.read) {
out.push({ collection, action: 'read_nodes', verbosity: args.verbosity || 'verbose' });
}
const out: NodesPermission = {
collection,
actions: [],
verbosity: args.verbosity || 'verbose',
};
if (args.read) out.actions.push('read_nodes');
return out;
});
},
roles: (args: { role: string | string[]; read?: boolean; manage?: boolean }): RolesPermission[] => {
const roles = Array.isArray(args.role) ? args.role : [args.role];
return roles.flatMap((role) => {
const out: RolesPermission[] = [];
if (args.read) {
out.push({ role, action: 'read_roles' });
}
if (args.manage) {
out.push({ role, action: 'manage_roles' });
}
const out: RolesPermission = { role, actions: [] };
if (args.read) out.actions.push('read_roles');
if (args.manage) out.actions.push('manage_roles');
return out;
});
},
Expand Down
39 changes: 21 additions & 18 deletions src/roles/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '../errors';
import { DbVersion } from '../utils/dbVersion';

const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 28, 0)
const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 29, 0)
? describe
: describe.skip;

Expand Down Expand Up @@ -45,11 +45,6 @@ only('Integration testing of the roles namespace', () => {
);
});

it('should get roles by user', async () => {
const roles = await client.roles.byUser('admin-user');
expect(Object.keys(roles).length).toBeGreaterThan(0);
});

it('should check the existance of a real role', async () => {
const exists = await client.roles.exists('admin');
expect(exists).toBeTruthy();
Expand All @@ -72,7 +67,7 @@ only('Integration testing of the roles namespace', () => {
permissions: weaviate.permissions.backup({ collection: 'Some-collection', manage: true }),
expected: {
name: 'backups',
backupsPermissions: [{ collection: 'Some-collection', action: 'manage_backups' }],
backupsPermissions: [{ collection: 'Some-collection', actions: ['manage_backups'] }],
clusterPermissions: [],
collectionsPermissions: [],
dataPermissions: [],
Expand All @@ -86,7 +81,7 @@ only('Integration testing of the roles namespace', () => {
expected: {
name: 'cluster',
backupsPermissions: [],
clusterPermissions: [{ action: 'read_cluster' }],
clusterPermissions: [{ actions: ['read_cluster'] }],
collectionsPermissions: [],
dataPermissions: [],
nodesPermissions: [],
Expand All @@ -107,10 +102,10 @@ only('Integration testing of the roles namespace', () => {
backupsPermissions: [],
clusterPermissions: [],
collectionsPermissions: [
{ collection: 'Some-collection', action: 'create_collections' },
{ collection: 'Some-collection', action: 'read_collections' },
{ collection: 'Some-collection', action: 'update_collections' },
{ collection: 'Some-collection', action: 'delete_collections' },
{
collection: 'Some-collection',
actions: ['create_collections', 'read_collections', 'update_collections', 'delete_collections'],
},
],
dataPermissions: [],
nodesPermissions: [],
Expand All @@ -132,10 +127,10 @@ only('Integration testing of the roles namespace', () => {
clusterPermissions: [],
collectionsPermissions: [],
dataPermissions: [
{ collection: 'Some-collection', action: 'create_data' },
{ collection: 'Some-collection', action: 'read_data' },
{ collection: 'Some-collection', action: 'update_data' },
{ collection: 'Some-collection', action: 'delete_data' },
{
collection: 'Some-collection',
actions: ['create_data', 'read_data', 'update_data', 'delete_data'],
},
],
nodesPermissions: [],
rolesPermissions: [],
Expand All @@ -154,7 +149,9 @@ only('Integration testing of the roles namespace', () => {
clusterPermissions: [],
collectionsPermissions: [],
dataPermissions: [],
nodesPermissions: [{ collection: 'Some-collection', verbosity: 'verbose', action: 'read_nodes' }],
nodesPermissions: [
{ collection: 'Some-collection', verbosity: 'verbose', actions: ['read_nodes'] },
],
rolesPermissions: [],
},
},
Expand All @@ -168,7 +165,7 @@ only('Integration testing of the roles namespace', () => {
collectionsPermissions: [],
dataPermissions: [],
nodesPermissions: [],
rolesPermissions: [{ role: 'some-role', action: 'manage_roles' }],
rolesPermissions: [{ role: 'some-role', actions: ['manage_roles'] }],
},
},
];
Expand All @@ -186,4 +183,10 @@ only('Integration testing of the roles namespace', () => {
await expect(client.roles.byName('backups')).rejects.toThrowError(WeaviateUnexpectedStatusCodeError);
await expect(client.roles.exists('backups')).resolves.toBeFalsy();
});

afterAll(() =>
Promise.all(
['backups', 'cluster', 'collections', 'data', 'nodes', 'roles'].map((n) => client.roles.delete(n))
)
);
});
16 changes: 6 additions & 10 deletions src/roles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,32 @@ export type RolesAction = Extract<Action, 'manage_roles' | 'read_roles'>;

export type BackupsPermission = {
collection: string;
action: BackupsAction;
actions: BackupsAction[];
};

export type ClusterPermission = {
action: ClusterAction;
actions: ClusterAction[];
};

export type CollectionsPermission = {
collection: string;
action: CollectionsAction;
actions: CollectionsAction[];
};

export type DataPermission = {
collection: string;
action: DataAction;
actions: DataAction[];
};

export type NodesPermission = {
collection: string;
verbosity: 'verbose' | 'minimal';
action: NodesAction;
actions: NodesAction[];
};

export type RolesPermission = {
role: string;
action: RolesAction;
actions: RolesAction[];
};

export type Role = {
Expand All @@ -57,10 +57,6 @@ export type Role = {
rolesPermissions: RolesPermission[];
};

export type User = {
name: string;
};

export type Permission =
| BackupsPermission
| ClusterPermission
Expand Down
Loading