mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 09:43:23 +00:00
[PM-28264] Consolidate and update the UI for key connector migration/confirmation (#17642)
* Consolidate the RemovePasswordComponent * Add getting confirmation details for confirm key connector * Add missing message
This commit is contained in:
@@ -3252,9 +3252,6 @@
|
|||||||
"copyCustomFieldNameNotUnique": {
|
"copyCustomFieldNameNotUnique": {
|
||||||
"message": "No unique identifier found."
|
"message": "No unique identifier found."
|
||||||
},
|
},
|
||||||
"removeMasterPasswordForOrganizationUserKeyConnector": {
|
|
||||||
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
|
|
||||||
},
|
|
||||||
"organizationName": {
|
"organizationName": {
|
||||||
"message": "Organization name"
|
"message": "Organization name"
|
||||||
},
|
},
|
||||||
@@ -5891,6 +5888,45 @@
|
|||||||
"cardNumberLabel": {
|
"cardNumberLabel": {
|
||||||
"message": "Card number"
|
"message": "Card number"
|
||||||
},
|
},
|
||||||
|
"removeMasterPasswordForOrgUserKeyConnector":{
|
||||||
|
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
|
||||||
|
},
|
||||||
|
"continueWithLogIn": {
|
||||||
|
"message": "Continue with log in"
|
||||||
|
},
|
||||||
|
"doNotContinue": {
|
||||||
|
"message": "Do not continue"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"message": "Domain"
|
||||||
|
},
|
||||||
|
"keyConnectorDomainTooltip": {
|
||||||
|
"message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin."
|
||||||
|
},
|
||||||
|
"verifyYourOrganization": {
|
||||||
|
"message": "Verify your organization to log in"
|
||||||
|
},
|
||||||
|
"organizationVerified":{
|
||||||
|
"message": "Organization verified"
|
||||||
|
},
|
||||||
|
"domainVerified":{
|
||||||
|
"message": "Domain verified"
|
||||||
|
},
|
||||||
|
"leaveOrganizationContent": {
|
||||||
|
"message": "If you don't verify your organization, your access to the organization will be revoked."
|
||||||
|
},
|
||||||
|
"leaveNow": {
|
||||||
|
"message": "Leave now"
|
||||||
|
},
|
||||||
|
"verifyYourDomainToLogin": {
|
||||||
|
"message": "Verify your domain to log in"
|
||||||
|
},
|
||||||
|
"verifyYourDomainDescription": {
|
||||||
|
"message": "To continue with log in, verify this domain."
|
||||||
|
},
|
||||||
|
"confirmKeyConnectorOrganizationUserDescription": {
|
||||||
|
"message": "To continue with log in, verify the organization and domain."
|
||||||
|
},
|
||||||
"sessionTimeoutSettingsAction": {
|
"sessionTimeoutSettingsAction": {
|
||||||
"message": "Timeout action"
|
"message": "Timeout action"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
<popup-page>
|
|
||||||
<popup-header slot="header" pageTitle="{{ 'removeMasterPassword' | i18n }}">
|
|
||||||
<ng-container slot="end">
|
|
||||||
<app-pop-out></app-pop-out>
|
|
||||||
</ng-container>
|
|
||||||
</popup-header>
|
|
||||||
|
|
||||||
@if (loading) {
|
|
||||||
<div class="tw-text-center">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<p>{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "organizationName" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.name }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "keyConnectorDomain" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.keyConnectorUrl }}</p>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
bitButton
|
|
||||||
buttonType="primary"
|
|
||||||
block
|
|
||||||
(click)="convert()"
|
|
||||||
[disabled]="action"
|
|
||||||
class="tw-mb-2"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
*ngIf="continuing"
|
|
||||||
></i>
|
|
||||||
{{ "removeMasterPassword" | i18n }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button" bitButton block (click)="leave()" [disabled]="action">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
*ngIf="leaving"
|
|
||||||
></i>
|
|
||||||
{{ "leaveOrganization" | i18n }}
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</popup-page>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// FIXME (PM-22628): angular imports are forbidden in background
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
|
||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui";
|
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
|
||||||
selector: "app-remove-password",
|
|
||||||
templateUrl: "remove-password.component.html",
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
|
||||||
@@ -43,7 +43,11 @@ import {
|
|||||||
TwoFactorAuthGuard,
|
TwoFactorAuthGuard,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||||
import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui";
|
import {
|
||||||
|
LockComponent,
|
||||||
|
ConfirmKeyConnectorDomainComponent,
|
||||||
|
RemovePasswordComponent,
|
||||||
|
} from "@bitwarden/key-management-ui";
|
||||||
|
|
||||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||||
import { AuthExtensionRoute } from "../auth/popup/constants/auth-extension-route.constant";
|
import { AuthExtensionRoute } from "../auth/popup/constants/auth-extension-route.constant";
|
||||||
@@ -59,7 +63,6 @@ import { NotificationsSettingsComponent } from "../autofill/popup/settings/notif
|
|||||||
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
||||||
import { PhishingWarning } from "../dirt/phishing-detection/popup/phishing-warning.component";
|
import { PhishingWarning } from "../dirt/phishing-detection/popup/phishing-warning.component";
|
||||||
import { ProtectedByComponent } from "../dirt/phishing-detection/popup/protected-by-component";
|
import { ProtectedByComponent } from "../dirt/phishing-detection/popup/protected-by-component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
|
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
|
||||||
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
||||||
import { RouteCacheOptions } from "../platform/services/popup-view-cache-background.service";
|
import { RouteCacheOptions } from "../platform/services/popup-view-cache-background.service";
|
||||||
@@ -188,9 +191,22 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "remove-password",
|
path: "remove-password",
|
||||||
component: RemovePasswordComponent,
|
component: ExtensionAnonLayoutWrapperComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: RemovePasswordComponent,
|
||||||
|
data: {
|
||||||
|
pageTitle: {
|
||||||
|
key: "verifyYourOrganization",
|
||||||
|
},
|
||||||
|
showBackButton: false,
|
||||||
|
pageIcon: LockIcon,
|
||||||
|
} satisfies ExtensionAnonLayoutWrapperData,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "view-cipher",
|
path: "view-cipher",
|
||||||
@@ -646,7 +662,7 @@ const routes: Routes = [
|
|||||||
component: ConfirmKeyConnectorDomainComponent,
|
component: ConfirmKeyConnectorDomainComponent,
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "confirmKeyConnectorDomain",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
pageIcon: DomainIcon,
|
pageIcon: DomainIcon,
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { CurrentAccountComponent } from "../auth/popup/account-switching/current
|
|||||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||||
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
||||||
import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component";
|
import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component";
|
||||||
import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.component";
|
||||||
@@ -85,13 +84,7 @@ import "../platform/popup/locales";
|
|||||||
CalloutModule,
|
CalloutModule,
|
||||||
LinkModule,
|
LinkModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [AppComponent, ColorPasswordPipe, ColorPasswordCountPipe, TabsV2Component],
|
||||||
AppComponent,
|
|
||||||
ColorPasswordPipe,
|
|
||||||
ColorPasswordCountPipe,
|
|
||||||
TabsV2Component,
|
|
||||||
RemovePasswordComponent,
|
|
||||||
],
|
|
||||||
exports: [CalloutModule],
|
exports: [CalloutModule],
|
||||||
providers: [CurrencyPipe, DatePipe],
|
providers: [CurrencyPipe, DatePipe],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
|||||||
@@ -42,14 +42,17 @@ import {
|
|||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||||
import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui";
|
import {
|
||||||
|
LockComponent,
|
||||||
|
ConfirmKeyConnectorDomainComponent,
|
||||||
|
RemovePasswordComponent,
|
||||||
|
} from "@bitwarden/key-management-ui";
|
||||||
|
|
||||||
import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard";
|
import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard";
|
||||||
import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard";
|
import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard";
|
||||||
import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component";
|
import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component";
|
||||||
import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component";
|
import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component";
|
||||||
import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component";
|
import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
||||||
import { VaultComponent } from "../vault/app/vault-v3/vault.component";
|
import { VaultComponent } from "../vault/app/vault-v3/vault.component";
|
||||||
|
|
||||||
@@ -117,11 +120,6 @@ const routes: Routes = [
|
|||||||
component: SendComponent,
|
component: SendComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "remove-password",
|
|
||||||
component: RemovePasswordComponent,
|
|
||||||
canActivate: [authGuard],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "fido2-assertion",
|
path: "fido2-assertion",
|
||||||
component: Fido2VaultComponent,
|
component: Fido2VaultComponent,
|
||||||
@@ -327,13 +325,24 @@ const routes: Routes = [
|
|||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "remove-password",
|
||||||
|
component: RemovePasswordComponent,
|
||||||
|
canActivate: [authGuard],
|
||||||
|
data: {
|
||||||
|
pageTitle: {
|
||||||
|
key: "verifyYourOrganization",
|
||||||
|
},
|
||||||
|
pageIcon: LockIcon,
|
||||||
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "confirm-key-connector-domain",
|
path: "confirm-key-connector-domain",
|
||||||
component: ConfirmKeyConnectorDomainComponent,
|
component: ConfirmKeyConnectorDomainComponent,
|
||||||
canActivate: [],
|
canActivate: [],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "confirmKeyConnectorDomain",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
pageIcon: DomainIcon,
|
pageIcon: DomainIcon,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { DeleteAccountComponent } from "../auth/delete-account.component";
|
|||||||
import { LoginModule } from "../auth/login/login.module";
|
import { LoginModule } from "../auth/login/login.module";
|
||||||
import { SshAgentService } from "../autofill/services/ssh-agent.service";
|
import { SshAgentService } from "../autofill/services/ssh-agent.service";
|
||||||
import { PremiumComponent } from "../billing/app/accounts/premium.component";
|
import { PremiumComponent } from "../billing/app/accounts/premium.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
||||||
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
||||||
|
|
||||||
@@ -50,7 +49,6 @@ import { SharedModule } from "./shared/shared.module";
|
|||||||
ColorPasswordCountPipe,
|
ColorPasswordCountPipe,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
PremiumComponent,
|
PremiumComponent,
|
||||||
RemovePasswordComponent,
|
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
],
|
],
|
||||||
providers: [SshAgentService],
|
providers: [SshAgentService],
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<div id="remove-password-page" *ngIf="!loading">
|
|
||||||
<div class="content">
|
|
||||||
<h1>{{ "removeMasterPassword" | i18n }}</h1>
|
|
||||||
<p>{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "organizationName" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.name }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "keyConnectorDomain" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.keyConnectorUrl }}</p>
|
|
||||||
<div class="buttons">
|
|
||||||
<button type="submit" class="btn primary block" [disabled]="action" (click)="convert()">
|
|
||||||
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn secondary block" [disabled]="action" (click)="leave()">
|
|
||||||
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui";
|
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
|
||||||
selector: "app-remove-password",
|
|
||||||
templateUrl: "remove-password.component.html",
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
|
||||||
@@ -2637,9 +2637,6 @@
|
|||||||
"removedMasterPassword": {
|
"removedMasterPassword": {
|
||||||
"message": "Master password removed"
|
"message": "Master password removed"
|
||||||
},
|
},
|
||||||
"removeMasterPasswordForOrganizationUserKeyConnector": {
|
|
||||||
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
|
|
||||||
},
|
|
||||||
"organizationName": {
|
"organizationName": {
|
||||||
"message": "Organization name"
|
"message": "Organization name"
|
||||||
},
|
},
|
||||||
@@ -4337,6 +4334,45 @@
|
|||||||
"upgradeToPremium": {
|
"upgradeToPremium": {
|
||||||
"message": "Upgrade to Premium"
|
"message": "Upgrade to Premium"
|
||||||
},
|
},
|
||||||
|
"removeMasterPasswordForOrgUserKeyConnector":{
|
||||||
|
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
|
||||||
|
},
|
||||||
|
"continueWithLogIn": {
|
||||||
|
"message": "Continue with log in"
|
||||||
|
},
|
||||||
|
"doNotContinue": {
|
||||||
|
"message": "Do not continue"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"message": "Domain"
|
||||||
|
},
|
||||||
|
"keyConnectorDomainTooltip": {
|
||||||
|
"message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin."
|
||||||
|
},
|
||||||
|
"verifyYourOrganization": {
|
||||||
|
"message": "Verify your organization to log in"
|
||||||
|
},
|
||||||
|
"organizationVerified":{
|
||||||
|
"message": "Organization verified"
|
||||||
|
},
|
||||||
|
"domainVerified":{
|
||||||
|
"message": "Domain verified"
|
||||||
|
},
|
||||||
|
"leaveOrganizationContent": {
|
||||||
|
"message": "If you don't verify your organization, your access to the organization will be revoked."
|
||||||
|
},
|
||||||
|
"leaveNow": {
|
||||||
|
"message": "Leave now"
|
||||||
|
},
|
||||||
|
"verifyYourDomainToLogin": {
|
||||||
|
"message": "Verify your domain to log in"
|
||||||
|
},
|
||||||
|
"verifyYourDomainDescription": {
|
||||||
|
"message": "To continue with log in, verify this domain."
|
||||||
|
},
|
||||||
|
"confirmKeyConnectorOrganizationUserDescription": {
|
||||||
|
"message": "To continue with log in, verify the organization and domain."
|
||||||
|
},
|
||||||
"sessionTimeoutSettingsAction": {
|
"sessionTimeoutSettingsAction": {
|
||||||
"message": "Timeout action"
|
"message": "Timeout action"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
<div *ngIf="loading" class="tw-text-center">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loading">
|
|
||||||
<p>{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "organizationName" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.name }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "keyConnectorDomain" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.keyConnectorUrl }}</p>
|
|
||||||
|
|
||||||
<button
|
|
||||||
bitButton
|
|
||||||
type="button"
|
|
||||||
buttonType="primary"
|
|
||||||
class="tw-w-full tw-mb-2"
|
|
||||||
[bitAction]="convert"
|
|
||||||
[block]="true"
|
|
||||||
>
|
|
||||||
{{ "removeMasterPassword" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
bitButton
|
|
||||||
type="button"
|
|
||||||
buttonType="secondary"
|
|
||||||
class="tw-w-full"
|
|
||||||
[bitAction]="leave"
|
|
||||||
[block]="true"
|
|
||||||
>
|
|
||||||
{{ "leaveOrganization" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui";
|
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
|
||||||
selector: "app-remove-password",
|
|
||||||
templateUrl: "remove-password.component.html",
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
|
||||||
@@ -51,7 +51,7 @@ import {
|
|||||||
import { canAccessEmergencyAccess } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { canAccessEmergencyAccess } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||||
import { LockComponent } from "@bitwarden/key-management-ui";
|
import { LockComponent, RemovePasswordComponent } from "@bitwarden/key-management-ui";
|
||||||
import { premiumInterestRedirectGuard } from "@bitwarden/web-vault/app/vault/guards/premium-interest-redirect/premium-interest-redirect.guard";
|
import { premiumInterestRedirectGuard } from "@bitwarden/web-vault/app/vault/guards/premium-interest-redirect/premium-interest-redirect.guard";
|
||||||
|
|
||||||
import { flagEnabled, Flags } from "../utils/flags";
|
import { flagEnabled, Flags } from "../utils/flags";
|
||||||
@@ -80,7 +80,6 @@ import { RouteDataProperties } from "./core";
|
|||||||
import { ReportsModule } from "./dirt/reports";
|
import { ReportsModule } from "./dirt/reports";
|
||||||
import { DataRecoveryComponent } from "./key-management/data-recovery/data-recovery.component";
|
import { DataRecoveryComponent } from "./key-management/data-recovery/data-recovery.component";
|
||||||
import { ConfirmKeyConnectorDomainComponent } from "./key-management/key-connector/confirm-key-connector-domain.component";
|
import { ConfirmKeyConnectorDomainComponent } from "./key-management/key-connector/confirm-key-connector-domain.component";
|
||||||
import { RemovePasswordComponent } from "./key-management/key-connector/remove-password.component";
|
|
||||||
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
|
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
|
||||||
import { UserLayoutComponent } from "./layouts/user-layout.component";
|
import { UserLayoutComponent } from "./layouts/user-layout.component";
|
||||||
import { RequestSMAccessComponent } from "./secrets-manager/secrets-manager-landing/request-sm-access.component";
|
import { RequestSMAccessComponent } from "./secrets-manager/secrets-manager-landing/request-sm-access.component";
|
||||||
@@ -545,9 +544,9 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "removeMasterPassword",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
titleId: "removeMasterPassword",
|
titleId: "verifyYourOrganization",
|
||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
@@ -557,9 +556,9 @@ const routes: Routes = [
|
|||||||
canActivate: [],
|
canActivate: [],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "confirmKeyConnectorDomain",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
titleId: "confirmKeyConnectorDomain",
|
titleId: "verifyYourOrganization",
|
||||||
pageIcon: DomainIcon,
|
pageIcon: DomainIcon,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.comp
|
|||||||
import { FreeBitwardenFamiliesComponent } from "../billing/members/free-bitwarden-families.component";
|
import { FreeBitwardenFamiliesComponent } from "../billing/members/free-bitwarden-families.component";
|
||||||
import { SponsoredFamiliesComponent } from "../billing/settings/sponsored-families.component";
|
import { SponsoredFamiliesComponent } from "../billing/settings/sponsored-families.component";
|
||||||
import { SponsoringOrgRowComponent } from "../billing/settings/sponsoring-org-row.component";
|
import { SponsoringOrgRowComponent } from "../billing/settings/sponsoring-org-row.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { HeaderModule } from "../layouts/header/header.module";
|
import { HeaderModule } from "../layouts/header/header.module";
|
||||||
import { OrganizationBadgeModule } from "../vault/individual-vault/organization-badge/organization-badge.module";
|
import { OrganizationBadgeModule } from "../vault/individual-vault/organization-badge/organization-badge.module";
|
||||||
import { PipesModule } from "../vault/individual-vault/pipes/pipes.module";
|
import { PipesModule } from "../vault/individual-vault/pipes/pipes.module";
|
||||||
@@ -21,7 +20,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
declarations: [
|
declarations: [
|
||||||
RecoverDeleteComponent,
|
RecoverDeleteComponent,
|
||||||
RecoverTwoFactorComponent,
|
RecoverTwoFactorComponent,
|
||||||
RemovePasswordComponent,
|
|
||||||
SponsoredFamiliesComponent,
|
SponsoredFamiliesComponent,
|
||||||
FreeBitwardenFamiliesComponent,
|
FreeBitwardenFamiliesComponent,
|
||||||
SponsoringOrgRowComponent,
|
SponsoringOrgRowComponent,
|
||||||
@@ -31,7 +29,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
exports: [
|
exports: [
|
||||||
RecoverDeleteComponent,
|
RecoverDeleteComponent,
|
||||||
RecoverTwoFactorComponent,
|
RecoverTwoFactorComponent,
|
||||||
RemovePasswordComponent,
|
|
||||||
SponsoredFamiliesComponent,
|
SponsoredFamiliesComponent,
|
||||||
VerifyEmailTokenComponent,
|
VerifyEmailTokenComponent,
|
||||||
VerifyRecoverDeleteComponent,
|
VerifyRecoverDeleteComponent,
|
||||||
|
|||||||
@@ -7136,9 +7136,6 @@
|
|||||||
"invalidVerificationCode": {
|
"invalidVerificationCode": {
|
||||||
"message": "Invalid verification code"
|
"message": "Invalid verification code"
|
||||||
},
|
},
|
||||||
"removeMasterPasswordForOrganizationUserKeyConnector": {
|
|
||||||
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
|
|
||||||
},
|
|
||||||
"keyConnectorDomain": {
|
"keyConnectorDomain": {
|
||||||
"message": "Key Connector domain"
|
"message": "Key Connector domain"
|
||||||
},
|
},
|
||||||
@@ -12247,6 +12244,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"removeMasterPasswordForOrgUserKeyConnector":{
|
||||||
|
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
|
||||||
|
},
|
||||||
|
"continueWithLogIn": {
|
||||||
|
"message": "Continue with log in"
|
||||||
|
},
|
||||||
|
"doNotContinue": {
|
||||||
|
"message": "Do not continue"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"message": "Domain"
|
||||||
|
},
|
||||||
|
"keyConnectorDomainTooltip": {
|
||||||
|
"message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin."
|
||||||
|
},
|
||||||
|
"verifyYourOrganization": {
|
||||||
|
"message": "Verify your organization to log in"
|
||||||
|
},
|
||||||
|
"organizationVerified":{
|
||||||
|
"message": "Organization verified"
|
||||||
|
},
|
||||||
|
"domainVerified":{
|
||||||
|
"message": "Domain verified"
|
||||||
|
},
|
||||||
|
"leaveOrganizationContent": {
|
||||||
|
"message": "If you don't verify your organization, your access to the organization will be revoked."
|
||||||
|
},
|
||||||
|
"leaveNow": {
|
||||||
|
"message": "Leave now"
|
||||||
|
},
|
||||||
|
"verifyYourDomainToLogin": {
|
||||||
|
"message": "Verify your domain to log in"
|
||||||
|
},
|
||||||
|
"verifyYourDomainDescription": {
|
||||||
|
"message": "To continue with log in, verify this domain."
|
||||||
|
},
|
||||||
|
"confirmKeyConnectorOrganizationUserDescription": {
|
||||||
|
"message": "To continue with log in, verify the organization and domain."
|
||||||
|
},
|
||||||
"confirmNoSelectedCriticalApplicationsTitle": {
|
"confirmNoSelectedCriticalApplicationsTitle": {
|
||||||
"message": "No critical applications are selected"
|
"message": "No critical applications are selected"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -184,7 +184,9 @@ import { DefaultChangeKdfApiService } from "@bitwarden/common/key-management/kdf
|
|||||||
import { ChangeKdfApiService } from "@bitwarden/common/key-management/kdf/change-kdf-api.service.abstraction";
|
import { ChangeKdfApiService } from "@bitwarden/common/key-management/kdf/change-kdf-api.service.abstraction";
|
||||||
import { DefaultChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf.service";
|
import { DefaultChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf.service";
|
||||||
import { ChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf.service.abstraction";
|
import { ChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf.service.abstraction";
|
||||||
|
import { KeyConnectorApiService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector-api.service";
|
||||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
|
import { DefaultKeyConnectorApiService } from "@bitwarden/common/key-management/key-connector/services/default-key-connector-api.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
|
||||||
import { KeyApiService } from "@bitwarden/common/key-management/keys/services/abstractions/key-api-service.abstraction";
|
import { KeyApiService } from "@bitwarden/common/key-management/keys/services/abstractions/key-api-service.abstraction";
|
||||||
import { RotateableKeySetService } from "@bitwarden/common/key-management/keys/services/abstractions/rotateable-key-set.service";
|
import { RotateableKeySetService } from "@bitwarden/common/key-management/keys/services/abstractions/rotateable-key-set.service";
|
||||||
@@ -1835,6 +1837,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: IpcSessionRepository,
|
useClass: IpcSessionRepository,
|
||||||
deps: [StateProvider],
|
deps: [StateProvider],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: KeyConnectorApiService,
|
||||||
|
useClass: DefaultKeyConnectorApiService,
|
||||||
|
deps: [ApiServiceAbstraction],
|
||||||
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: PremiumInterestStateService,
|
provide: PremiumInterestStateService,
|
||||||
useClass: NoopPremiumInterestStateService,
|
useClass: NoopPremiumInterestStateService,
|
||||||
|
|||||||
@@ -421,6 +421,7 @@ describe("TwoFactorAuthComponent", () => {
|
|||||||
keyConnectorUrl:
|
keyConnectorUrl:
|
||||||
mockUserDecryptionOpts.noMasterPasswordWithKeyConnector.keyConnectorOption!
|
mockUserDecryptionOpts.noMasterPasswordWithKeyConnector.keyConnectorOption!
|
||||||
.keyConnectorUrl,
|
.keyConnectorUrl,
|
||||||
|
organizationSsoIdentifier: "test-sso-id",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const authResult = new AuthResult();
|
const authResult = new AuthResult();
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
|
||||||
|
|
||||||
|
export abstract class KeyConnectorApiService {
|
||||||
|
abstract getConfirmationDetails(
|
||||||
|
orgSsoIdentifier: string,
|
||||||
|
): Promise<KeyConnectorConfirmationDetailsResponse>;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export interface KeyConnectorDomainConfirmation {
|
export interface KeyConnectorDomainConfirmation {
|
||||||
keyConnectorUrl: string;
|
keyConnectorUrl: string;
|
||||||
|
organizationSsoIdentifier: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { BaseResponse } from "../../../../models/response/base.response";
|
||||||
|
|
||||||
|
export class KeyConnectorConfirmationDetailsResponse extends BaseResponse {
|
||||||
|
organizationName: string;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
this.organizationName = this.getResponseProperty("OrganizationName");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { ApiService } from "../../../abstractions/api.service";
|
||||||
|
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
|
||||||
|
|
||||||
|
import { DefaultKeyConnectorApiService } from "./default-key-connector-api.service";
|
||||||
|
|
||||||
|
describe("DefaultKeyConnectorApiService", () => {
|
||||||
|
let apiService: MockProxy<ApiService>;
|
||||||
|
let sut: DefaultKeyConnectorApiService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
apiService = mock<ApiService>();
|
||||||
|
sut = new DefaultKeyConnectorApiService(apiService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getConfirmationDetails", () => {
|
||||||
|
it("encodes orgSsoIdentifier in URL", async () => {
|
||||||
|
const orgSsoIdentifier = "test org/with special@chars";
|
||||||
|
const expectedEncodedIdentifier = encodeURIComponent(orgSsoIdentifier);
|
||||||
|
const mockResponse = {};
|
||||||
|
apiService.send.mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
await sut.getConfirmationDetails(orgSsoIdentifier);
|
||||||
|
|
||||||
|
expect(apiService.send).toHaveBeenCalledWith(
|
||||||
|
"GET",
|
||||||
|
`/accounts/key-connector/confirmation-details/${expectedEncodedIdentifier}`,
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns expected response", async () => {
|
||||||
|
const orgSsoIdentifier = "test-org";
|
||||||
|
const expectedOrgName = "example";
|
||||||
|
const mockResponse = { OrganizationName: expectedOrgName };
|
||||||
|
apiService.send.mockResolvedValue(mockResponse);
|
||||||
|
|
||||||
|
const result = await sut.getConfirmationDetails(orgSsoIdentifier);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(KeyConnectorConfirmationDetailsResponse);
|
||||||
|
expect(result.organizationName).toBe(expectedOrgName);
|
||||||
|
expect(apiService.send).toHaveBeenCalledWith(
|
||||||
|
"GET",
|
||||||
|
"/accounts/key-connector/confirmation-details/test-org",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { ApiService } from "../../../abstractions/api.service";
|
||||||
|
import { KeyConnectorApiService } from "../abstractions/key-connector-api.service";
|
||||||
|
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
|
||||||
|
|
||||||
|
export class DefaultKeyConnectorApiService implements KeyConnectorApiService {
|
||||||
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
|
async getConfirmationDetails(
|
||||||
|
orgSsoIdentifier: string,
|
||||||
|
): Promise<KeyConnectorConfirmationDetailsResponse> {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/accounts/key-connector/confirmation-details/" + encodeURIComponent(orgSsoIdentifier),
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return new KeyConnectorConfirmationDetailsResponse(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -603,7 +603,10 @@ describe("KeyConnectorService", () => {
|
|||||||
const data$ = keyConnectorService.requiresDomainConfirmation$(mockUserId);
|
const data$ = keyConnectorService.requiresDomainConfirmation$(mockUserId);
|
||||||
const data = await firstValueFrom(data$);
|
const data = await firstValueFrom(data$);
|
||||||
|
|
||||||
expect(data).toEqual({ keyConnectorUrl: conversion.keyConnectorUrl });
|
expect(data).toEqual({
|
||||||
|
keyConnectorUrl: conversion.keyConnectorUrl,
|
||||||
|
organizationSsoIdentifier: conversion.organizationId,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return observable of null value when no data is set", async () => {
|
it("should return observable of null value when no data is set", async () => {
|
||||||
|
|||||||
@@ -202,9 +202,16 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requiresDomainConfirmation$(userId: UserId): Observable<KeyConnectorDomainConfirmation | null> {
|
requiresDomainConfirmation$(userId: UserId): Observable<KeyConnectorDomainConfirmation | null> {
|
||||||
return this.stateProvider
|
return this.stateProvider.getUserState$(NEW_SSO_USER_KEY_CONNECTOR_CONVERSION, userId).pipe(
|
||||||
.getUserState$(NEW_SSO_USER_KEY_CONNECTOR_CONVERSION, userId)
|
map((data) =>
|
||||||
.pipe(map((data) => (data != null ? { keyConnectorUrl: data.keyConnectorUrl } : null)));
|
data != null
|
||||||
|
? {
|
||||||
|
keyConnectorUrl: data.keyConnectorUrl,
|
||||||
|
organizationSsoIdentifier: data.organizationId,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleKeyConnectorError(e: any) {
|
private handleKeyConnectorError(e: any) {
|
||||||
|
|||||||
@@ -8,17 +8,34 @@
|
|||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="tw-mb-4">
|
@if (organizationName) {
|
||||||
<p class="tw-mb-1 tw-text-sm tw-font-medium">{{ "keyConnectorDomain" | i18n }}:</p>
|
<p>{{ "confirmKeyConnectorOrganizationUserDescription" | i18n }}</p>
|
||||||
<p class="tw-text-muted tw-break-all">{{ keyConnectorUrl }}</p>
|
|
||||||
</div>
|
<p class="tw-mb-0 tw-font-bold">{{ "organization" | i18n }}</p>
|
||||||
|
<p class="tw-mb-6">{{ organizationName }}</p>
|
||||||
|
} @else {
|
||||||
|
<p>{{ "verifyYourDomainDescription" | i18n }}</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<p class="tw-mb-0 tw-font-bold tw-inline-flex tw-items-center">
|
||||||
|
{{ "domain" | i18n }}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
[label]="'keyConnectorDomainTooltip' | i18n"
|
||||||
|
tooltipPosition="above-center"
|
||||||
|
bitIconButton="bwi-info-circle"
|
||||||
|
size="small"
|
||||||
|
></button>
|
||||||
|
</p>
|
||||||
|
<p class="tw-mb-6 tw-font-mono">{{ keyConnectorHostName }}</p>
|
||||||
|
|
||||||
<div class="tw-flex tw-flex-col tw-gap-2">
|
<div class="tw-flex tw-flex-col tw-gap-2">
|
||||||
<button bitButton type="button" buttonType="primary" [bitAction]="confirm" [block]="true">
|
<button bitButton type="button" buttonType="primary" [bitAction]="confirm" [block]="true">
|
||||||
{{ "confirm" | i18n }}
|
{{ "continueWithLogIn" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button bitButton type="button" buttonType="secondary" [bitAction]="cancel" [block]="true">
|
<button bitButton type="button" buttonType="secondary" [bitAction]="cancel" [block]="true">
|
||||||
{{ "cancel" | i18n }}
|
{{ "doNotContinue" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,17 @@ import { Router } from "@angular/router";
|
|||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
|
import { KeyConnectorApiService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector-api.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
import { KeyConnectorDomainConfirmation } from "@bitwarden/common/key-management/key-connector/models/key-connector-domain-confirmation";
|
import { KeyConnectorDomainConfirmation } from "@bitwarden/common/key-management/key-connector/models/key-connector-domain-confirmation";
|
||||||
|
import { KeyConnectorConfirmationDetailsResponse } from "@bitwarden/common/key-management/key-connector/models/response/key-connector-confirmation-details.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||||
import { mockAccountServiceWith } from "@bitwarden/common/spec";
|
import { mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { AnonLayoutWrapperDataService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { ConfirmKeyConnectorDomainComponent } from "./confirm-key-connector-domain.component";
|
import { ConfirmKeyConnectorDomainComponent } from "./confirm-key-connector-domain.component";
|
||||||
|
|
||||||
@@ -16,8 +20,10 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
let component: ConfirmKeyConnectorDomainComponent;
|
let component: ConfirmKeyConnectorDomainComponent;
|
||||||
|
|
||||||
const userId = "test-user-id" as UserId;
|
const userId = "test-user-id" as UserId;
|
||||||
|
const expectedHostName = "key-connector-url.com";
|
||||||
const confirmation: KeyConnectorDomainConfirmation = {
|
const confirmation: KeyConnectorDomainConfirmation = {
|
||||||
keyConnectorUrl: "https://key-connector-url.com",
|
keyConnectorUrl: "https://key-connector-url.com",
|
||||||
|
organizationSsoIdentifier: "org-sso-identifier",
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockRouter = mock<Router>();
|
const mockRouter = mock<Router>();
|
||||||
@@ -25,6 +31,10 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
const mockKeyConnectorService = mock<KeyConnectorService>();
|
const mockKeyConnectorService = mock<KeyConnectorService>();
|
||||||
const mockLogService = mock<LogService>();
|
const mockLogService = mock<LogService>();
|
||||||
const mockMessagingService = mock<MessagingService>();
|
const mockMessagingService = mock<MessagingService>();
|
||||||
|
const mockKeyConnectorApiService = mock<KeyConnectorApiService>();
|
||||||
|
const mockToastService = mock<ToastService>();
|
||||||
|
const mockI18nService = mock<I18nService>();
|
||||||
|
const mockAnonLayoutWrapperDataService = mock<AnonLayoutWrapperDataService>();
|
||||||
let mockAccountService = mockAccountServiceWith(userId);
|
let mockAccountService = mockAccountServiceWith(userId);
|
||||||
const onBeforeNavigation = jest.fn();
|
const onBeforeNavigation = jest.fn();
|
||||||
|
|
||||||
@@ -33,6 +43,8 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
|
|
||||||
mockAccountService = mockAccountServiceWith(userId);
|
mockAccountService = mockAccountServiceWith(userId);
|
||||||
|
|
||||||
|
mockI18nService.t.mockImplementation((key) => `${key}-used-i18n`);
|
||||||
|
|
||||||
component = new ConfirmKeyConnectorDomainComponent(
|
component = new ConfirmKeyConnectorDomainComponent(
|
||||||
mockRouter,
|
mockRouter,
|
||||||
mockLogService,
|
mockLogService,
|
||||||
@@ -40,6 +52,10 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
mockMessagingService,
|
mockMessagingService,
|
||||||
mockSyncService,
|
mockSyncService,
|
||||||
mockAccountService,
|
mockAccountService,
|
||||||
|
mockKeyConnectorApiService,
|
||||||
|
mockToastService,
|
||||||
|
mockI18nService,
|
||||||
|
mockAnonLayoutWrapperDataService,
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(component, "onBeforeNavigation").mockImplementation(onBeforeNavigation);
|
jest.spyOn(component, "onBeforeNavigation").mockImplementation(onBeforeNavigation);
|
||||||
@@ -67,17 +83,41 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
expect(component.loading).toEqual(true);
|
expect(component.loading).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets organization name to undefined when getOrganizationName throws error", async () => {
|
||||||
|
mockKeyConnectorApiService.getConfirmationDetails.mockRejectedValue(new Error("API error"));
|
||||||
|
|
||||||
|
await component.ngOnInit();
|
||||||
|
|
||||||
|
expect(component.organizationName).toBeUndefined();
|
||||||
|
expect(component.userId).toEqual(userId);
|
||||||
|
expect(component.keyConnectorUrl).toEqual(confirmation.keyConnectorUrl);
|
||||||
|
expect(component.keyConnectorHostName).toEqual(expectedHostName);
|
||||||
|
expect(component.loading).toEqual(false);
|
||||||
|
expect(mockAnonLayoutWrapperDataService.setAnonLayoutWrapperData).toHaveBeenCalledWith({
|
||||||
|
pageTitle: { key: "verifyYourDomainToLogin" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should set component properties correctly", async () => {
|
it("should set component properties correctly", async () => {
|
||||||
|
const expectedOrgName = "Test Organization";
|
||||||
|
mockKeyConnectorApiService.getConfirmationDetails.mockResolvedValue({
|
||||||
|
organizationName: expectedOrgName,
|
||||||
|
} as KeyConnectorConfirmationDetailsResponse);
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
expect(component.userId).toEqual(userId);
|
expect(component.userId).toEqual(userId);
|
||||||
|
expect(component.organizationName).toEqual(expectedOrgName);
|
||||||
expect(component.keyConnectorUrl).toEqual(confirmation.keyConnectorUrl);
|
expect(component.keyConnectorUrl).toEqual(confirmation.keyConnectorUrl);
|
||||||
|
expect(component.keyConnectorHostName).toEqual(expectedHostName);
|
||||||
expect(component.loading).toEqual(false);
|
expect(component.loading).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("confirm", () => {
|
describe("confirm", () => {
|
||||||
it("should call keyConnectorService.convertNewSsoUserToKeyConnector with full sync and navigation to home page", async () => {
|
it("calls domain verified toast when organization name is not set", async () => {
|
||||||
|
mockKeyConnectorApiService.getConfirmationDetails.mockRejectedValue(new Error("API error"));
|
||||||
|
|
||||||
await component.ngOnInit();
|
await component.ngOnInit();
|
||||||
|
|
||||||
await component.confirm();
|
await component.confirm();
|
||||||
@@ -94,6 +134,43 @@ describe("ConfirmKeyConnectorDomainComponent", () => {
|
|||||||
expect(mockSyncService.fullSync.mock.invocationCallOrder[0]).toBeLessThan(
|
expect(mockSyncService.fullSync.mock.invocationCallOrder[0]).toBeLessThan(
|
||||||
mockMessagingService.send.mock.invocationCallOrder[0],
|
mockMessagingService.send.mock.invocationCallOrder[0],
|
||||||
);
|
);
|
||||||
|
expect(mockToastService.showToast).toHaveBeenCalledWith({
|
||||||
|
variant: "success",
|
||||||
|
message: "domainVerified-used-i18n",
|
||||||
|
});
|
||||||
|
expect(mockMessagingService.send.mock.invocationCallOrder[0]).toBeLessThan(
|
||||||
|
onBeforeNavigation.mock.invocationCallOrder[0],
|
||||||
|
);
|
||||||
|
expect(onBeforeNavigation.mock.invocationCallOrder[0]).toBeLessThan(
|
||||||
|
mockRouter.navigate.mock.invocationCallOrder[0],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call keyConnectorService.convertNewSsoUserToKeyConnector with full sync and navigation to home page", async () => {
|
||||||
|
mockKeyConnectorApiService.getConfirmationDetails.mockResolvedValue({
|
||||||
|
organizationName: "Test Org Name",
|
||||||
|
} as KeyConnectorConfirmationDetailsResponse);
|
||||||
|
|
||||||
|
await component.ngOnInit();
|
||||||
|
|
||||||
|
await component.confirm();
|
||||||
|
|
||||||
|
expect(mockKeyConnectorService.convertNewSsoUserToKeyConnector).toHaveBeenCalledWith(userId);
|
||||||
|
expect(mockSyncService.fullSync).toHaveBeenCalledWith(true);
|
||||||
|
expect(mockRouter.navigate).toHaveBeenCalledWith(["/"]);
|
||||||
|
expect(mockMessagingService.send).toHaveBeenCalledWith("loggedIn");
|
||||||
|
expect(onBeforeNavigation).toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mockKeyConnectorService.convertNewSsoUserToKeyConnector.mock.invocationCallOrder[0],
|
||||||
|
).toBeLessThan(mockSyncService.fullSync.mock.invocationCallOrder[0]);
|
||||||
|
expect(mockSyncService.fullSync.mock.invocationCallOrder[0]).toBeLessThan(
|
||||||
|
mockMessagingService.send.mock.invocationCallOrder[0],
|
||||||
|
);
|
||||||
|
expect(mockToastService.showToast).toHaveBeenCalledWith({
|
||||||
|
variant: "success",
|
||||||
|
message: "organizationVerified-used-i18n",
|
||||||
|
});
|
||||||
expect(mockMessagingService.send.mock.invocationCallOrder[0]).toBeLessThan(
|
expect(mockMessagingService.send.mock.invocationCallOrder[0]).toBeLessThan(
|
||||||
onBeforeNavigation.mock.invocationCallOrder[0],
|
onBeforeNavigation.mock.invocationCallOrder[0],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,12 +5,21 @@ import { firstValueFrom } from "rxjs";
|
|||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
|
import { KeyConnectorApiService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector-api.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { BitActionDirective, ButtonModule } from "@bitwarden/components";
|
import {
|
||||||
|
AnonLayoutWrapperDataService,
|
||||||
|
BitActionDirective,
|
||||||
|
ButtonModule,
|
||||||
|
IconButtonModule,
|
||||||
|
ToastService,
|
||||||
|
} from "@bitwarden/components";
|
||||||
import { I18nPipe } from "@bitwarden/ui-common";
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
@@ -19,11 +28,13 @@ import { I18nPipe } from "@bitwarden/ui-common";
|
|||||||
selector: "confirm-key-connector-domain",
|
selector: "confirm-key-connector-domain",
|
||||||
templateUrl: "confirm-key-connector-domain.component.html",
|
templateUrl: "confirm-key-connector-domain.component.html",
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, ButtonModule, I18nPipe, BitActionDirective],
|
imports: [CommonModule, ButtonModule, I18nPipe, BitActionDirective, IconButtonModule],
|
||||||
})
|
})
|
||||||
export class ConfirmKeyConnectorDomainComponent implements OnInit {
|
export class ConfirmKeyConnectorDomainComponent implements OnInit {
|
||||||
loading = true;
|
loading = true;
|
||||||
keyConnectorUrl!: string;
|
keyConnectorUrl!: string;
|
||||||
|
keyConnectorHostName!: string;
|
||||||
|
organizationName: string | undefined;
|
||||||
userId!: UserId;
|
userId!: UserId;
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||||
@@ -37,6 +48,10 @@ export class ConfirmKeyConnectorDomainComponent implements OnInit {
|
|||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
|
private keyConnectorApiService: KeyConnectorApiService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private anonLayoutWrapperDataService: AnonLayoutWrapperDataService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -57,14 +72,36 @@ export class ConfirmKeyConnectorDomainComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keyConnectorUrl = confirmation.keyConnectorUrl;
|
this.organizationName = await this.getOrganizationName(confirmation.organizationSsoIdentifier);
|
||||||
|
|
||||||
|
// PM-29133 Remove during cleanup.
|
||||||
|
if (this.organizationName == undefined) {
|
||||||
|
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
|
||||||
|
pageTitle: { key: "verifyYourDomainToLogin" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keyConnectorUrl = confirmation.keyConnectorUrl;
|
||||||
|
this.keyConnectorHostName = Utils.getHostname(confirmation.keyConnectorUrl);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm = async () => {
|
confirm = async () => {
|
||||||
await this.keyConnectorService.convertNewSsoUserToKeyConnector(this.userId);
|
await this.keyConnectorService.convertNewSsoUserToKeyConnector(this.userId);
|
||||||
|
|
||||||
|
if (this.organizationName) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
message: this.i18nService.t("organizationVerified"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// PM-29133 Remove during cleanup.
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
message: this.i18nService.t("domainVerified"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
|
|
||||||
this.messagingService.send("loggedIn");
|
this.messagingService.send("loggedIn");
|
||||||
@@ -77,4 +114,22 @@ export class ConfirmKeyConnectorDomainComponent implements OnInit {
|
|||||||
cancel = async () => {
|
cancel = async () => {
|
||||||
this.messagingService.send("logout");
|
this.messagingService.send("logout");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async getOrganizationName(
|
||||||
|
organizationSsoIdentifier: string,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const details =
|
||||||
|
await this.keyConnectorApiService.getConfirmationDetails(organizationSsoIdentifier);
|
||||||
|
return details.organizationName;
|
||||||
|
} catch (error) {
|
||||||
|
// PM-29133 Remove during cleanup.
|
||||||
|
// Old self hosted servers may not have this endpoint yet. On error log a warning and continue without organization name.
|
||||||
|
this.logService.warning(
|
||||||
|
`[ConfirmKeyConnectorDomainComponent] Unable to get key connector confirmation details for organizationSsoIdentifier ${organizationSsoIdentifier}:`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
@if (loading) {
|
||||||
|
<div class="tw-text-center">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<p>{{ "removeMasterPasswordForOrgUserKeyConnector" | i18n }}</p>
|
||||||
|
<p class="tw-mb-0 tw-font-bold">{{ "organization" | i18n }}</p>
|
||||||
|
<p class="tw-mb-6">{{ organization.name }}</p>
|
||||||
|
<p class="tw-mb-0 tw-font-bold tw-inline-flex tw-items-center">
|
||||||
|
{{ "domain" | i18n }}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
[label]="'keyConnectorDomainTooltip' | i18n"
|
||||||
|
tooltipPosition="above-center"
|
||||||
|
bitIconButton="bwi-info-circle"
|
||||||
|
size="small"
|
||||||
|
></button>
|
||||||
|
</p>
|
||||||
|
<p class="tw-mb-6 tw-font-mono">{{ keyConnectorHostName }}</p>
|
||||||
|
|
||||||
|
<div class="tw-flex tw-flex-col tw-gap-2">
|
||||||
|
<button bitButton type="button" buttonType="primary" [block]="true" [bitAction]="convert">
|
||||||
|
{{ "continueWithLogIn" | i18n }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button bitButton type="button" buttonType="secondary" [block]="true" [bitAction]="leave">
|
||||||
|
{{ "doNotContinue" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ describe("RemovePasswordComponent", () => {
|
|||||||
let component: RemovePasswordComponent;
|
let component: RemovePasswordComponent;
|
||||||
|
|
||||||
const userId = "test-user-id" as UserId;
|
const userId = "test-user-id" as UserId;
|
||||||
|
const expectedHostName = "key-connector-url.com";
|
||||||
const organization = {
|
const organization = {
|
||||||
id: "test-organization-id",
|
id: "test-organization-id",
|
||||||
name: "test-organization-name",
|
name: "test-organization-name",
|
||||||
@@ -62,6 +63,7 @@ describe("RemovePasswordComponent", () => {
|
|||||||
expect(component["activeUserId"]).toBe("test-user-id");
|
expect(component["activeUserId"]).toBe("test-user-id");
|
||||||
expect(component.organization).toEqual(organization);
|
expect(component.organization).toEqual(organization);
|
||||||
expect(component.loading).toEqual(false);
|
expect(component.loading).toEqual(false);
|
||||||
|
expect(component.keyConnectorHostName).toBe(expectedHostName);
|
||||||
|
|
||||||
expect(mockKeyConnectorService.getManagingOrganization).toHaveBeenCalledWith(userId);
|
expect(mockKeyConnectorService.getManagingOrganization).toHaveBeenCalledWith(userId);
|
||||||
expect(mockSyncService.fullSync).toHaveBeenCalledWith(false);
|
expect(mockSyncService.fullSync).toHaveBeenCalledWith(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Directive, OnInit } from "@angular/core";
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
@@ -9,17 +10,33 @@ import { KeyConnectorService } from "@bitwarden/common/key-management/key-connec
|
|||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import {
|
||||||
|
DialogService,
|
||||||
|
ToastService,
|
||||||
|
ButtonModule,
|
||||||
|
BitActionDirective,
|
||||||
|
IconButtonModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
@Directive()
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||||
|
@Component({
|
||||||
|
selector: "km-ui-remove-password",
|
||||||
|
templateUrl: "remove-password.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, ButtonModule, I18nPipe, BitActionDirective, IconButtonModule],
|
||||||
|
})
|
||||||
export class RemovePasswordComponent implements OnInit {
|
export class RemovePasswordComponent implements OnInit {
|
||||||
continuing = false;
|
continuing = false;
|
||||||
leaving = false;
|
leaving = false;
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
organization!: Organization;
|
organization!: Organization;
|
||||||
|
keyConnectorHostName!: string;
|
||||||
private activeUserId!: UserId;
|
private activeUserId!: UserId;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -55,6 +72,7 @@ export class RemovePasswordComponent implements OnInit {
|
|||||||
await this.router.navigate(["/"]);
|
await this.router.navigate(["/"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.keyConnectorHostName = Utils.getHostname(this.organization.keyConnectorUrl);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +91,7 @@ export class RemovePasswordComponent implements OnInit {
|
|||||||
|
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
message: this.i18nService.t("removedMasterPassword"),
|
message: this.i18nService.t("organizationVerified"),
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.router.navigate(["/"]);
|
await this.router.navigate(["/"]);
|
||||||
@@ -86,9 +104,11 @@ export class RemovePasswordComponent implements OnInit {
|
|||||||
|
|
||||||
leave = async () => {
|
leave = async () => {
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
title: this.organization.name,
|
title: { key: "leaveOrganization" },
|
||||||
content: { key: "leaveOrganizationConfirmation" },
|
content: { key: "leaveOrganizationContent" },
|
||||||
type: "warning",
|
type: "warning",
|
||||||
|
acceptButtonText: { key: "leaveNow" },
|
||||||
|
cancelButtonText: { key: "cancel" },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
|
|||||||
Reference in New Issue
Block a user