@ibgib/web-gib 0.0.59 → 0.0.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/helpers.d.mts.map +1 -1
- package/dist/helpers.mjs +1 -0
- package/dist/helpers.mjs.map +1 -1
- package/dist/identity/device-delegate-helper.d.mts +43 -0
- package/dist/identity/device-delegate-helper.d.mts.map +1 -0
- package/dist/identity/device-delegate-helper.mjs +106 -0
- package/dist/identity/device-delegate-helper.mjs.map +1 -0
- package/dist/identity/device-delegate.respec.d.mts +2 -0
- package/dist/identity/device-delegate.respec.d.mts.map +1 -0
- package/dist/identity/device-delegate.respec.mjs +106 -0
- package/dist/identity/device-delegate.respec.mjs.map +1 -0
- package/dist/identity/identity-constants.mjs +2 -2
- package/dist/identity/identity-constants.mjs.map +1 -1
- package/dist/identity/identity-types.d.mts +4 -4
- package/dist/identity/identity-types.d.mts.map +1 -1
- package/dist/identity/keystone-sync-service.d.mts +18 -0
- package/dist/identity/keystone-sync-service.d.mts.map +1 -1
- package/dist/identity/keystone-sync-service.mjs +77 -1
- package/dist/identity/keystone-sync-service.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/index.mjs.map +1 -1
- package/dist/storage/storage-helpers.web.d.mts +9 -0
- package/dist/storage/storage-helpers.web.d.mts.map +1 -1
- package/dist/storage/storage-helpers.web.mjs +42 -0
- package/dist/storage/storage-helpers.web.mjs.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.css +93 -17
- package/dist/ui/component/identity/add-device/add-device.d.mts +24 -7
- package/dist/ui/component/identity/add-device/add-device.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.html +97 -23
- package/dist/ui/component/identity/add-device/add-device.mjs +489 -198
- package/dist/ui/component/identity/add-device/add-device.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +48 -61
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +11 -8
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +358 -97
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.html +3 -3
- package/dist/ui/component/identity/remove-device/remove-device.mjs +7 -7
- package/dist/ui/component/identity/remove-device/remove-device.mjs.map +1 -1
- package/dist/ui/ui-constants.d.mts +1 -0
- package/dist/ui/ui-constants.d.mts.map +1 -1
- package/dist/ui/ui-constants.mjs +1 -0
- package/dist/ui/ui-constants.mjs.map +1 -1
- package/dist/ui/ui-types.d.mts +5 -0
- package/dist/ui/ui-types.d.mts.map +1 -1
- package/dist/util/device-helper.d.mts +30 -0
- package/dist/util/device-helper.d.mts.map +1 -0
- package/dist/util/device-helper.mjs +90 -0
- package/dist/util/device-helper.mjs.map +1 -0
- package/dist/util/device-helper.respec.d.mts +2 -0
- package/dist/util/device-helper.respec.d.mts.map +1 -0
- package/dist/util/device-helper.respec.mjs +83 -0
- package/dist/util/device-helper.respec.mjs.map +1 -0
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/helpers.mts +1 -0
- package/src/identity/custodian-delegate-helper.mts.bak +7 -0
- package/src/identity/device-delegate-helper.mts +135 -0
- package/src/identity/device-delegate.respec.mts +125 -0
- package/src/identity/identity-constants.mts +2 -2
- package/src/identity/identity-types.mts +4 -4
- package/src/identity/keystone-sync-service.mts +95 -2
- package/src/index.mts +1 -2
- package/src/storage/storage-helpers.web.mts +47 -0
- package/src/ui/component/identity/add-device/add-device.css +93 -17
- package/src/ui/component/identity/add-device/add-device.html +97 -23
- package/src/ui/component/identity/add-device/add-device.mts +579 -208
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +55 -69
- package/src/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/src/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/src/ui/component/identity/keystone-details/keystone-details.mts +374 -106
- package/src/ui/component/identity/remove-device/remove-device.html +3 -3
- package/src/ui/component/identity/remove-device/remove-device.mts +8 -8
- package/src/ui/ui-constants.mts +1 -0
- package/src/ui/ui-types.mts +6 -0
- package/src/util/device-helper.mts +104 -0
- package/src/util/device-helper.respec.mts +90 -0
- package/dist/identity/custodian-delegate-helper.d.mts +0 -22
- package/dist/identity/custodian-delegate-helper.d.mts.map +0 -1
- package/dist/identity/custodian-delegate-helper.mjs +0 -50
- package/dist/identity/custodian-delegate-helper.mjs.map +0 -1
- package/src/identity/custodian-delegate-helper.mts +0 -61
|
@@ -23,10 +23,11 @@ import { updateSpecialIndex } from "@ibgib/core-gib/dist/timeline/timeline-api.m
|
|
|
23
23
|
import { getTjpAddr, toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
24
24
|
import { parseSpaceIb } from "@ibgib/core-gib/dist/witness/space/space-helper.mjs";
|
|
25
25
|
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
26
|
-
|
|
27
26
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
28
27
|
import { promptForSecret } from "../../../../helpers.web.mjs";
|
|
29
|
-
import { isSsoProviderLinked } from "
|
|
28
|
+
import { isSsoProviderLinked, getKeystoneCapabilities, isDelegateExpired } from "@ibgib/core-gib/dist/keystone/delegate/delegate-helpers.mjs";
|
|
29
|
+
import { generatePoolChallenges } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
30
|
+
import { POOL_ID_MANAGE } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
30
31
|
import { getGlobalMetaspace_waitIfNeeded } from "../../../../helpers.mjs";
|
|
31
32
|
|
|
32
33
|
const logalot = false;
|
|
@@ -94,12 +95,12 @@ interface KeystoneDetailsElements extends ElementsBase {
|
|
|
94
95
|
btnLinkGoogleEl: HTMLDivElement;
|
|
95
96
|
btnLinkGithubEl: HTMLDivElement;
|
|
96
97
|
ssoStatusMsgEl: HTMLDivElement;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
inputCurrentPasswordEl: HTMLInputElement;
|
|
99
|
+
inputNewPasswordEl: HTMLInputElement;
|
|
100
|
+
inputConfirmPasswordEl: HTMLInputElement;
|
|
101
|
+
btnChangePasswordEl: HTMLButtonElement;
|
|
102
|
+
changePasswordStatusEl: HTMLDivElement;
|
|
103
|
+
changePasswordCardEl: HTMLDivElement;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
export class KeystoneDetailsComponentInstance
|
|
@@ -118,6 +119,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
118
119
|
private isHistoricalFrame: boolean = false;
|
|
119
120
|
private _onIdentityChanged: ((e: any) => void) | null = null;
|
|
120
121
|
private keystoneCache: Map<string, { username: string, description: string, isPrimary: boolean, n: number }> = new Map();
|
|
122
|
+
private aggregatedDetails?: any;
|
|
121
123
|
|
|
122
124
|
constructor() {
|
|
123
125
|
super();
|
|
@@ -212,12 +214,12 @@ export class KeystoneDetailsComponentInstance
|
|
|
212
214
|
this.elements.btnLinkGoogleEl = shadow.getElementById('btn-link-google') as HTMLDivElement;
|
|
213
215
|
this.elements.btnLinkGithubEl = shadow.getElementById('btn-link-github') as HTMLDivElement;
|
|
214
216
|
this.elements.ssoStatusMsgEl = shadow.getElementById('sso-status-msg') as HTMLDivElement;
|
|
215
|
-
this.elements.
|
|
216
|
-
this.elements.
|
|
217
|
-
this.elements.
|
|
218
|
-
this.elements.
|
|
219
|
-
this.elements.
|
|
220
|
-
this.elements.
|
|
217
|
+
this.elements.inputCurrentPasswordEl = shadow.getElementById('input-current-password') as HTMLInputElement;
|
|
218
|
+
this.elements.inputNewPasswordEl = shadow.getElementById('input-new-password') as HTMLInputElement;
|
|
219
|
+
this.elements.inputConfirmPasswordEl = shadow.getElementById('input-confirm-password') as HTMLInputElement;
|
|
220
|
+
this.elements.btnChangePasswordEl = shadow.getElementById('btn-change-password') as HTMLButtonElement;
|
|
221
|
+
this.elements.changePasswordStatusEl = shadow.getElementById('change-password-status') as HTMLDivElement;
|
|
222
|
+
this.elements.changePasswordCardEl = shadow.getElementById('change-password-card') as HTMLDivElement;
|
|
221
223
|
|
|
222
224
|
// ElementBase requires contentEl
|
|
223
225
|
this.elements.contentEl = shadow.getElementById('identity-details-view') as HTMLDivElement;
|
|
@@ -260,7 +262,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
260
262
|
this.elements!.btnUpdateIdentityEl.addEventListener('click', () => this.handleUpdateIdentity());
|
|
261
263
|
this.elements!.btnLinkGoogleEl.addEventListener('click', () => this.handleSSOClick('google'));
|
|
262
264
|
this.elements!.btnLinkGithubEl.addEventListener('click', () => this.handleSSOClick('github'));
|
|
263
|
-
this.elements!.
|
|
265
|
+
this.elements!.btnChangePasswordEl.addEventListener('click', () => this.handleChangePassword());
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
private toggleAccordion() {
|
|
@@ -454,9 +456,12 @@ export class KeystoneDetailsComponentInstance
|
|
|
454
456
|
this.elements.btnLinkGoogleEl.classList.remove('disabled');
|
|
455
457
|
this.elements.btnLinkGithubEl.classList.remove('disabled');
|
|
456
458
|
|
|
457
|
-
// Check if
|
|
458
|
-
const
|
|
459
|
-
const
|
|
459
|
+
// Check if origin provider or linked
|
|
460
|
+
const originProvider = this.aggregatedDetails?.custodianProvider || this.ibGib?.data?.frameDetails?.custodianProvider;
|
|
461
|
+
const isGoogleLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId: 'google' });
|
|
462
|
+
const isGithubLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId: 'github' });
|
|
463
|
+
|
|
464
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
460
465
|
|
|
461
466
|
// Google Card States
|
|
462
467
|
const googleCard = this.elements.btnLinkGoogleEl;
|
|
@@ -464,11 +469,22 @@ export class KeystoneDetailsComponentInstance
|
|
|
464
469
|
if (isGoogleLinked) {
|
|
465
470
|
googleCard.classList.remove('unlinked');
|
|
466
471
|
googleCard.classList.add('linked');
|
|
467
|
-
|
|
472
|
+
const isOrigin = originProvider === 'google';
|
|
473
|
+
if (isOrigin && !caps.isSovereign) {
|
|
474
|
+
googleLabel.textContent = "Google (Primary Account) 🔒";
|
|
475
|
+
googleCard.title = "Cannot unlink primary account without first adding a Master Password below.";
|
|
476
|
+
} else if (isOrigin) {
|
|
477
|
+
googleLabel.textContent = "Google (Primary Account) ✅";
|
|
478
|
+
googleCard.title = "Click to unlink Google provider (requires Master Password).";
|
|
479
|
+
} else {
|
|
480
|
+
googleLabel.textContent = "Google Linked ✅";
|
|
481
|
+
googleCard.title = "Click to unlink Google provider.";
|
|
482
|
+
}
|
|
468
483
|
} else {
|
|
469
484
|
googleCard.classList.remove('linked');
|
|
470
485
|
googleCard.classList.add('unlinked');
|
|
471
486
|
googleLabel.textContent = "Unlinked";
|
|
487
|
+
googleCard.title = "Click to link Google account.";
|
|
472
488
|
}
|
|
473
489
|
|
|
474
490
|
// GitHub Card States
|
|
@@ -477,15 +493,47 @@ export class KeystoneDetailsComponentInstance
|
|
|
477
493
|
if (isGithubLinked) {
|
|
478
494
|
githubCard.classList.remove('unlinked');
|
|
479
495
|
githubCard.classList.add('linked');
|
|
480
|
-
|
|
496
|
+
const isOrigin = originProvider === 'github';
|
|
497
|
+
if (isOrigin && !caps.isSovereign) {
|
|
498
|
+
githubLabel.textContent = "GitHub (Primary Account) 🔒";
|
|
499
|
+
githubCard.title = "Cannot unlink primary account without first adding a Master Password below.";
|
|
500
|
+
} else if (isOrigin) {
|
|
501
|
+
githubLabel.textContent = "GitHub (Primary Account) ✅";
|
|
502
|
+
githubCard.title = "Click to unlink GitHub provider (requires Master Password).";
|
|
503
|
+
} else {
|
|
504
|
+
githubLabel.textContent = "GitHub Linked ✅";
|
|
505
|
+
githubCard.title = "Click to unlink GitHub provider.";
|
|
506
|
+
}
|
|
481
507
|
} else {
|
|
482
508
|
githubCard.classList.remove('linked');
|
|
483
509
|
githubCard.classList.add('unlinked');
|
|
484
510
|
githubLabel.textContent = "Unlinked";
|
|
511
|
+
githubCard.title = "Click to link GitHub account.";
|
|
485
512
|
}
|
|
486
513
|
}
|
|
487
514
|
}
|
|
488
515
|
|
|
516
|
+
private updatePasswordCardView(): void {
|
|
517
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
518
|
+
const cardTitleEl = this.elements?.changePasswordCardEl?.querySelector('h3');
|
|
519
|
+
const cardDescEl = this.elements?.changePasswordCardEl?.querySelector('.description');
|
|
520
|
+
const currentPasswordGroup = this.elements?.inputCurrentPasswordEl?.closest('.form-group') as HTMLElement | null;
|
|
521
|
+
const btnChangePassword = this.elements?.btnChangePasswordEl;
|
|
522
|
+
|
|
523
|
+
const isCustodialOnly = caps.isCustodial && !caps.isSovereign;
|
|
524
|
+
if (isCustodialOnly) {
|
|
525
|
+
if (cardTitleEl) cardTitleEl.textContent = "Add Master Password";
|
|
526
|
+
if (cardDescEl) cardDescEl.textContent = "Establish sovereign ownership by adding a master password. This enables direct, independent cryptographic management without relying solely on custodian authentication.";
|
|
527
|
+
if (currentPasswordGroup) currentPasswordGroup.style.display = 'none';
|
|
528
|
+
if (btnChangePassword) btnChangePassword.textContent = "Add Master Password";
|
|
529
|
+
} else {
|
|
530
|
+
if (cardTitleEl) cardTitleEl.textContent = "Change Password";
|
|
531
|
+
if (cardDescEl) cardDescEl.textContent = "Rotate your local password that drives this identity. This will cryptographically evolve your primary identity and all registered user-owned delegate keystones to the new password.";
|
|
532
|
+
if (currentPasswordGroup) currentPasswordGroup.style.display = '';
|
|
533
|
+
if (btnChangePassword) btnChangePassword.textContent = "Update Password";
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
489
537
|
private async updateFrameDetailsView(): Promise<void> {
|
|
490
538
|
const lc = `${this.lc}[${this.updateFrameDetailsView.name}]`;
|
|
491
539
|
try {
|
|
@@ -549,6 +597,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
549
597
|
space
|
|
550
598
|
});
|
|
551
599
|
|
|
600
|
+
this.aggregatedDetails = aggregated;
|
|
552
601
|
this.elements.identityAggrDetailsEl.textContent = aggregated ? pretty(aggregated) : '{}';
|
|
553
602
|
|
|
554
603
|
// Update Username and Description fields
|
|
@@ -556,16 +605,19 @@ export class KeystoneDetailsComponentInstance
|
|
|
556
605
|
this.elements.identityDescriptionEl.textContent = aggregated?.description || aggregated?.profile?.description || '-';
|
|
557
606
|
} catch (err) {
|
|
558
607
|
console.warn(`${lc} Error aggregating details: ${extractErrorMsg(err)}`);
|
|
608
|
+
this.aggregatedDetails = undefined;
|
|
559
609
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
560
610
|
this.elements.identityNameEl.textContent = '-';
|
|
561
611
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
562
612
|
}
|
|
563
613
|
} else {
|
|
614
|
+
this.aggregatedDetails = undefined;
|
|
564
615
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
565
616
|
this.elements.identityNameEl.textContent = '-';
|
|
566
617
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
567
618
|
}
|
|
568
619
|
} else {
|
|
620
|
+
this.aggregatedDetails = undefined;
|
|
569
621
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
570
622
|
this.elements.identityNameEl.textContent = '-';
|
|
571
623
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
@@ -591,17 +643,18 @@ export class KeystoneDetailsComponentInstance
|
|
|
591
643
|
// Render Challenge Pools
|
|
592
644
|
this.renderChallengePools(keystone.data?.challengePools || []);
|
|
593
645
|
|
|
594
|
-
// Render Delegates, SSO link, and
|
|
646
|
+
// Render Delegates, SSO link, and Password Change cards (only if this is a primary keystone, hide them for delegate/session keystones)
|
|
595
647
|
const details = await this.getKeystoneDetails(this.ibGibAddr);
|
|
596
648
|
if (details.isPrimary) {
|
|
597
649
|
this.elements.delegatesCardEl.classList.remove('hidden');
|
|
598
650
|
await this.renderDelegates(keystone.data?.delegates);
|
|
599
651
|
this.elements.ssoLinkCardEl.classList.remove('hidden');
|
|
600
|
-
this.elements.
|
|
652
|
+
this.elements.changePasswordCardEl.classList.remove('hidden');
|
|
653
|
+
this.updatePasswordCardView();
|
|
601
654
|
} else {
|
|
602
655
|
this.elements.delegatesCardEl.classList.add('hidden');
|
|
603
656
|
this.elements.ssoLinkCardEl.classList.add('hidden');
|
|
604
|
-
this.elements.
|
|
657
|
+
this.elements.changePasswordCardEl.classList.add('hidden');
|
|
605
658
|
}
|
|
606
659
|
|
|
607
660
|
this.updateActiveButtonState();
|
|
@@ -615,6 +668,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
615
668
|
|
|
616
669
|
private clearActiveDetails() {
|
|
617
670
|
if (!this.elements) { return; }
|
|
671
|
+
this.aggregatedDetails = undefined;
|
|
618
672
|
this.isHistoricalFrame = false;
|
|
619
673
|
if (this.elements.historicalWarningBannerEl) {
|
|
620
674
|
this.elements.historicalWarningBannerEl.classList.add('hidden');
|
|
@@ -804,43 +858,71 @@ export class KeystoneDetailsComponentInstance
|
|
|
804
858
|
}
|
|
805
859
|
|
|
806
860
|
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
861
|
+
if (!metaspace) {
|
|
862
|
+
throw new Error(`(UNEXPECTED) metaspace falsy? (E: 8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3e)`);
|
|
863
|
+
}
|
|
864
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
865
|
+
if (!space) {
|
|
866
|
+
throw new Error(`(UNEXPECTED) space falsy? (E: 9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4f)`);
|
|
813
867
|
}
|
|
868
|
+
const localSpaceId = space.ib ? parseSpaceIb({ spaceIb: space.ib }).spaceId : undefined;
|
|
814
869
|
|
|
815
870
|
for (const [delegateTjpAddr, info] of Object.entries(delegates)) {
|
|
816
871
|
const tr = document.createElement('tr');
|
|
817
872
|
|
|
818
|
-
const deviceName = info.deviceName || info.details?.deviceTag || info.details?.name || 'Device Delegate';
|
|
873
|
+
const deviceName = info.deviceName || (info as any).details?.deviceTag || (info as any).details?.name || 'Device Delegate';
|
|
819
874
|
const tdName = document.createElement('td');
|
|
820
875
|
|
|
821
|
-
const delegateSpaceId = info.spaceId || info.details?.spaceId;
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
badgeEl.className = 'badge badge-this-device';
|
|
834
|
-
badgeEl.style.backgroundColor = '#1b5e20';
|
|
835
|
-
badgeEl.style.color = '#a5d6a7';
|
|
836
|
-
} else {
|
|
837
|
-
badgeEl.textContent = 'Remote Device';
|
|
838
|
-
badgeEl.className = 'badge badge-remote-device';
|
|
839
|
-
badgeEl.style.backgroundColor = '#0d47a1';
|
|
840
|
-
badgeEl.style.color = '#90caf9';
|
|
876
|
+
const delegateSpaceId = info.spaceId || (info as any).details?.spaceId;
|
|
877
|
+
|
|
878
|
+
// Check if delegate is present in local space (via spaceId match or direct presence check)
|
|
879
|
+
let isThisDevice = false;
|
|
880
|
+
if (localSpaceId && delegateSpaceId === localSpaceId) {
|
|
881
|
+
isThisDevice = true;
|
|
882
|
+
} else {
|
|
883
|
+
try {
|
|
884
|
+
const resGet = await metaspace.get({ addrs: [delegateTjpAddr], space });
|
|
885
|
+
isThisDevice = !!(resGet.success && resGet.ibGibs && resGet.ibGibs.length > 0);
|
|
886
|
+
} catch {
|
|
887
|
+
isThisDevice = false;
|
|
841
888
|
}
|
|
842
889
|
}
|
|
843
890
|
|
|
891
|
+
const badgeEl = document.createElement('span');
|
|
892
|
+
badgeEl.style.marginLeft = '0.5rem';
|
|
893
|
+
badgeEl.style.padding = '0.15rem 0.4rem';
|
|
894
|
+
badgeEl.style.borderRadius = '4px';
|
|
895
|
+
badgeEl.style.fontSize = '0.75rem';
|
|
896
|
+
badgeEl.style.fontWeight = 'bold';
|
|
897
|
+
|
|
898
|
+
if (isThisDevice) {
|
|
899
|
+
badgeEl.textContent = 'This Device';
|
|
900
|
+
badgeEl.className = 'badge badge-this-device';
|
|
901
|
+
badgeEl.style.backgroundColor = '#1b5e20';
|
|
902
|
+
badgeEl.style.color = '#a5d6a7';
|
|
903
|
+
} else {
|
|
904
|
+
badgeEl.textContent = 'Remote Device';
|
|
905
|
+
badgeEl.className = 'badge badge-remote-device';
|
|
906
|
+
badgeEl.style.backgroundColor = '#0d47a1';
|
|
907
|
+
badgeEl.style.color = '#90caf9';
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Check expiration
|
|
911
|
+
const isExpired = isDelegateExpired({ delegateInfo: info });
|
|
912
|
+
let expiredBadgeEl: HTMLSpanElement | null = null;
|
|
913
|
+
if (isExpired) {
|
|
914
|
+
expiredBadgeEl = document.createElement('span');
|
|
915
|
+
expiredBadgeEl.textContent = 'Expired';
|
|
916
|
+
expiredBadgeEl.className = 'badge badge-expired';
|
|
917
|
+
expiredBadgeEl.style.marginLeft = '0.3rem';
|
|
918
|
+
expiredBadgeEl.style.padding = '0.15rem 0.4rem';
|
|
919
|
+
expiredBadgeEl.style.borderRadius = '4px';
|
|
920
|
+
expiredBadgeEl.style.fontSize = '0.75rem';
|
|
921
|
+
expiredBadgeEl.style.fontWeight = 'bold';
|
|
922
|
+
expiredBadgeEl.style.backgroundColor = '#b71c1c';
|
|
923
|
+
expiredBadgeEl.style.color = '#ef9a9a';
|
|
924
|
+
}
|
|
925
|
+
|
|
844
926
|
const applyNameAndBadge = (displayName: string, hoverText?: string) => {
|
|
845
927
|
tdName.innerHTML = '';
|
|
846
928
|
const textSpan = document.createElement('span');
|
|
@@ -849,6 +931,9 @@ export class KeystoneDetailsComponentInstance
|
|
|
849
931
|
if (badgeEl) {
|
|
850
932
|
tdName.appendChild(badgeEl);
|
|
851
933
|
}
|
|
934
|
+
if (expiredBadgeEl) {
|
|
935
|
+
tdName.appendChild(expiredBadgeEl);
|
|
936
|
+
}
|
|
852
937
|
tdName.title = hoverText || displayName;
|
|
853
938
|
};
|
|
854
939
|
|
|
@@ -863,7 +948,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
863
948
|
tdVerbs.textContent = info.allowedVerbs ? info.allowedVerbs.join(', ') : '-';
|
|
864
949
|
tdVerbs.title = tdVerbs.textContent;
|
|
865
950
|
|
|
866
|
-
const isRemote =
|
|
951
|
+
const isRemote = !isThisDevice;
|
|
867
952
|
|
|
868
953
|
const tdActions = document.createElement('td');
|
|
869
954
|
const btnView = document.createElement('button');
|
|
@@ -874,20 +959,20 @@ export class KeystoneDetailsComponentInstance
|
|
|
874
959
|
if (isRemote) {
|
|
875
960
|
this.showRemoteDelegateModal(info);
|
|
876
961
|
} else {
|
|
877
|
-
if (info.delegateAddr) {
|
|
962
|
+
if (info.delegateAddr || delegateTjpAddr) {
|
|
878
963
|
let addrToLoad = info.delegateAddr;
|
|
879
|
-
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
console.warn(`${lc} Error resolving latest delegate address: ${extractErrorMsg(err)}`);
|
|
889
|
-
}
|
|
964
|
+
try {
|
|
965
|
+
const keystoneService = new KeystoneService_V1();
|
|
966
|
+
const latestKeystone = await keystoneService.getLatestKeystone({
|
|
967
|
+
addr: delegateTjpAddr,
|
|
968
|
+
metaspace,
|
|
969
|
+
space
|
|
970
|
+
});
|
|
971
|
+
if (latestKeystone) {
|
|
972
|
+
addrToLoad = getIbGibAddr({ ibGib: latestKeystone });
|
|
890
973
|
}
|
|
974
|
+
} catch (err) {
|
|
975
|
+
console.warn(`${lc} Error resolving latest delegate keystone: ${extractErrorMsg(err)}`);
|
|
891
976
|
}
|
|
892
977
|
this.dispatchEvent(new CustomEvent('ibgib-view-keystone-details', {
|
|
893
978
|
detail: { addr: addrToLoad },
|
|
@@ -924,7 +1009,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
924
1009
|
let masterSecret: string;
|
|
925
1010
|
try {
|
|
926
1011
|
masterSecret = await promptForSecret({
|
|
927
|
-
msg: `Enter Master
|
|
1012
|
+
msg: `Enter Master Password to revoke delegate '${deviceName}':`,
|
|
928
1013
|
confirm: false
|
|
929
1014
|
});
|
|
930
1015
|
} catch (err) {
|
|
@@ -1103,8 +1188,8 @@ export class KeystoneDetailsComponentInstance
|
|
|
1103
1188
|
while (tries < maxTries && !isCorrect) {
|
|
1104
1189
|
try {
|
|
1105
1190
|
const promptMsg = tries === 0
|
|
1106
|
-
? "Link successful! Enter your local Master Secret (
|
|
1107
|
-
: `Incorrect
|
|
1191
|
+
? "Link successful! Enter your local Master Secret (Password) to sign and register the custodian pool"
|
|
1192
|
+
: `Incorrect password. Please try again (${maxTries - tries} tries remaining):`;
|
|
1108
1193
|
masterSecret = await promptForSecret({
|
|
1109
1194
|
msg: promptMsg,
|
|
1110
1195
|
confirm: false
|
|
@@ -1133,7 +1218,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1133
1218
|
}
|
|
1134
1219
|
|
|
1135
1220
|
if (!isCorrect) {
|
|
1136
|
-
this.setSSOStatus("Link cancelled or failed (incorrect
|
|
1221
|
+
this.setSSOStatus("Link cancelled or failed (incorrect password).", "error");
|
|
1137
1222
|
return;
|
|
1138
1223
|
}
|
|
1139
1224
|
|
|
@@ -1187,8 +1272,13 @@ export class KeystoneDetailsComponentInstance
|
|
|
1187
1272
|
}
|
|
1188
1273
|
|
|
1189
1274
|
protected async handleSSOClick(providerId: 'google' | 'github') {
|
|
1190
|
-
const isLinked = isSsoProviderLinked({ keystone: this.ibGib, providerId });
|
|
1275
|
+
const isLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId });
|
|
1191
1276
|
if (isLinked) {
|
|
1277
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
1278
|
+
if (!caps.isSovereign) {
|
|
1279
|
+
this.setSSOStatus(`Cannot unlink ${providerId}: This is your only identity recovery and management method. Please add a Master Password first in the Password card below to enable sovereign ownership before unlinking.`, "error");
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1192
1282
|
await this.handleSSOUnlink(providerId);
|
|
1193
1283
|
} else {
|
|
1194
1284
|
await this.handleSSOLink(providerId);
|
|
@@ -1200,7 +1290,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1200
1290
|
this.setSSOStatus(`Unlinking ${providerId}...`, "info");
|
|
1201
1291
|
|
|
1202
1292
|
try {
|
|
1203
|
-
// 1. Prompt user for
|
|
1293
|
+
// 1. Prompt user for password
|
|
1204
1294
|
const keystoneService = new KeystoneService_V1();
|
|
1205
1295
|
let masterSecret: string | undefined;
|
|
1206
1296
|
let isCorrect = false;
|
|
@@ -1210,8 +1300,8 @@ export class KeystoneDetailsComponentInstance
|
|
|
1210
1300
|
while (tries < maxTries && !isCorrect) {
|
|
1211
1301
|
try {
|
|
1212
1302
|
const promptMsg = tries === 0
|
|
1213
|
-
? `Are you sure you want to unlink your ${providerId} account? If so, enter your local Master Secret (
|
|
1214
|
-
: `Incorrect
|
|
1303
|
+
? `Are you sure you want to unlink your ${providerId} account? If so, enter your local Master Secret (Password) to authorize. Otherwise, click Cancel.`
|
|
1304
|
+
: `Incorrect password. Please try again (${maxTries - tries} tries remaining):`;
|
|
1215
1305
|
masterSecret = await promptForSecret({
|
|
1216
1306
|
msg: promptMsg,
|
|
1217
1307
|
confirm: false
|
|
@@ -1238,7 +1328,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1238
1328
|
}
|
|
1239
1329
|
|
|
1240
1330
|
if (!isCorrect) {
|
|
1241
|
-
this.setSSOStatus("Unlink cancelled or failed (incorrect
|
|
1331
|
+
this.setSSOStatus("Unlink cancelled or failed (incorrect password).", "error");
|
|
1242
1332
|
return;
|
|
1243
1333
|
}
|
|
1244
1334
|
|
|
@@ -1251,7 +1341,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1251
1341
|
|
|
1252
1342
|
// 2. Evolve parent keystone to remove the custodian pool
|
|
1253
1343
|
const targetPoolId = `custodian-manage-${providerId}`;
|
|
1254
|
-
|
|
1344
|
+
let evolvedParent = await keystoneService.removePools({
|
|
1255
1345
|
latestKeystone: this.ibGib!,
|
|
1256
1346
|
masterSecret: masterSecret!,
|
|
1257
1347
|
poolIds: [targetPoolId],
|
|
@@ -1259,6 +1349,22 @@ export class KeystoneDetailsComponentInstance
|
|
|
1259
1349
|
space
|
|
1260
1350
|
});
|
|
1261
1351
|
|
|
1352
|
+
// Unregister any corresponding outward delegate for this provider if present
|
|
1353
|
+
const delegatesMap = this.ibGib?.data?.delegates;
|
|
1354
|
+
if (delegatesMap) {
|
|
1355
|
+
for (const [delegateTjpAddr, info] of Object.entries(delegatesMap)) {
|
|
1356
|
+
if (info.deviceName?.includes(providerId)) {
|
|
1357
|
+
evolvedParent = await keystoneService.unregisterDelegate({
|
|
1358
|
+
parentKeystone: evolvedParent,
|
|
1359
|
+
delegateTjpAddr,
|
|
1360
|
+
masterSecret: masterSecret!,
|
|
1361
|
+
metaspace,
|
|
1362
|
+
space,
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1262
1368
|
// 3. Sync evolved parent keystone back to server
|
|
1263
1369
|
this.setSSOStatus("Publishing evolved identity keystone to server...", "info");
|
|
1264
1370
|
const syncRes = await getApiBridge().putEvolveKeystone({
|
|
@@ -1292,49 +1398,211 @@ export class KeystoneDetailsComponentInstance
|
|
|
1292
1398
|
}
|
|
1293
1399
|
}
|
|
1294
1400
|
|
|
1295
|
-
private
|
|
1296
|
-
const el = this.elements!.
|
|
1401
|
+
private setChangePasswordStatus(msg: string, type: 'info' | 'success' | 'error') {
|
|
1402
|
+
const el = this.elements!.changePasswordStatusEl;
|
|
1297
1403
|
el.textContent = msg;
|
|
1298
1404
|
el.className = `status-msg ${type}`;
|
|
1299
1405
|
el.classList.remove('hidden');
|
|
1300
1406
|
}
|
|
1301
1407
|
|
|
1302
|
-
protected async
|
|
1303
|
-
const lc = `${this.lc}[${this.
|
|
1304
|
-
this.
|
|
1408
|
+
protected async handleAddMasterPassword(): Promise<void> {
|
|
1409
|
+
const lc = `${this.lc}[${this.handleAddMasterPassword.name}]`;
|
|
1410
|
+
this.setChangePasswordStatus("Validating entries...", "info");
|
|
1411
|
+
|
|
1412
|
+
try {
|
|
1413
|
+
const newPassword = this.elements!.inputNewPasswordEl.value;
|
|
1414
|
+
const confirmPassword = this.elements!.inputConfirmPasswordEl.value;
|
|
1415
|
+
|
|
1416
|
+
if (!newPassword) {
|
|
1417
|
+
this.setChangePasswordStatus("Master password is required.", "error");
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
if (newPassword !== confirmPassword) {
|
|
1421
|
+
this.setChangePasswordStatus("Master password and confirmation do not match.", "error");
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
const initialManagePool = this.ibGib?.data?.challengePools?.find(p => p.id === POOL_ID_MANAGE);
|
|
1426
|
+
if (!initialManagePool) {
|
|
1427
|
+
this.setChangePasswordStatus("Primary keystone missing active manage challenge pool.", "error");
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
this.showBusy({
|
|
1432
|
+
isBusy: true,
|
|
1433
|
+
title: 'Elevating Identity...',
|
|
1434
|
+
msg: 'Generating cryptographic sovereign challenge pool...',
|
|
1435
|
+
animationEmoji: '🔑'
|
|
1436
|
+
});
|
|
1437
|
+
this.setChangePasswordStatus("Generating sovereign challenge pool...", "info");
|
|
1438
|
+
const newChallenges = await generatePoolChallenges({
|
|
1439
|
+
config: initialManagePool.config,
|
|
1440
|
+
masterSecret: newPassword,
|
|
1441
|
+
});
|
|
1442
|
+
const newManagePool = {
|
|
1443
|
+
id: POOL_ID_MANAGE,
|
|
1444
|
+
config: initialManagePool.config,
|
|
1445
|
+
challenges: newChallenges,
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1448
|
+
const primaryTjpAddr = getTjpAddr({ ibGib: this.ibGib! }) || getIbGibAddr({ ibGib: this.ibGib! });
|
|
1449
|
+
const custodianProvider = this.aggregatedDetails?.custodianProvider || 'google';
|
|
1450
|
+
|
|
1451
|
+
let requestBody: any = {
|
|
1452
|
+
primaryTjpAddr,
|
|
1453
|
+
newManagePool,
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
if (custodianProvider === 'google' || custodianProvider === 'github') {
|
|
1457
|
+
this.showBusy({
|
|
1458
|
+
isBusy: true,
|
|
1459
|
+
title: 'Re-authenticating with Provider...',
|
|
1460
|
+
msg: `Please complete authentication in the ${custodianProvider} popup window...`,
|
|
1461
|
+
animationEmoji: '🔒'
|
|
1462
|
+
});
|
|
1463
|
+
this.setChangePasswordStatus(`Please authenticate in the popup with ${custodianProvider} to verify ownership...`, "info");
|
|
1464
|
+
const resPopup = await executeSsoOAuthPopup({
|
|
1465
|
+
providerId: custodianProvider,
|
|
1466
|
+
parentTjpAddr: primaryTjpAddr,
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
if (!resPopup.success || !resPopup.code) {
|
|
1470
|
+
this.showBusy({ isBusy: false });
|
|
1471
|
+
this.setChangePasswordStatus(resPopup.message || "Re-authentication cancelled.", "error");
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
requestBody.providerId = custodianProvider;
|
|
1476
|
+
requestBody.code = resPopup.code;
|
|
1477
|
+
requestBody.redirectUri = resPopup.redirectUri;
|
|
1478
|
+
} else {
|
|
1479
|
+
this.showBusy({ isBusy: false });
|
|
1480
|
+
// Email OTP fallback
|
|
1481
|
+
const email = this.aggregatedDetails?.emails?.[0] || this.ibGib?.data?.emails?.[0];
|
|
1482
|
+
const otpCode = await promptForSecret({
|
|
1483
|
+
msg: `Enter 6-digit verification code sent to ${email}:`,
|
|
1484
|
+
confirm: false,
|
|
1485
|
+
});
|
|
1486
|
+
if (!otpCode) {
|
|
1487
|
+
this.setChangePasswordStatus("Verification code required.", "error");
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
requestBody.email = email;
|
|
1491
|
+
requestBody.code = otpCode;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
this.showBusy({
|
|
1495
|
+
isBusy: true,
|
|
1496
|
+
title: 'Updating Keystone Timeline...',
|
|
1497
|
+
msg: 'Publishing sovereign master password and evolving identity DAG...',
|
|
1498
|
+
animationEmoji: '⏳'
|
|
1499
|
+
});
|
|
1500
|
+
this.setChangePasswordStatus("Elevating identity to sovereign master password on server...", "info");
|
|
1501
|
+
const response = await fetch('/api/identity/custodian/add-master-password', {
|
|
1502
|
+
method: 'POST',
|
|
1503
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1504
|
+
body: JSON.stringify(requestBody),
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
const resJson = await response.json();
|
|
1508
|
+
if (!response.ok || !resJson.success) {
|
|
1509
|
+
throw new Error(resJson.error || resJson.message || `Server error (${response.status})`);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
const evolvedParent = resJson.primaryKeystone;
|
|
1513
|
+
|
|
1514
|
+
// Save to local user space & metaspace
|
|
1515
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
1516
|
+
if (!metaspace) { throw new Error("Global metaspace not available"); }
|
|
1517
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
1518
|
+
if (!space) { throw new Error("Local user space not available"); }
|
|
1519
|
+
|
|
1520
|
+
await metaspace.put({ ibGibs: [evolvedParent], space });
|
|
1521
|
+
await metaspace.registerNewIbGib({ ibGib: evolvedParent, space });
|
|
1522
|
+
await updateSpecialIndex({
|
|
1523
|
+
type: "keystones",
|
|
1524
|
+
rel8nInfos: [
|
|
1525
|
+
{
|
|
1526
|
+
rel8nName: "keystone",
|
|
1527
|
+
ibGibs: [toDto({ ibGib: evolvedParent })]
|
|
1528
|
+
}
|
|
1529
|
+
],
|
|
1530
|
+
metaspace,
|
|
1531
|
+
space
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
const evolvedAddr = getIbGibAddr({ ibGib: evolvedParent });
|
|
1535
|
+
|
|
1536
|
+
// Dispatch EVENT_IBGIB_IDENTITY_CHANGED
|
|
1537
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_CHANGED, {
|
|
1538
|
+
detail: { activeIdentityAddr: evolvedAddr },
|
|
1539
|
+
bubbles: true,
|
|
1540
|
+
composed: true
|
|
1541
|
+
}));
|
|
1542
|
+
|
|
1543
|
+
this.setChangePasswordStatus("Successfully added Master Password! Identity is now sovereign.", "success");
|
|
1544
|
+
|
|
1545
|
+
// Refresh the view
|
|
1546
|
+
this.ibGibAddr = evolvedAddr;
|
|
1547
|
+
this.staticIbGib = evolvedParent;
|
|
1548
|
+
await this.loadIbGib({ getLatest: true });
|
|
1549
|
+
|
|
1550
|
+
// Clear inputs
|
|
1551
|
+
this.elements!.inputNewPasswordEl.value = '';
|
|
1552
|
+
this.elements!.inputConfirmPasswordEl.value = '';
|
|
1553
|
+
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
console.error(`${lc} Elevation failed: ${extractErrorMsg(error)}`);
|
|
1556
|
+
this.setChangePasswordStatus(`Failed to add master password: ${extractErrorMsg(error)}`, "error");
|
|
1557
|
+
} finally {
|
|
1558
|
+
this.showBusy({ isBusy: false });
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
protected async handleChangePassword(): Promise<void> {
|
|
1563
|
+
const lc = `${this.lc}[${this.handleChangePassword.name}]`;
|
|
1564
|
+
|
|
1565
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
1566
|
+
const isCustodialOnly = caps.isCustodial && !caps.isSovereign;
|
|
1567
|
+
if (isCustodialOnly) {
|
|
1568
|
+
await this.handleAddMasterPassword();
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
this.setChangePasswordStatus("Validating entries...", "info");
|
|
1305
1573
|
|
|
1306
1574
|
try {
|
|
1307
|
-
const
|
|
1308
|
-
const
|
|
1309
|
-
const
|
|
1575
|
+
const currentPassword = this.elements!.inputCurrentPasswordEl.value;
|
|
1576
|
+
const newPassword = this.elements!.inputNewPasswordEl.value;
|
|
1577
|
+
const confirmPassword = this.elements!.inputConfirmPasswordEl.value;
|
|
1310
1578
|
|
|
1311
|
-
if (!
|
|
1312
|
-
this.
|
|
1579
|
+
if (!currentPassword) {
|
|
1580
|
+
this.setChangePasswordStatus("Current password is required.", "error");
|
|
1313
1581
|
return;
|
|
1314
1582
|
}
|
|
1315
|
-
if (!
|
|
1316
|
-
this.
|
|
1583
|
+
if (!newPassword) {
|
|
1584
|
+
this.setChangePasswordStatus("New password is required.", "error");
|
|
1317
1585
|
return;
|
|
1318
1586
|
}
|
|
1319
|
-
if (
|
|
1320
|
-
this.
|
|
1587
|
+
if (newPassword !== confirmPassword) {
|
|
1588
|
+
this.setChangePasswordStatus("New password and confirmation do not match.", "error");
|
|
1321
1589
|
return;
|
|
1322
1590
|
}
|
|
1323
1591
|
|
|
1324
1592
|
const keystoneService = new KeystoneService_V1();
|
|
1325
1593
|
|
|
1326
|
-
// 1. Verify current
|
|
1594
|
+
// 1. Verify current password against parent manage pool
|
|
1327
1595
|
const isMatch = await keystoneService.verifySigningSecret({
|
|
1328
1596
|
keystoneIbGib: this.ibGib!,
|
|
1329
|
-
signingSecret:
|
|
1597
|
+
signingSecret: currentPassword,
|
|
1330
1598
|
poolId: 'manage'
|
|
1331
1599
|
});
|
|
1332
1600
|
if (!isMatch) {
|
|
1333
|
-
this.
|
|
1601
|
+
this.setChangePasswordStatus("Incorrect current password.", "error");
|
|
1334
1602
|
return;
|
|
1335
1603
|
}
|
|
1336
1604
|
|
|
1337
|
-
this.
|
|
1605
|
+
this.setChangePasswordStatus("Evolving primary identity keystone...", "info");
|
|
1338
1606
|
|
|
1339
1607
|
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
1340
1608
|
if (!metaspace) { throw new Error(`(UNEXPECTED) metaspace falsy? (E: 95e3c8374b0f088f339f5c5a8700f826)`); }
|
|
@@ -1346,15 +1614,15 @@ export class KeystoneDetailsComponentInstance
|
|
|
1346
1614
|
const { newKeystone: evolvedParent, newDelegates: evolvedDelegates } =
|
|
1347
1615
|
await keystoneService.changePassword({
|
|
1348
1616
|
keystoneIbGib: this.ibGib!,
|
|
1349
|
-
newMasterSecret:
|
|
1350
|
-
signingSecret:
|
|
1617
|
+
newMasterSecret: newPassword,
|
|
1618
|
+
signingSecret: currentPassword,
|
|
1351
1619
|
signingPoolId: 'manage',
|
|
1352
1620
|
metaspace,
|
|
1353
1621
|
space
|
|
1354
1622
|
});
|
|
1355
1623
|
debugger; // look at evolvedDelegates
|
|
1356
1624
|
|
|
1357
|
-
this.
|
|
1625
|
+
this.setChangePasswordStatus("Resolving and rotating delegate keystones...", "info");
|
|
1358
1626
|
|
|
1359
1627
|
// // 3. Evolve registered delegate keystones (preserving custodian/foreign delegate secrets)
|
|
1360
1628
|
// const delegates = this.ibGib!.data?.delegates || {};
|
|
@@ -1368,7 +1636,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1368
1636
|
// space,
|
|
1369
1637
|
// });
|
|
1370
1638
|
|
|
1371
|
-
// // Identify user-owned challenge pools in the delegate that match the old
|
|
1639
|
+
// // Identify user-owned challenge pools in the delegate that match the old password
|
|
1372
1640
|
// const poolsToRotate: string[] = [];
|
|
1373
1641
|
// for (const pool of delegateKeystone.data?.challengePools ?? []) {
|
|
1374
1642
|
// if (!pool.isForeign) {
|
|
@@ -1383,7 +1651,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1383
1651
|
// */
|
|
1384
1652
|
// const isPoolMatch = await keystoneService.verifySigningSecret({
|
|
1385
1653
|
// keystoneIbGib: delegateKeystone,
|
|
1386
|
-
// signingSecret:
|
|
1654
|
+
// signingSecret: currentPassword,
|
|
1387
1655
|
// poolId: pool.id
|
|
1388
1656
|
// });
|
|
1389
1657
|
// if (isPoolMatch) { poolsToRotate.push(pool.id); }
|
|
@@ -1394,8 +1662,8 @@ export class KeystoneDetailsComponentInstance
|
|
|
1394
1662
|
// console.log(`${lc} Rotating pools in delegate ${delegateInfo.delegateTjpAddr}: ${poolsToRotate.join(', ')}`);
|
|
1395
1663
|
// const {newKeystone: evolvedDelegate} = await keystoneService.changePassword({
|
|
1396
1664
|
// keystoneIbGib: delegateKeystone,
|
|
1397
|
-
// newMasterSecret:
|
|
1398
|
-
// signingSecret:
|
|
1665
|
+
// newMasterSecret: newPassword,
|
|
1666
|
+
// signingSecret: currentPassword,
|
|
1399
1667
|
// signingPoolId: 'manage',
|
|
1400
1668
|
// poolIds: poolsToRotate,
|
|
1401
1669
|
// metaspace,
|
|
@@ -1406,7 +1674,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1406
1674
|
// }
|
|
1407
1675
|
|
|
1408
1676
|
// 4. Sync evolved parent and all evolved delegate keystones atomically
|
|
1409
|
-
this.
|
|
1677
|
+
this.setChangePasswordStatus("Syncing evolved keystones to server...", "info");
|
|
1410
1678
|
const parentTjpAddr = getIbGibAddr({ ibGib: this.ibGib! }); // is this.ibGib the tjp always? or is it the latest in the keystone line? Does it matter?
|
|
1411
1679
|
|
|
1412
1680
|
const syncRes = await getApiBridge().putEvolveKeystone({
|
|
@@ -1419,7 +1687,7 @@ export class KeystoneDetailsComponentInstance
|
|
|
1419
1687
|
}
|
|
1420
1688
|
|
|
1421
1689
|
// 5. Update index locally
|
|
1422
|
-
this.
|
|
1690
|
+
this.setChangePasswordStatus("Updating local indexes...", "info");
|
|
1423
1691
|
const newKeystoneIbGibs = [toDto({ ibGib: evolvedParent })];
|
|
1424
1692
|
if (evolvedDelegates) {
|
|
1425
1693
|
evolvedDelegates.forEach(d => {
|
|
@@ -1440,16 +1708,16 @@ export class KeystoneDetailsComponentInstance
|
|
|
1440
1708
|
});
|
|
1441
1709
|
|
|
1442
1710
|
// Clear inputs
|
|
1443
|
-
this.elements!.
|
|
1444
|
-
this.elements!.
|
|
1445
|
-
this.elements!.
|
|
1711
|
+
this.elements!.inputCurrentPasswordEl.value = "";
|
|
1712
|
+
this.elements!.inputNewPasswordEl.value = "";
|
|
1713
|
+
this.elements!.inputConfirmPasswordEl.value = "";
|
|
1446
1714
|
|
|
1447
|
-
this.
|
|
1715
|
+
this.setChangePasswordStatus("Password updated successfully!", "success");
|
|
1448
1716
|
await this.loadIbGib({ getLatest: true });
|
|
1449
1717
|
|
|
1450
1718
|
} catch (error) {
|
|
1451
|
-
console.error(`${lc}
|
|
1452
|
-
this.
|
|
1719
|
+
console.error(`${lc} Password rotation failed: ${extractErrorMsg(error)}`);
|
|
1720
|
+
this.setChangePasswordStatus(`Update failed: ${extractErrorMsg(error)}`, "error");
|
|
1453
1721
|
}
|
|
1454
1722
|
}
|
|
1455
1723
|
}
|