Skip to content

Commit 33adac2

Browse files
[DB-19] feat: secretsManager (#265)
* init files * added external secrets manager interfaces * fix: config type * added method * refactor * refactor folder name * fix: interface reusability * fix * added caching service * refactored providerRegistry * fix: types * fix * poc code * added initilization flow * remove unused code * fix * fix: encrypted storage init * fix: error * fix: type * fix: schemas * fix: config read/write * fix: provider reading * added key sanitization * fix: deletion result * fix: orphaned indexes in manifest * use electron-store * fix: initialization * fix: file name * fix: encryption code * cleanup unncessary changes * wrap in trycatch * removed linting changes * fix: new line * [DB-21] added variable fetching flow and awsSecretsManagerProvider (#266) * convert into generic types * fix: types * readded vault types * adds support for listener in secretsManager (#273) * added aws sdk * added refreshSecrets * fix: webpack config * fix * fix * fix: class singleton initialization * fix: SecretReference type * fix: getSecrets * fix: getSecrets * added fallback * fix: cache cleanup * fix: initialization * fix: infinite loop * fix: types * fix: init * fix: eslint rules * revert eslint changes * remove change * added storage listener in secrets manager * remove unsubscribe listener * [DB-29] added IPC methods for secretsManager (#277) * added aws sdk * added refreshSecrets * fix: webpack config * fix * fix * fix: class singleton initialization * fix: SecretReference type * fix: getSecrets * fix: getSecrets * added fallback * fix: cache cleanup * fix: initialization * fix: infinite loop * fix: types * fix: init * fix: eslint rules * revert eslint changes * remove change * added ipc methods * added listProviders * fix: initialization * exposed getSecrets * fix: event name * only return metadata for listing * added providers onCHange listener * added storage listener in secrets manager * remove unsubscribe listener * [DB-28] error handling in secretsManager (#278) * handled secret manager errors * fix: return types * added initialization error handling * fix: test return * [DB-21] added variable fetching flow and awsSecretsManagerProvider (#266) * convert into generic types * fix: types * fix: working types --------- Co-authored-by: Sahil Gupta <sahil865gupta@gmail.com> * remove examples.ts * fix: type * added IPC event to test connection with config * added alias in secretReference type * [DB-48] added persistence to secret values (#306) * [DB-48] added persistence to secret values * handled editing variables using secret IDs * handled errors on secrets fetch * added reset call on initialization * fix: storing userId in the file * fix: handle invalid userId * rmove unused import * fix: setSecrets write --------- Co-authored-by: Sahil Gupta <sahil865gupta@gmail.com> --------- Co-authored-by: Sahil Gupta <sahil865gupta@gmail.com>
1 parent f5d6154 commit 33adac2

18 files changed

Lines changed: 16176 additions & 11243 deletions

package-lock.json

Lines changed: 14190 additions & 11241 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@
272272
"webpack-merge": "^5.8.0"
273273
},
274274
"dependencies": {
275+
"@aws-sdk/client-secrets-manager": "^3.969.0",
275276
"@devicefarmer/adbkit": "^3.2.6",
276277
"@electron/remote": "^2.1.2",
277278
"@requestly/requestly-core": "1.1.1",
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
/**
2+
* Usage Examples for Type-Safe Secrets Manager
3+
*
4+
* This file demonstrates how TypeScript automatically infers types
5+
* throughout the secrets manager system.
6+
*/
7+
8+
import {
9+
SecretProviderType,
10+
AWSSecretProviderConfig,
11+
HashicorpVaultProviderConfig,
12+
AwsSecretReference,
13+
VaultSecretReference,
14+
} from "./types";
15+
import { createProviderInstance } from "./providerService/providerFactory";
16+
import { AWSSecretsManagerProvider } from "./providerService/awsSecretManagerProvider";
17+
// import { HashicorpVaultProvider } from "./providerService/hashicorpVaultProvider";
18+
19+
// ============================================================================
20+
// Example 1: Creating Provider Configurations (Type-Safe)
21+
// ============================================================================
22+
23+
// AWS Provider Config - TypeScript enforces correct config structure
24+
const awsConfig: AWSSecretProviderConfig = {
25+
id: "aws-prod",
26+
type: SecretProviderType.AWS_SECRETS_MANAGER,
27+
name: "AWS Production",
28+
createdAt: Date.now(),
29+
updatedAt: Date.now(),
30+
credentials: {
31+
accessKeyId: "AKIA...",
32+
secretAccessKey: "...",
33+
region: "us-east-1",
34+
sessionToken: "...", // optional
35+
},
36+
};
37+
38+
// HashiCorp Vault Config - TypeScript enforces correct config structure
39+
const vaultConfig: HashicorpVaultProviderConfig = {
40+
id: "vault-dev",
41+
type: SecretProviderType.HASHICORP_VAULT,
42+
name: "Vault Development",
43+
createdAt: Date.now(),
44+
updatedAt: Date.now(),
45+
credentials: {
46+
address: "https://vault.example.com",
47+
token: "s.xyz...",
48+
namespace: "admin", // optional
49+
},
50+
};
51+
52+
// ❌ This will cause a TypeScript error - wrong config type for provider type
53+
// const invalidConfig: AWSSecretProviderConfig = {
54+
// id: "invalid",
55+
// type: SecretProviderType.HASHICORP_VAULT, // ❌ Error: type mismatch
56+
// ...
57+
// };
58+
59+
// ============================================================================
60+
// Example 2: Creating Provider Instances (Type-Safe Factory)
61+
// ============================================================================
62+
63+
async function example2() {
64+
// Generic factory - returns AbstractSecretProvider<SecretProviderType>
65+
const awsProvider = createProviderInstance(awsConfig);
66+
const vaultProvider = createProviderInstance(vaultConfig);
67+
68+
// TypeScript knows the provider type from the instance
69+
console.log(awsProvider.type); // SecretProviderType.AWS_SECRETS_MANAGER
70+
console.log(vaultProvider.type); // SecretProviderType.HASHICORP_VAULT
71+
72+
// Strongly-typed factory - returns specific provider type
73+
const typedAwsProvider = createTypedProviderInstance(awsConfig);
74+
// typedAwsProvider is AbstractSecretProvider<SecretProviderType.AWS_SECRETS_MANAGER>
75+
76+
const typedVaultProvider = createTypedProviderInstance(vaultConfig);
77+
// typedVaultProvider is AbstractSecretProvider<SecretProviderType.HASHICORP_VAULT>
78+
}
79+
80+
// ============================================================================
81+
// Example 3: Working with Secret References (Type-Safe)
82+
// ============================================================================
83+
84+
async function example3() {
85+
const awsProvider = new AWSSecretsManagerProvider(awsConfig);
86+
const vaultProvider = new HashicorpVaultProvider(vaultConfig);
87+
88+
// AWS Secret Reference - TypeScript enforces correct structure
89+
const awsRef: AwsSecretReference = {
90+
type: SecretProviderType.AWS_SECRETS_MANAGER,
91+
identifier: "arn:aws:secretsmanager:us-east-1:123456789:secret:myapp/config",
92+
version: "AWSCURRENT", // optional
93+
};
94+
95+
// Vault Secret Reference - TypeScript enforces correct structure
96+
const vaultRef: VaultSecretReference = {
97+
type: SecretProviderType.HASHICORP_VAULT,
98+
path: "secret/data/myapp/config",
99+
version: 2, // optional - KV v2 version number
100+
};
101+
102+
// TypeScript ensures you pass the correct reference type to each provider
103+
const awsSecret = await awsProvider.getSecret(awsRef); // ✅ Correct
104+
const vaultSecret = await vaultProvider.getSecret(vaultRef); // ✅ Correct
105+
106+
// ❌ These would cause TypeScript errors:
107+
// await awsProvider.getSecret(vaultRef); // ❌ Error: wrong reference type
108+
// await vaultProvider.getSecret(awsRef); // ❌ Error: wrong reference type
109+
110+
// TypeScript knows the exact return types
111+
if (awsSecret) {
112+
console.log(awsSecret.ARN); // ✅ ARN exists on AwsSecretValue
113+
console.log(awsSecret.value); // ✅ value is string | undefined
114+
// console.log(awsSecret.data); // ❌ Error: data doesn't exist on AwsSecretValue
115+
}
116+
117+
if (vaultSecret) {
118+
console.log(vaultSecret.data); // ✅ data exists on VaultSecretValue
119+
console.log(vaultSecret.metadata?.version); // ✅ metadata is optional
120+
// console.log(vaultSecret.ARN); // ❌ Error: ARN doesn't exist on VaultSecretValue
121+
}
122+
}
123+
124+
// ============================================================================
125+
// Example 4: Batch Operations (Type-Safe)
126+
// ============================================================================
127+
128+
async function example4() {
129+
const awsProvider = new AWSSecretsManagerProvider(awsConfig);
130+
const vaultProvider = new HashicorpVaultProvider(vaultConfig);
131+
132+
// Get multiple secrets - types are preserved
133+
const awsRefs: AwsSecretReference[] = [
134+
{
135+
type: SecretProviderType.AWS_SECRETS_MANAGER,
136+
identifier: "secret-1",
137+
},
138+
{
139+
type: SecretProviderType.AWS_SECRETS_MANAGER,
140+
identifier: "secret-2",
141+
},
142+
];
143+
144+
const vaultRefs: VaultSecretReference[] = [
145+
{
146+
type: SecretProviderType.HASHICORP_VAULT,
147+
path: "secret/data/app1",
148+
},
149+
{
150+
type: SecretProviderType.HASHICORP_VAULT,
151+
path: "secret/data/app2",
152+
},
153+
];
154+
155+
const awsSecrets = await awsProvider.getSecrets(awsRefs);
156+
// TypeScript knows: awsSecrets is (AwsSecretValue | null)[]
157+
158+
const vaultSecrets = await vaultProvider.getSecrets(vaultRefs);
159+
// TypeScript knows: vaultSecrets is (VaultSecretValue | null)[]
160+
161+
// Type-safe iteration
162+
awsSecrets.forEach((secret) => {
163+
if (secret) {
164+
console.log(secret.ARN); // ✅ ARN exists
165+
console.log(secret.versionId); // ✅ versionId exists
166+
}
167+
});
168+
169+
vaultSecrets.forEach((secret) => {
170+
if (secret) {
171+
console.log(secret.path); // ✅ path exists
172+
console.log(secret.data); // ✅ data exists
173+
}
174+
});
175+
}
176+
177+
// ============================================================================
178+
// Example 5: Setting Secrets (Type-Safe)
179+
// ============================================================================
180+
181+
async function example5() {
182+
const awsProvider = new AWSSecretsManagerProvider(awsConfig);
183+
const vaultProvider = new HashicorpVaultProvider(vaultConfig);
184+
185+
const awsRef: AwsSecretReference = {
186+
type: SecretProviderType.AWS_SECRETS_MANAGER,
187+
identifier: "my-secret",
188+
};
189+
190+
const vaultRef: VaultSecretReference = {
191+
type: SecretProviderType.HASHICORP_VAULT,
192+
path: "secret/data/myapp/config",
193+
};
194+
195+
// Set a single secret - both string and object values are supported
196+
await awsProvider.setSecret(awsRef, "my-secret-value");
197+
await vaultProvider.setSecret(vaultRef, {
198+
database: "postgres://...",
199+
apiKey: "xyz...",
200+
});
201+
202+
// Batch set
203+
await awsProvider.setSecrets([
204+
{ ref: awsRef, value: "value1" },
205+
]);
206+
207+
await vaultProvider.setSecrets([
208+
{
209+
ref: { type: SecretProviderType.HASHICORP_VAULT, path: "secret/data/app1" },
210+
value: { key1: "value1" },
211+
},
212+
{
213+
ref: { type: SecretProviderType.HASHICORP_VAULT, path: "secret/data/app2" },
214+
value: { key2: "value2" },
215+
},
216+
]);
217+
}
218+
219+
// ============================================================================
220+
// Example 6: Using the Registry (Type-Safe)
221+
// ============================================================================
222+
223+
async function example6() {
224+
// Assume we have a registry instance
225+
const registry: any = null; // FileBasedProviderRegistry instance
226+
227+
// Get a provider without knowing its type
228+
const provider = registry.getProvider("aws-prod");
229+
if (provider) {
230+
// provider is AbstractSecretProvider<SecretProviderType>
231+
console.log(provider.type);
232+
}
233+
234+
// Get a provider with a specific type (type-safe)
235+
const awsProvider = registry.getTypedProvider(
236+
"aws-prod",
237+
SecretProviderType.AWS_SECRETS_MANAGER
238+
);
239+
240+
if (awsProvider) {
241+
// TypeScript knows: awsProvider is AbstractSecretProvider<SecretProviderType.AWS_SECRETS_MANAGER>
242+
const ref: AwsSecretReference = {
243+
type: SecretProviderType.AWS_SECRETS_MANAGER,
244+
identifier: "my-secret",
245+
};
246+
247+
const secret = await awsProvider.getSecret(ref);
248+
// TypeScript knows: secret is AwsSecretValue | null
249+
250+
if (secret) {
251+
console.log(secret.ARN); // ✅ Type-safe access to AWS-specific fields
252+
}
253+
}
254+
}
255+
256+
// ============================================================================
257+
// Example 7: Type Guards and Runtime Checks
258+
// ============================================================================
259+
260+
async function example7() {
261+
const registry: any = null; // FileBasedProviderRegistry instance
262+
263+
// Get a provider and use type guards
264+
const provider = registry.getProvider("some-provider-id");
265+
266+
if (provider) {
267+
// Runtime check with type narrowing
268+
if (provider.type === SecretProviderType.AWS_SECRETS_MANAGER) {
269+
// TypeScript narrows the type here
270+
const awsProvider = provider as AWSSecretsManagerProvider;
271+
// Now you have full access to AWS-specific methods if any
272+
} else if (provider.type === SecretProviderType.HASHICORP_VAULT) {
273+
const vaultProvider = provider as HashicorpVaultProvider;
274+
// Now you have full access to Vault-specific methods if any
275+
}
276+
}
277+
}
278+
279+
// ============================================================================
280+
// Example 8: Adding a New Provider (Easy Extension)
281+
// ============================================================================
282+
283+
/**
284+
* To add a new provider (e.g., Azure Key Vault):
285+
*
286+
* 1. Add the provider type to the enum in types.ts:
287+
* ```
288+
* export enum SecretProviderType {
289+
* AWS_SECRETS_MANAGER = "aws",
290+
* HASHICORP_VAULT = "vault",
291+
* AZURE_KEY_VAULT = "azure", // ← Add this
292+
* }
293+
* ```
294+
*
295+
* 2. Add the config interface in types.ts:
296+
* ```
297+
* export interface AzureKeyVaultConfig {
298+
* vaultUrl: string;
299+
* tenantId: string;
300+
* clientId: string;
301+
* clientSecret: string;
302+
* }
303+
* ```
304+
*
305+
* 3. Add to the discriminated unions in types.ts:
306+
* ```
307+
* export type AzureKeyVaultProviderConfig = ProviderConfig<
308+
* SecretProviderType.AZURE_KEY_VAULT,
309+
* AzureKeyVaultConfig
310+
* >;
311+
*
312+
* export type SecretProviderConfig =
313+
* | AWSSecretProviderConfig
314+
* | HashicorpVaultProviderConfig
315+
* | AzureKeyVaultProviderConfig; // ← Add this
316+
* ```
317+
*
318+
* 4. Add reference and value types in types.ts:
319+
* ```
320+
* export interface AzureSecretReference extends BaseSecretReference<...> {
321+
* name: string;
322+
* version?: string;
323+
* }
324+
*
325+
* export interface AzureSecretValue extends BaseSecretValue<...> {
326+
* value: string;
327+
* id: string;
328+
* // ... other Azure-specific fields
329+
* }
330+
* ```
331+
*
332+
* 5. Add to the type map in types.ts:
333+
* ```
334+
* export interface ProviderTypeMap {
335+
* [SecretProviderType.AWS_SECRETS_MANAGER]: { ... };
336+
* [SecretProviderType.HASHICORP_VAULT]: { ... };
337+
* [SecretProviderType.AZURE_KEY_VAULT]: { // ← Add this
338+
* config: AzureKeyVaultConfig;
339+
* providerConfig: AzureKeyVaultProviderConfig;
340+
* reference: AzureSecretReference;
341+
* value: AzureSecretValue;
342+
* };
343+
* }
344+
* ```
345+
*
346+
* 6. Create the provider class (azureKeyVaultProvider.ts):
347+
* ```
348+
* export class AzureKeyVaultProvider extends AbstractSecretProvider<
349+
* SecretProviderType.AZURE_KEY_VAULT
350+
* > {
351+
* readonly type = SecretProviderType.AZURE_KEY_VAULT as const;
352+
* // ... implement abstract methods
353+
* }
354+
* ```
355+
*
356+
* 7. Add to the factory in providerFactory.ts:
357+
* ```
358+
* case SecretProviderType.AZURE_KEY_VAULT:
359+
* return new AzureKeyVaultProvider(config as AzureKeyVaultProviderConfig);
360+
* ```
361+
*
362+
* That's it! TypeScript will now enforce type safety for your new provider
363+
* throughout the entire system.
364+
*/
365+
366+
export {};

0 commit comments

Comments
 (0)