Skip to content

Commit 0531fbc

Browse files
committed
First implementation of the secrets manager
1 parent 31c857a commit 0531fbc

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

src/manager.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export class SecretsManager implements ISecretsManager {
1414
return this._connector.fetch(id);
1515
}
1616

17-
async set(secret: ISecret): Promise<void> {
18-
this._connector.save(secret.id, secret);
17+
async set(id: string, secret: ISecret): Promise<void> {
18+
this._connector.save(id, secret);
1919
}
2020

2121
async remove(id: string): Promise<void> {
@@ -26,5 +26,64 @@ export class SecretsManager implements ISecretsManager {
2626
return (await this._connector.list(namespace)).ids;
2727
}
2828

29+
private _onchange = (e: Event): void => {
30+
const target = e.target as HTMLInputElement;
31+
const attachedId = target.dataset.secretsId;
32+
if (attachedId) {
33+
const splitId = attachedId.split(':');
34+
const namespace = splitId.shift();
35+
const id = splitId.join(':');
36+
if (namespace && id) {
37+
this.set(attachedId, { namespace, id, value: target.value });
38+
}
39+
}
40+
};
41+
42+
async attach(
43+
namespace: string,
44+
id: string,
45+
input: HTMLInputElement
46+
): Promise<void> {
47+
const attachedId = `${namespace}:${id}`;
48+
const attachedInput = this._attachedInputs.get(attachedId);
49+
50+
// Do not attach the input if it is already attached.
51+
if (attachedInput === input) {
52+
return;
53+
}
54+
55+
// Detach the previous input.
56+
if (attachedInput) {
57+
this.detach(namespace, id);
58+
}
59+
this._attachedInputs.set(attachedId, input);
60+
61+
// Fill the password if the input is empty and a value is fetched by the data
62+
// connector.
63+
input.dataset.secretsId = attachedId;
64+
const value = await this.get(attachedId);
65+
if (!input.value && value) {
66+
input.value = value.value;
67+
}
68+
input.addEventListener('change', this._onchange);
69+
}
70+
71+
detach(namespace: string, id: string): void {
72+
const attachedId = `${namespace}:${id}`;
73+
const input = this._attachedInputs.get(attachedId);
74+
if (input) {
75+
input.removeEventListener('change', this._onchange);
76+
}
77+
this._attachedInputs.delete(attachedId);
78+
}
79+
80+
async detachAll(namespace: string): Promise<void> {
81+
const attachedIds = await this.list(namespace);
82+
attachedIds.forEach(id => {
83+
this.detach(namespace, id);
84+
});
85+
}
86+
2987
private _connector: ISecretsConnector;
88+
private _attachedInputs = new Map<string, HTMLInputElement>();
3089
}

src/token.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { IDataConnector } from '@jupyterlab/statedb';
22
import { Token } from '@lumino/coreutils';
33

44
export interface ISecret {
5+
namespace: string;
56
id: string;
67
value: string;
7-
namespace: string;
88
}
99

1010
export interface ISecretsConnector extends IDataConnector<ISecret> {}
@@ -16,9 +16,12 @@ export const ISecretsConnector = new Token<ISecretsConnector>(
1616

1717
export interface ISecretsManager {
1818
get(id: string): Promise<ISecret | undefined>;
19-
set(secret: ISecret): Promise<void>;
19+
set(id: string, secret: ISecret): Promise<void>;
2020
remove(id: string): Promise<void>;
2121
list(namespace: string): Promise<string[]>;
22+
attach(namespace: string, id: string, input: HTMLInputElement): void;
23+
detach(namespace: string, id: string): void;
24+
detachAll(namespace: string): void;
2225
}
2326

2427
export const ISecretsManager = new Token<ISecretsManager>(

0 commit comments

Comments
 (0)