Skip to content

Commit df74c29

Browse files
authored
DGS-19519 Enhance error handling for CSFLE configure method (#236)
* Enhance error handling for CSFLE configure method * Minor cleanup * Add test * Add test * Minor fix
1 parent 95801d1 commit df74c29

File tree

7 files changed

+77
-5
lines changed

7 files changed

+77
-5
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemaregistry/rules/encryption/dekregistry/dekregistry-client.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ interface Dek {
4747
}
4848

4949
interface DekClient {
50+
config(): ClientConfig;
5051
registerKek(name: string, kmsType: string, kmsKeyId: string, shared: boolean,
5152
kmsProps?: { [key: string]: string }, doc?: string): Promise<Kek>;
5253
getKek(name: string, deleted: boolean): Promise<Kek>;
@@ -57,13 +58,15 @@ interface DekClient {
5758
}
5859

5960
class DekRegistryClient implements DekClient {
61+
private clientConfig: ClientConfig;
6062
private restService: RestService;
6163
private kekCache: LRUCache<string, Kek>;
6264
private dekCache: LRUCache<string, Dek>;
6365
private kekMutex: Mutex;
6466
private dekMutex: Mutex;
6567

6668
constructor(config: ClientConfig) {
69+
this.clientConfig = config;
6770
const cacheOptions = {
6871
max: config.cacheCapacity !== undefined ? config.cacheCapacity : 1000,
6972
...(config.cacheLatestTtlSecs !== undefined && { ttl: config.cacheLatestTtlSecs * 1000 }),
@@ -82,7 +85,7 @@ class DekRegistryClient implements DekClient {
8285
static newClient(config: ClientConfig): DekClient {
8386
const url = config.baseURLs[0];
8487
if (url.startsWith("mock://")) {
85-
return new MockDekRegistryClient()
88+
return new MockDekRegistryClient(config)
8689
}
8790
return new DekRegistryClient(config)
8891
}
@@ -134,6 +137,10 @@ class DekRegistryClient implements DekClient {
134137
}
135138
}
136139

140+
config(): ClientConfig {
141+
return this.clientConfig;
142+
}
143+
137144
async registerKek(name: string, kmsType: string, kmsKeyId: string, shared: boolean,
138145
kmsProps?: { [key: string]: string }, doc?: string): Promise<Kek> {
139146
const cacheKey = stringify({ name, deleted: false });

schemaregistry/rules/encryption/dekregistry/mock-dekregistry-client.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@ import { DekClient, Dek, Kek } from "./dekregistry-client";
22
import { MOCK_TS } from "./constants";
33
import stringify from "json-stringify-deterministic";
44
import {RestError} from "../../../rest-error";
5+
import {ClientConfig} from "../../../rest-service";
56

67
class MockDekRegistryClient implements DekClient {
8+
private clientConfig?: ClientConfig;
79
private kekCache: Map<string, Kek>;
810
private dekCache: Map<string, Dek>;
911

10-
constructor() {
12+
constructor(config?: ClientConfig) {
13+
this.clientConfig = config
1114
this.kekCache = new Map<string, Kek>();
1215
this.dekCache = new Map<string, Dek>();
1316
}
1417

18+
config(): ClientConfig {
19+
return this.clientConfig!;
20+
}
21+
1522
async registerKek(name: string, kmsType: string, kmsKeyId: string, shared: boolean,
1623
kmsProps?: { [key: string]: string }, doc?: string): Promise<Kek> {
1724
const cacheKey = stringify({ name, deleted: false });

schemaregistry/rules/encryption/encrypt-executor.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {AesSivKey, AesSivKeySchema} from "./tink/proto/aes_siv_pb";
2020
import {create, fromBinary, toBinary} from "@bufbuild/protobuf";
2121
import {fromRawKey as aesGcmFromRawKey} from "./tink/aes_gcm";
2222
import {fromRawKey as aesSivFromRawKey} from "./tink/aes_siv";
23+
import {deepEqual} from "../../serde/json-util";
2324

2425
// EncryptKekName represents a kek name
2526
const ENCRYPT_KEK_NAME = 'encrypt.kek.name'
@@ -83,8 +84,28 @@ export class FieldEncryptionExecutor extends FieldRuleExecutor {
8384
}
8485

8586
override configure(clientConfig: ClientConfig, config: Map<string, string>) {
86-
this.client = DekRegistryClient.newClient(clientConfig)
87-
this.config = config
87+
if (this.client != null) {
88+
if (!deepEqual(this.client.config(), clientConfig)) {
89+
throw new RuleError('executor already configured')
90+
}
91+
} else {
92+
this.client = DekRegistryClient.newClient(clientConfig)
93+
}
94+
95+
if (this.config != null) {
96+
for (let [key, value] of config) {
97+
let v = this.config.get(key)
98+
if (v != null) {
99+
if (v !== value) {
100+
throw new RuleError('rule config key already set: {key}')
101+
}
102+
} else {
103+
this.config.set(key, value)
104+
}
105+
}
106+
} else {
107+
this.config = config
108+
}
88109
}
89110

90111
override type(): string {

schemaregistry/serde/protobuf.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ function getType(fd: DescField): FieldType {
570570
case ScalarType.SFIXED64:
571571
return FieldType.LONG
572572
case ScalarType.FLOAT:
573+
return FieldType.FLOAT
573574
case ScalarType.DOUBLE:
574575
return FieldType.DOUBLE
575576
case ScalarType.BOOL:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import {FieldEncryptionExecutor} from "../../../rules/encryption/encrypt-executor";
3+
import {ClientConfig} from "../../../rest-service";
4+
5+
describe('FieldEncryptionExecutor', () => {
6+
it('configure error', () => {
7+
const executor = new FieldEncryptionExecutor()
8+
const clientConfig: ClientConfig = {
9+
baseURLs: ['mock://'],
10+
cacheCapacity: 1000
11+
}
12+
const config = new Map<string, string>();
13+
config.set('key', 'value');
14+
executor.configure(clientConfig, config);
15+
// configure with same args is fine
16+
executor.configure(clientConfig, config);
17+
const config2 = new Map<string, string>();
18+
config2.set('key2', 'value2');
19+
// configure with additional config keys is fine
20+
executor.configure(clientConfig, config);
21+
22+
const clientConfig2: ClientConfig = {
23+
baseURLs: ['blah://'],
24+
cacheCapacity: 1000
25+
}
26+
expect(() => executor.configure(clientConfig2, config)).toThrowError()
27+
28+
const config3 = new Map<string, string>();
29+
config3.set('key', 'value3');
30+
expect(() => executor.configure(clientConfig, config3)).toThrowError()
31+
})
32+
})

schemaregistry/test/serde/avro.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ describe('AvroSerializer', () => {
865865
expect(obj2.bytesField).toEqual(obj.bytesField);
866866
})
867867
it('basic encryption with dek rotation', async () => {
868+
const fieldEncryptionExecutor = FieldEncryptionExecutor.registerWithClock(new FakeClock());
868869
(fieldEncryptionExecutor.clock as FakeClock).fixedNow = Date.now()
869870
let conf: ClientConfig = {
870871
baseURLs: [baseURL],
@@ -972,6 +973,7 @@ describe('AvroSerializer', () => {
972973
expect(3).toEqual(dek.version);
973974
})
974975
it('basic encryption with preserialized data', async () => {
976+
const fieldEncryptionExecutor = FieldEncryptionExecutor.registerWithClock(new FakeClock());
975977
let conf: ClientConfig = {
976978
baseURLs: [baseURL],
977979
cacheCapacity: 1000
@@ -1024,6 +1026,7 @@ describe('AvroSerializer', () => {
10241026
expect(obj2.f1).toEqual(obj.f1);
10251027
})
10261028
it('deterministic encryption with preserialized data', async () => {
1029+
const fieldEncryptionExecutor = FieldEncryptionExecutor.registerWithClock(new FakeClock());
10271030
let conf: ClientConfig = {
10281031
baseURLs: [baseURL],
10291032
cacheCapacity: 1000
@@ -1077,6 +1080,7 @@ describe('AvroSerializer', () => {
10771080
expect(obj2.f1).toEqual(obj.f1);
10781081
})
10791082
it('dek rotation encryption with preserialized data', async () => {
1083+
const fieldEncryptionExecutor = FieldEncryptionExecutor.registerWithClock(new FakeClock());
10801084
let conf: ClientConfig = {
10811085
baseURLs: [baseURL],
10821086
cacheCapacity: 1000

0 commit comments

Comments
 (0)