Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 into 1.29/colbert-multivector-support
  • Loading branch information
tsmith023 committed Feb 13, 2025
2 parents 2c2964b + 1b67dbf commit 2e4258c
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 173 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@
"*.{ts,js}": [
"npm run format:check",
"npm run lint -- --cache",
"npm run prepack",
"npm run docs"
"npm run prepack"
]
}
}
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
164 changes: 93 additions & 71 deletions src/roles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,86 @@ import {
PermissionsInput,
Role,
RolesPermission,
User,
} from './types.js';
import { Map } from './util.js';

export interface Roles {
/**
* Retrieve all the roles in the system.
*
* @returns {Promise<Record<string, Role>>} A map of role names to their respective roles.
*/
listAll: () => Promise<Record<string, Role>>;
ofCurrentUser: () => Promise<Record<string, Role>>;
/**
* Retrieve a role by its name.
*
* @param {string} roleName The name of the role to retrieve.
* @returns {Promise<Role | null>} The role if it exists, or null if it does not.
*/
byName: (roleName: string) => Promise<Role | null>;
byUser: (user: string) => Promise<Record<string, Role>>;
assignedUsers: (roleName: string) => Promise<Record<string, User>>;
/**
* Retrieve the user IDs assigned to a role.
*
* @param {string} roleName The name of the role to retrieve the assigned user IDs for.
* @returns {Promise<string[]>} The user IDs assigned to the role.
*/
assignedUserIds: (roleName: string) => Promise<string[]>;
/**
* Delete a role by its name.
*
* @param {string} roleName The name of the role to delete.
* @returns {Promise<void>} A promise that resolves when the role is deleted.
*/
delete: (roleName: string) => Promise<void>;
/**
* Create a new role.
*
* @param {string} roleName The name of the new role.
* @param {PermissionsInput} permissions The permissions to assign to the new role.
* @returns {Promise<Role>} The newly created role.
*/
create: (roleName: string, permissions: PermissionsInput) => Promise<Role>;
assignToUser: (roleNames: string | string[], user: string) => Promise<void>;
/**
* Check if a role exists.
*
* @param {string} roleName The name of the role to check for.
* @returns {Promise<boolean>} A promise that resolves to true if the role exists, or false if it does not.
*/
exists: (roleName: string) => Promise<boolean>;
revokeFromUser: (roleNames: string | string[], user: string) => Promise<void>;
/**
* Add permissions to a role.
*
* @param {string} roleName The name of the role to add permissions to.
* @param {PermissionsInput} permissions The permissions to add.
* @returns {Promise<void>} A promise that resolves when the permissions are added.
*/
addPermissions: (roleName: string, permissions: PermissionsInput) => Promise<void>;
/**
* Remove permissions from a role.
*
* @param {string} roleName The name of the role to remove permissions from.
* @param {PermissionsInput} permissions The permissions to remove.
* @returns {Promise<void>} A promise that resolves when the permissions are removed.
*/
removePermissions: (roleName: string, permissions: PermissionsInput) => Promise<void>;
hasPermission: (roleName: string, permission: Permission) => Promise<boolean>;
/**
* Check if a role has the specified permissions.
*
* @param {string} roleName The name of the role to check.
* @param {Permission | Permission[]} permission The permission or permissions to check for.
* @returns {Promise<boolean>} A promise that resolves to true if the role has the permissions, or false if it does not.
*/
hasPermissions: (roleName: string, permission: 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 +103,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 +141,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 +158,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 +173,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))
)
);
});
Loading

0 comments on commit 2e4258c

Please sign in to comment.