Skip to content

Commit 7fbfebe

Browse files
authored
Merge pull request #240 from weaviate/add-support-for-rbac
Add support for RBAC with tests, update CI images
2 parents edb27cb + bf05f68 commit 7fbfebe

File tree

19 files changed

+1439
-176
lines changed

19 files changed

+1439
-176
lines changed

.github/workflows/main.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ on:
88

99
env:
1010
WEAVIATE_124: 1.24.26
11-
WEAVIATE_125: 1.25.25
12-
WEAVIATE_126: 1.26.10
13-
WEAVIATE_127: 1.27.3
11+
WEAVIATE_125: 1.25.28
12+
WEAVIATE_126: 1.26.13
13+
WEAVIATE_127: 1.27.8
14+
WEAVIATE_128: 1.28.1-77a2178
1415

1516
jobs:
1617
checks:
@@ -37,9 +38,10 @@ jobs:
3738
{ node: "22.x", weaviate: $WEAVIATE_124},
3839
{ node: "22.x", weaviate: $WEAVIATE_125},
3940
{ node: "22.x", weaviate: $WEAVIATE_126},
40-
{ node: "18.x", weaviate: $WEAVIATE_127},
41-
{ node: "20.x", weaviate: $WEAVIATE_127},
42-
{ node: "22.x", weaviate: $WEAVIATE_127}
41+
{ node: "22.x", weaviate: $WEAVIATE_127},
42+
{ node: "18.x", weaviate: $WEAVIATE_128},
43+
{ node: "20.x", weaviate: $WEAVIATE_128},
44+
{ node: "22.x", weaviate: $WEAVIATE_128}
4345
]
4446
steps:
4547
- uses: actions/checkout@v3

ci/docker-compose-proxy.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,6 @@ services:
2626
AUTOSCHEMA_ENABLED: 'false'
2727
DISABLE_TELEMETRY: 'true'
2828
GRPC_PORT: 8021
29-
contextionary:
30-
environment:
31-
OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75
32-
EXTENSIONS_STORAGE_MODE: weaviate
33-
EXTENSIONS_STORAGE_ORIGIN: http://weaviate-proxy:8020
34-
NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5
35-
ENABLE_COMPOUND_SPLITTING: 'false'
36-
image: semitechnologies/contextionary:en0.16.0-v1.2.0
37-
ports:
38-
- 9999:9999
3929
proxy-http:
4030
image: envoyproxy/envoy:v1.29-latest
4131
command: envoy --config-path /etc/envoy/http.yaml

src/collections/config/types/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export type MultiTenancyConfig = {
4141
enabled: boolean;
4242
};
4343

44-
export type ReplicationDeletionStrategy = 'DeleteOnConflict' | 'NoAutomatedResolution';
44+
export type ReplicationDeletionStrategy =
45+
| 'DeleteOnConflict'
46+
| 'NoAutomatedResolution'
47+
| 'TimeBasedResolution';
4548

4649
export type ReplicationConfig = {
4750
asyncEnabled: boolean;

src/collections/integration.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,9 +582,7 @@ describe('Testing of the collections.create method', () => {
582582

583583
expect(response.replication.asyncEnabled).toEqual(false);
584584
expect(response.replication.deletionStrategy).toEqual<ReplicationDeletionStrategy>(
585-
(await cluster.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0)))
586-
? 'NoAutomatedResolution'
587-
: 'DeleteOnConflict'
585+
'NoAutomatedResolution'
588586
);
589587
expect(response.replication.factor).toEqual(2);
590588

src/connection/helpers.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,15 @@ export function connectToCustom(
157157
}
158158

159159
function addWeaviateEmbeddingServiceHeaders(clusterURL: string, options?: ConnectToWeaviateCloudOptions) {
160-
if (!isApiKey(options?.authCredentials)) {
160+
const creds = options?.authCredentials;
161+
162+
if (!isApiKey(creds)) {
161163
return options?.headers;
162164
}
163165

164166
return {
165-
...options.headers,
166-
'X-Weaviate-Api-Key': mapApiKey(options.authCredentials).apiKey,
167+
...options?.headers,
168+
'X-Weaviate-Api-Key': mapApiKey(creds).apiKey,
167169
'X-Weaviate-Cluster-Url': clusterURL,
168170
};
169171
}

src/connection/http.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { Agent } from 'http';
44
import OpenidConfigurationGetter from '../misc/openidConfigurationGetter.js';
55

66
import {
7+
WeaviateInsufficientPermissionsError,
78
WeaviateInvalidInputError,
89
WeaviateRequestTimeoutError,
10+
WeaviateUnauthenticatedError,
911
WeaviateUnexpectedStatusCodeError,
1012
} from '../errors.js';
1113
import {
@@ -155,11 +157,11 @@ export default class ConnectionREST {
155157
return this.http.head(path, payload);
156158
};
157159

158-
get = (path: string, expectReturnContent = true) => {
160+
get = <T>(path: string, expectReturnContent = true) => {
159161
if (this.authEnabled) {
160-
return this.login().then((token) => this.http.get(path, expectReturnContent, token));
162+
return this.login().then((token) => this.http.get<T>(path, expectReturnContent, token));
161163
}
162-
return this.http.get(path, expectReturnContent);
164+
return this.http.get<T>(path, expectReturnContent);
163165
};
164166

165167
login = async () => {
@@ -197,7 +199,7 @@ export interface HttpClient {
197199
expectReturnContent: boolean,
198200
bearerToken: string
199201
) => Promise<T | undefined>;
200-
get: (path: string, expectReturnContent?: boolean, bearerToken?: string) => any;
202+
get: <T>(path: string, expectReturnContent?: boolean, bearerToken?: string) => Promise<T>;
201203
externalPost: (externalUrl: string, body: any, contentType: any) => any;
202204
getRaw: (path: string, bearerToken?: string) => any;
203205
delete: (path: string, payload: any, expectReturnContent?: boolean, bearerToken?: string) => any;
@@ -313,7 +315,7 @@ export const httpClient = (config: InternalConnectionParams): HttpClient => {
313315
handleHeadResponse<undefined>(false)
314316
);
315317
},
316-
get: <T>(path: string, expectReturnContent = true, bearerToken = ''): Promise<T | undefined> => {
318+
get: <T>(path: string, expectReturnContent = true, bearerToken = ''): Promise<T> => {
317319
const request = {
318320
method: 'GET',
319321
headers: {
@@ -323,7 +325,7 @@ export const httpClient = (config: InternalConnectionParams): HttpClient => {
323325
};
324326
addAuthHeaderIfNeeded(request, bearerToken);
325327
return fetchWithTimeout(url(path), config.timeout?.query || 30, request).then(
326-
checkStatus<T>(expectReturnContent)
328+
checkStatus<any>(expectReturnContent)
327329
);
328330
},
329331
getRaw: (path: string, bearerToken = '') => {
@@ -380,7 +382,13 @@ const checkStatus =
380382
} catch (e) {
381383
err = errText;
382384
}
383-
return Promise.reject(new WeaviateUnexpectedStatusCodeError(res.status, err));
385+
if (res.status === 401) {
386+
return Promise.reject(new WeaviateUnauthenticatedError(err));
387+
} else if (res.status === 403) {
388+
return Promise.reject(new WeaviateInsufficientPermissionsError(403, err));
389+
} else {
390+
return Promise.reject(new WeaviateUnexpectedStatusCodeError(res.status, err));
391+
}
384392
});
385393
}
386394
if (expectResponseBody) {

src/errors.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,20 @@ export class WeaviateRequestTimeoutError extends WeaviateError {
148148
super(`Weaviate request timed out with message: ${message}`);
149149
}
150150
}
151+
152+
/**
153+
* Is thrown if a request to Weaviate fails with a forbidden status code due to insufficient permissions.
154+
*/
155+
export class WeaviateInsufficientPermissionsError extends WeaviateError {
156+
public code: number;
157+
constructor(code: number, message: string) {
158+
super(`Forbidden: ${message}`);
159+
this.code = code;
160+
}
161+
}
162+
163+
export class WeaviateUnauthenticatedError extends WeaviateError {
164+
constructor(message: string) {
165+
super(`Unauthenticated: ${message}`);
166+
}
167+
}

src/grpc/batcher.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import { Metadata } from 'nice-grpc';
1+
import { Metadata, ServerError, Status } from 'nice-grpc';
22

33
import { ConsistencyLevel } from '../data/index.js';
44

55
import { BatchObject, BatchObjectsReply, BatchObjectsRequest } from '../proto/v1/batch.js';
66
import { WeaviateClient } from '../proto/v1/weaviate.js';
77

88
import { RetryOptions } from 'nice-grpc-client-middleware-retry';
9-
import { WeaviateBatchError, WeaviateDeleteManyError } from '../errors.js';
9+
import {
10+
WeaviateBatchError,
11+
WeaviateDeleteManyError,
12+
WeaviateInsufficientPermissionsError,
13+
} from '../errors.js';
1014
import { Filters } from '../proto/v1/base.js';
1115
import { BatchDeleteReply, BatchDeleteRequest } from '../proto/v1/batch_delete.js';
1216
import Base from './base.js';
@@ -58,6 +62,9 @@ export default class Batcher extends Base implements Batch {
5862
}
5963
)
6064
).catch((err) => {
65+
if (err instanceof ServerError && err.code === Status.PERMISSION_DENIED) {
66+
throw new WeaviateInsufficientPermissionsError(7, err.message);
67+
}
6168
throw new WeaviateDeleteManyError(err.message);
6269
});
6370
}
@@ -77,6 +84,9 @@ export default class Batcher extends Base implements Batch {
7784
}
7885
)
7986
.catch((err) => {
87+
if (err instanceof ServerError && err.code === Status.PERMISSION_DENIED) {
88+
throw new WeaviateInsufficientPermissionsError(7, err.message);
89+
}
8090
throw new WeaviateBatchError(err.message);
8191
})
8292
);

src/grpc/searcher.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ConsistencyLevel } from '../data/index.js';
22

3-
import { Metadata } from 'nice-grpc';
3+
import { Metadata, ServerError, Status } from 'nice-grpc';
44
import { Filters } from '../proto/v1/base.js';
55
import {
66
BM25,
@@ -9,8 +9,8 @@ import {
99
MetadataRequest,
1010
NearAudioSearch,
1111
NearDepthSearch,
12-
NearImageSearch,
1312
NearIMUSearch,
13+
NearImageSearch,
1414
NearObject,
1515
NearTextSearch,
1616
NearThermalSearch,
@@ -25,7 +25,7 @@ import {
2525
import { WeaviateClient } from '../proto/v1/weaviate.js';
2626

2727
import { RetryOptions } from 'nice-grpc-client-middleware-retry';
28-
import { WeaviateQueryError } from '../errors.js';
28+
import { WeaviateInsufficientPermissionsError, WeaviateQueryError } from '../errors.js';
2929
import { GenerativeSearch } from '../proto/v1/generative.js';
3030
import Base from './base.js';
3131
import { retryOptions } from './retry.js';
@@ -157,6 +157,9 @@ export default class Searcher extends Base implements Search {
157157
}
158158
)
159159
.catch((err) => {
160+
if (err instanceof ServerError && err.code === Status.PERMISSION_DENIED) {
161+
throw new WeaviateInsufficientPermissionsError(7, err.message);
162+
}
160163
throw new WeaviateQueryError(err.message, 'gRPC');
161164
})
162165
);

src/grpc/tenantsManager.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Metadata } from 'nice-grpc';
1+
import { Metadata, ServerError, Status } from 'nice-grpc';
22
import { RetryOptions } from 'nice-grpc-client-middleware-retry';
3+
import { WeaviateDeleteManyError, WeaviateInsufficientPermissionsError } from '../errors.js';
34
import { TenantsGetReply, TenantsGetRequest } from '../proto/v1/tenants.js';
45
import { WeaviateClient } from '../proto/v1/weaviate.js';
56
import Base from './base.js';
@@ -28,17 +29,24 @@ export default class TenantsManager extends Base implements Tenants {
2829

2930
private call(message: TenantsGetRequest) {
3031
return this.sendWithTimeout((signal: AbortSignal) =>
31-
this.connection.tenantsGet(
32-
{
33-
...message,
34-
collection: this.collection,
35-
},
36-
{
37-
metadata: this.metadata,
38-
signal,
39-
...retryOptions,
40-
}
41-
)
32+
this.connection
33+
.tenantsGet(
34+
{
35+
...message,
36+
collection: this.collection,
37+
},
38+
{
39+
metadata: this.metadata,
40+
signal,
41+
...retryOptions,
42+
}
43+
)
44+
.catch((err) => {
45+
if (err instanceof ServerError && err.code === Status.PERMISSION_DENIED) {
46+
throw new WeaviateInsufficientPermissionsError(7, err.message);
47+
}
48+
throw new WeaviateDeleteManyError(err.message);
49+
})
4250
);
4351
}
4452
}

0 commit comments

Comments
 (0)