Skip to content

Commit a3fda1c

Browse files
authored
Upgrade secrets manager (#75)
* Use the field visibility property from the secrets manager * Remove the settings to use the secret manager if not provided
1 parent b5041f5 commit a3fda1c

File tree

5 files changed

+73
-53
lines changed

5 files changed

+73
-53
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"@rjsf/utils": "^5.18.4",
8484
"@rjsf/validator-ajv8": "^5.18.4",
8585
"json5": "^2.2.3",
86-
"jupyter-secrets-manager": "^0.3.0",
86+
"jupyter-secrets-manager": "^0.4.0",
8787
"react": "^18.2.0",
8888
"react-dom": "^18.2.0"
8989
},

schema/provider-registry.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111
"description": "Whether to use or not the secrets manager. If not, secrets will be stored in the browser (local storage)",
1212
"default": true
1313
},
14-
"HideSecretFields": {
15-
"type": "boolean",
16-
"title": "Hide secret fields",
17-
"description": "Whether to hide the secret fields in the UI or not",
18-
"default": true
19-
},
2014
"AIprovider": {
2115
"type": "object",
2216
"title": "AI provider",

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
191191
settingRegistry
192192
.load(providerRegistryPlugin.id)
193193
.then(settings => {
194+
if (!secretsManager) {
195+
delete settings.schema.properties?.['UseSecretsManager'];
196+
}
194197
const updateProvider = () => {
195198
// Update the settings to the AI providers.
196199
const providerSettings = (settings.get('AIprovider').composite ?? {

src/settings/panel.tsx

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ export class AiSettings extends React.Component<
7272
this._settingConnector = props.formContext.settingConnector ?? null;
7373
this._settings = props.formContext.settings;
7474

75-
this._useSecretsManager =
75+
const useSecretsManagerSetting =
7676
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
77-
this._hideSecretFields =
78-
(this._settings.get('HideSecretFields').composite as boolean) ?? true;
77+
this._useSecretsManager =
78+
useSecretsManagerSetting && this._secretsManager !== null;
7979

8080
// Initialize the providers schema.
8181
const providerSchema = JSONExt.deepCopy(baseSettings) as any;
@@ -124,19 +124,11 @@ export class AiSettings extends React.Component<
124124
// Update the setting registry.
125125
this.saveSettingsToRegistry();
126126

127-
this._settings.changed.connect(() => {
128-
const useSecretsManager =
129-
(this._settings.get('UseSecretsManager').composite as boolean) ?? true;
130-
if (useSecretsManager !== this._useSecretsManager) {
131-
this._updateUseSecretsManager(useSecretsManager);
132-
}
133-
const hideSecretFields =
134-
(this._settings.get('HideSecretFields').composite as boolean) ?? true;
135-
if (hideSecretFields !== this._hideSecretFields) {
136-
this._hideSecretFields = hideSecretFields;
137-
this._updateSchema();
138-
}
139-
});
127+
this._secretsManager?.fieldVisibilityChanged.connect(
128+
this._fieldVisibilityChanged
129+
);
130+
131+
this._settings.changed.connect(this._settingsChanged);
140132
}
141133

142134
async componentDidUpdate(): Promise<void> {
@@ -165,6 +157,10 @@ export class AiSettings extends React.Component<
165157
}
166158

167159
componentWillUnmount(): void {
160+
this._settings.changed.disconnect(this._settingsChanged);
161+
this._secretsManager?.fieldVisibilityChanged.disconnect(
162+
this._fieldVisibilityChanged
163+
);
168164
if (!this._secretsManager || !this._useSecretsManager) {
169165
return;
170166
}
@@ -199,11 +195,11 @@ export class AiSettings extends React.Component<
199195
/**
200196
* Save settings in local storage for a given provider.
201197
*/
202-
saveSettingsToLocalStorage(value: IDict<any>) {
203-
const currentSettings = { ...value };
198+
saveSettingsToLocalStorage() {
199+
const currentSettings = { ...this._currentSettings };
204200
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) ?? '{}');
205201
// Do not save secrets in local storage if using the secrets manager.
206-
if (this._secretsManager && this._useSecretsManager) {
202+
if (this._useSecretsManager) {
207203
this._secretFields.forEach(field => delete currentSettings[field]);
208204
}
209205
settings[this._provider] = currentSettings;
@@ -219,21 +215,44 @@ export class AiSettings extends React.Component<
219215
.catch(console.error);
220216
}
221217

218+
/**
219+
* Triggered when the settings has changed.
220+
*/
221+
private _settingsChanged = (settings: ISettingRegistry.ISettings) => {
222+
this._updateUseSecretsManager(
223+
(this._settings.get('UseSecretsManager').composite as boolean) ?? true
224+
);
225+
};
226+
227+
/**
228+
* Triggered when the secret fields visibility has changed.
229+
*/
230+
private _fieldVisibilityChanged = (
231+
_: ISecretsManager,
232+
value: boolean
233+
): void => {
234+
if (this._useSecretsManager) {
235+
this._updateSchema();
236+
}
237+
};
238+
222239
/**
223240
* Update the settings whether the secrets manager is used or not.
224241
*
225242
* @param value - whether to use the secrets manager or not.
226243
*/
227244
private _updateUseSecretsManager = (value: boolean) => {
245+
// No-op if the value did not change or the secrets manager has not been provided.
246+
if (value === this._useSecretsManager || this._secretsManager === null) {
247+
return;
248+
}
249+
250+
// Update the secrets manager.
228251
this._useSecretsManager = value;
229252
if (!value) {
230253
// Detach all the password inputs attached to the secrets manager, and save the
231254
// current settings to the local storage to save the password.
232-
this._secretsManager?.detachAll(Private.getToken(), SECRETS_NAMESPACE);
233-
if (this._settingConnector instanceof SettingConnector) {
234-
this._settingConnector.doNotSave = [];
235-
}
236-
this.saveSettingsToLocalStorage(this._currentSettings);
255+
this._secretsManager.detachAll(Private.getToken(), SECRETS_NAMESPACE);
237256
} else {
238257
// Remove all the keys stored locally.
239258
const settings = JSON.parse(localStorage.getItem(STORAGE_NAME) || '{}');
@@ -245,13 +264,9 @@ export class AiSettings extends React.Component<
245264
});
246265
});
247266
localStorage.setItem(STORAGE_NAME, JSON.stringify(settings));
248-
// Update the fields not to save in settings.
249-
if (this._settingConnector instanceof SettingConnector) {
250-
this._settingConnector.doNotSave = this._secretFields;
251-
}
252-
// Attach the password inputs to the secrets manager.
253-
this.componentDidUpdate();
254267
}
268+
this._updateSchema();
269+
this.saveSettingsToLocalStorage();
255270
this.saveSettingsToRegistry();
256271
};
257272

@@ -271,9 +286,16 @@ export class AiSettings extends React.Component<
271286
Object.entries(settingsSchema).forEach(([key, value]) => {
272287
if (key.toLowerCase().includes('key')) {
273288
this._secretFields.push(key);
274-
if (this._hideSecretFields) {
289+
290+
// If the secrets manager is not used, do not show the secrets fields.
291+
// If the secrets manager is used, check if the fields should be visible.
292+
const showSecretFields =
293+
!this._useSecretsManager ||
294+
(this._secretsManager?.secretFieldsVisibility ?? true);
295+
if (!showSecretFields) {
275296
return;
276297
}
298+
277299
this._uiSchema[key] = { 'ui:widget': 'password' };
278300
}
279301
schema.properties[key] = value;
@@ -284,12 +306,12 @@ export class AiSettings extends React.Component<
284306
}
285307

286308
// Do not save secrets in settings if using the secrets manager.
287-
if (
288-
this._secretsManager &&
289-
this._useSecretsManager &&
290-
this._settingConnector instanceof SettingConnector
291-
) {
292-
this._settingConnector.doNotSave = this._secretFields;
309+
if (this._settingConnector instanceof SettingConnector) {
310+
if (this._useSecretsManager) {
311+
this._settingConnector.doNotSave = this._secretFields;
312+
} else {
313+
this._settingConnector.doNotSave = [];
314+
}
293315
}
294316
return schema as JSONSchema7;
295317
}
@@ -389,7 +411,7 @@ export class AiSettings extends React.Component<
389411
private _onFormChanged = (e: IChangeEvent): void => {
390412
const { formData } = e;
391413
const isModified = this._updatedFormData(formData);
392-
this.saveSettingsToLocalStorage(this._currentSettings);
414+
this.saveSettingsToLocalStorage();
393415
this.saveSettingsToRegistry();
394416
if (isModified !== this.state.isModified) {
395417
this.setState({ isModified });
@@ -407,7 +429,7 @@ export class AiSettings extends React.Component<
407429
...this._currentSettings,
408430
...this._defaultFormData
409431
};
410-
this.saveSettingsToLocalStorage(this._currentSettings);
432+
this.saveSettingsToLocalStorage();
411433
this.saveSettingsToRegistry();
412434
this.setState({ isModified: false });
413435
};
@@ -459,7 +481,6 @@ export class AiSettings extends React.Component<
459481
private _provider: string;
460482
private _providerSchema: JSONSchema7;
461483
private _useSecretsManager: boolean;
462-
private _hideSecretFields: boolean;
463484
private _rmRegistry: IRenderMimeRegistry | null;
464485
private _secretsManager: ISecretsManager | null;
465486
private _settingConnector: ISettingConnector | null;

yarn.lock

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ __metadata:
14391439
languageName: node
14401440
linkType: hard
14411441

1442-
"@jupyterlab/settingregistry@npm:^4.4.0, @jupyterlab/settingregistry@npm:^4.4.1":
1442+
"@jupyterlab/settingregistry@npm:^4.0.0, @jupyterlab/settingregistry@npm:^4.4.0, @jupyterlab/settingregistry@npm:^4.4.1":
14431443
version: 4.4.1
14441444
resolution: "@jupyterlab/settingregistry@npm:4.4.1"
14451445
dependencies:
@@ -1598,7 +1598,7 @@ __metadata:
15981598
eslint-config-prettier: ^8.8.0
15991599
eslint-plugin-prettier: ^5.0.0
16001600
json5: ^2.2.3
1601-
jupyter-secrets-manager: ^0.3.0
1601+
jupyter-secrets-manager: ^0.4.0
16021602
npm-run-all: ^4.1.5
16031603
prettier: ^3.0.0
16041604
react: ^18.2.0
@@ -5862,16 +5862,18 @@ __metadata:
58625862
languageName: node
58635863
linkType: hard
58645864

5865-
"jupyter-secrets-manager@npm:^0.3.0":
5866-
version: 0.3.0
5867-
resolution: "jupyter-secrets-manager@npm:0.3.0"
5865+
"jupyter-secrets-manager@npm:^0.4.0":
5866+
version: 0.4.0
5867+
resolution: "jupyter-secrets-manager@npm:0.4.0"
58685868
dependencies:
58695869
"@jupyterlab/application": ^4.0.0
58705870
"@jupyterlab/coreutils": ^6.0.0
5871+
"@jupyterlab/settingregistry": ^4.0.0
58715872
"@jupyterlab/statedb": ^4.0.0
58725873
"@lumino/algorithm": ^2.0.0
58735874
"@lumino/coreutils": ^2.1.2
5874-
checksum: 8e0b9dd4acf746c3e8383a8de2621745297f7e96a323b7a5469e9cb487d4ddf0ef36af9b716a53e85868e922dfae3632dc4a961b8c059b2b79b5a0b93f2146d5
5875+
"@lumino/signaling": ^2.1.2
5876+
checksum: 1cf0399df4bc9c16e91b60d2526e32f6690b5a3b4293d5ff727ab992b49d08d172e3fba6f3d95ad97eef44b1f934d2db56823c886c47bca2fb1216e4a9b4f140
58755877
languageName: node
58765878
linkType: hard
58775879

0 commit comments

Comments
 (0)