1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

Merge branch 'feature/sm-billing' into AC-1418-add-secrets-manager-manage-subscription-component

This commit is contained in:
Thomas Rittson
2023-06-29 10:55:39 +10:00
committed by GitHub
96 changed files with 1548 additions and 394 deletions

View File

@@ -952,7 +952,7 @@
"message": "Server URL"
},
"apiUrl": {
"message": "API Server URL"
"message": "API server URL"
},
"webVaultUrl": {
"message": "Web vault server URL"

View File

@@ -7,8 +7,6 @@ import { NotificationsService as NotificationsServiceAbstraction } from "@bitwar
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -21,6 +19,8 @@ import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/ab
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";

View File

@@ -15,7 +15,6 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notificatio
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -31,6 +30,7 @@ import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-con
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { LoginService } from "@bitwarden/common/auth/services/login.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";

View File

@@ -5,8 +5,8 @@ import { Router } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";

View File

@@ -6,8 +6,8 @@ import { UntypedFormBuilder } from "@angular/forms";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";

View File

@@ -1,11 +0,0 @@
{
"urls": {
"icons": "https://icons.eudevtest.bitwarden.pw",
"notifications": "https://notifications.eudevtest.bitwarden.pw",
"scim": "https://scim.eudevtest.bitwarden.pw"
},
"flags": {
"secretsManager": true,
"showPasswordless": true
}
}

View File

@@ -1,6 +1,6 @@
{
"urls": {
"icons": "https://icons.bitwarden.net",
"icons": "https://icons.bitwarden.eu",
"notifications": "https://notifications.bitwarden.eu",
"scim": "https://scim.bitwarden.eu"
},

11
apps/web/config/euqa.json Normal file
View File

@@ -0,0 +1,11 @@
{
"urls": {
"icons": "https://icons.euqa.bitwarden.pw",
"notifications": "https://notifications.euqa.bitwarden.pw",
"scim": "https://scim.euqa.bitwarden.pw"
},
"flags": {
"secretsManager": true,
"showPasswordless": true
}
}

View File

@@ -11,7 +11,7 @@
"build:bit:dev:watch": "cross-env ENV=development npm run build:bit:watch",
"build:bit:qa": "cross-env NODE_ENV=production ENV=qa npm run build:bit",
"build:bit:euprd": "cross-env NODE_ENV=production ENV=euprd npm run build:bit",
"build:bit:eudevtest": "cross-env NODE_ENV=production ENV=eudevtest npm run build:bit",
"build:bit:euqa": "cross-env NODE_ENV=production ENV=euqa npm run build:bit",
"build:bit:cloud": "cross-env NODE_ENV=production ENV=cloud npm run build:bit",
"build:oss:selfhost:watch": "cross-env ENV=selfhosted npm run build:oss:watch",
"build:bit:selfhost:watch": "cross-env ENV=selfhosted npm run build:bit:watch",

View File

@@ -4,10 +4,10 @@ import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { combineLatest, Subject, takeUntil } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -17,7 +17,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { UserVerificationModule } from "../../../../shared/components/user-verification";
import { UserVerificationModule } from "../../../../auth/shared/components/user-verification";
import { SharedModule } from "../../../../shared/shared.module";
class CountBasedLocalizationKey {

View File

@@ -3,10 +3,9 @@ import { UntypedFormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EventType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@@ -35,7 +34,6 @@ export class OrganizationExportComponent extends ExportComponent {
userVerificationService: UserVerificationService,
formBuilder: UntypedFormBuilder,
fileDownloadService: FileDownloadService,
modalService: ModalService,
dialogService: DialogServiceAbstraction
) {
super(
@@ -49,7 +47,6 @@ export class OrganizationExportComponent extends ExportComponent {
userVerificationService,
formBuilder,
fileDownloadService,
modalService,
dialogService
);
}

View File

@@ -4,9 +4,9 @@ import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";

View File

@@ -1,8 +1,8 @@
import { ScrollingModule } from "@angular/cdk/scrolling";
import { NgModule } from "@angular/core";
import { UserVerificationModule } from "../../../auth/shared/components/user-verification";
import { LooseComponentsModule, SharedModule } from "../../../shared";
import { UserVerificationModule } from "../../../shared/components/user-verification";
import { EnrollMasterPasswordReset } from "./enroll-master-password-reset.component";

View File

@@ -1,7 +1,7 @@
import { Component } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";

View File

@@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request";
import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response";

View File

@@ -2,7 +2,7 @@ import { Directive, EventEmitter, Output } from "@angular/core";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";

View File

@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorDuoRequest } from "@bitwarden/common/auth/models/request/update-two-factor-duo.request";
import { TwoFactorDuoResponse } from "@bitwarden/common/auth/models/response/two-factor-duo.response";

View File

@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
import { UpdateTwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/update-two-factor-email.request";

View File

@@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";

View File

@@ -2,7 +2,7 @@ import { Component, NgZone } from "@angular/core";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { UpdateTwoFactorWebAuthnDeleteRequest } from "@bitwarden/common/auth/models/request/update-two-factor-web-authn-delete.request";

View File

@@ -2,7 +2,7 @@ import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { UpdateTwoFactorYubioOtpRequest } from "@bitwarden/common/auth/models/request/update-two-factor-yubio-otp.request";
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/auth/models/response/two-factor-yubi-key.response";

View File

@@ -1,3 +1,3 @@
export * from "./user-verification.module";
export * from "./user-verification-prompt.component";
export * from "./user-verification.component";
export * from "./user-verification-prompt.component";

View File

@@ -0,0 +1,20 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog>
<span bitDialogTitle>{{ modalTitle | i18n }}</span>
<ng-container bitDialogContent>
<p bitTypography="body1">{{ confirmDescription | i18n }}</p>
<app-user-verification
[(invalidSecret)]="invalidSecret"
formControlName="secret"
></app-user-verification>
</ng-container>
<ng-container bitDialogFooter>
<button type="submit" bitButton bitFormButton buttonType="primary">
{{ confirmButtonText | i18n }}
</button>
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>
</form>

View File

@@ -0,0 +1,60 @@
import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/auth/components/user-verification-prompt.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
export interface UserVerificationPromptParams {
confirmDescription: string;
confirmButtonText: string;
modalTitle: string;
}
@Component({
templateUrl: "user-verification-prompt.component.html",
})
export class UserVerificationPromptComponent extends BaseUserVerificationPrompt {
constructor(
@Inject(DIALOG_DATA) data: UserVerificationPromptParams,
private dialogRef: DialogRef<boolean>,
userVerificationService: UserVerificationService,
formBuilder: FormBuilder,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService
) {
// TODO: Remove when BaseUserVerificationPrompt has support for CL
const modalConfig: ModalConfig = { data };
super(
null,
modalConfig,
userVerificationService,
formBuilder,
platformUtilsService,
i18nService
);
}
override close(success: boolean) {
this.dialogRef.close(success);
}
}
/**
* Strongly typed helper to open a UserVerificationPrompt
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param config Configuration for the dialog
*/
export const openUserVerificationPrompt = (
dialogService: DialogServiceAbstraction,
config: DialogConfig<UserVerificationPromptParams>
) => {
return dialogService.open<boolean, UserVerificationPromptParams>(
UserVerificationPromptComponent,
config
);
};

View File

@@ -0,0 +1,41 @@
<ng-container *ngIf="!usesKeyConnector">
<bit-form-field disableMargin>
<bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
bitInput
id="masterPassword"
type="password"
name="MasterPasswordHash"
[formControl]="secret"
appAutofocus
appInputVerbatim
/>
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
<bit-hint>{{ "confirmIdentity" | i18n }}</bit-hint>
</bit-form-field>
</ng-container>
<ng-container *ngIf="usesKeyConnector">
<div class="tw-mb-6">
<label class="tw-block">{{ "sendVerificationCode" | i18n }}</label>
<button type="button" bitButton buttonType="secondary" [bitAction]="requestOTP" appAutofocus>
{{ "sendCode" | i18n }}
</button>
<span class="tw-ml-2 tw-text-success" role="alert" @sent *ngIf="sentCode">
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
{{ "codeSent" | i18n }}
</span>
</div>
<bit-form-field disableMargin>
<bit-label>{{ "verificationCode" | i18n }}</bit-label>
<input
bitInput
type="text"
id="verificationCode"
name="verificationCode"
[formControl]="secret"
appInputVerbatim
/>
<bit-hint>{{ "confirmIdentity" | i18n }}</bit-hint>
</bit-form-field>
</ng-container>

View File

@@ -1,12 +1,13 @@
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { SharedModule } from "../../shared.module";
import { SharedModule } from "../../../../shared/shared.module";
import { UserVerificationPromptComponent } from "./user-verification-prompt.component";
import { UserVerificationComponent } from "./user-verification.component";
@NgModule({
imports: [SharedModule],
imports: [SharedModule, FormsModule, ReactiveFormsModule],
declarations: [UserVerificationComponent, UserVerificationPromptComponent],
exports: [UserVerificationComponent, UserVerificationPromptComponent],
})

View File

@@ -4,8 +4,8 @@ import { Router } from "@angular/router";
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";

View File

@@ -2,10 +2,10 @@ import { Component } from "@angular/core";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
import { OrganizationApiKeyRequest } from "@bitwarden/common/admin-console/models/request/organization-api-key.request";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

View File

@@ -1,7 +1,7 @@
import { NgModule } from "@angular/core";
import { UserVerificationModule } from "../../auth/shared/components/user-verification";
import { LooseComponentsModule, SharedModule } from "../../shared";
import { UserVerificationModule } from "../../shared/components/user-verification";
import { AdjustSubscription } from "./adjust-subscription.component";
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";

View File

@@ -1,6 +1,6 @@
import { Component } from "@angular/core";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";

View File

@@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

View File

@@ -1,26 +0,0 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="confirmUserTitle">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<form class="modal-content" #form (ngSubmit)="submit()">
<h2 class="tw-my-6 tw-px-3.5 tw-font-semibold" id="modalTitle | i18n ">
{{ modalTitle | i18n | uppercase }}
</h2>
<div class="tw-border-0 tw-border-t tw-border-solid tw-border-secondary-300 tw-p-3.5">
{{ confirmDescription | i18n }}
</div>
<div class="tw-p-3.5">
<app-user-verification ngDefaultControl [formControl]="secret" name="secret">
</app-user-verification>
</div>
<div
class="tw-border-0 tw-border-t tw-border-solid tw-border-secondary-300 tw-bg-background-alt tw-p-3.5"
>
<button type="button" bitButton buttonType="primary" type="submit" appBlurClick>
<span>{{ confirmButtonText | i18n }}</span>
</button>
<button type="button" bitButton buttonType="secondary" data-dismiss="modal">
{{ "cancel" | i18n }}
</button>
</div>
</form>
</div>
</div>

View File

@@ -1,8 +0,0 @@
import { Component } from "@angular/core";
import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/auth/components/user-verification-prompt.component";
@Component({
templateUrl: "user-verification-prompt.component.html",
})
export class UserVerificationPromptComponent extends BaseUserVerificationPrompt {}

View File

@@ -1,46 +0,0 @@
<ng-container *ngIf="!usesKeyConnector">
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
<input
id="masterPassword"
type="password"
name="MasterPasswordHash"
class="form-control"
[formControl]="secret"
required
appAutofocus
appInputVerbatim
/>
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
</ng-container>
<ng-container *ngIf="usesKeyConnector">
<div class="form-group">
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
<button
type="button"
class="btn btn-outline-secondary"
(click)="requestOTP()"
[disabled]="disableRequestOTP"
>
{{ "sendCode" | i18n }}
</button>
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
{{ "codeSent" | i18n }}
</span>
</div>
<div class="form-group">
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
<input
id="verificationCode"
type="input"
name="verificationCode"
class="form-control"
[formControl]="secret"
required
appAutofocus
appInputVerbatim
/>
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
</div>
</ng-container>

View File

@@ -43,6 +43,7 @@ import { TwoFactorVerifyComponent } from "../auth/settings/two-factor-verify.com
import { TwoFactorWebAuthnComponent } from "../auth/settings/two-factor-webauthn.component";
import { TwoFactorYubiKeyComponent } from "../auth/settings/two-factor-yubikey.component";
import { VerifyEmailComponent } from "../auth/settings/verify-email.component";
import { UserVerificationModule } from "../auth/shared/components/user-verification";
import { SsoComponent } from "../auth/sso.component";
import { TwoFactorOptionsComponent } from "../auth/two-factor-options.component";
import { TwoFactorComponent } from "../auth/two-factor.component";
@@ -109,7 +110,6 @@ import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-va
import { CollectionsComponent as OrgCollectionsComponent } from "../vault/org-vault/collections.component";
import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component";
import { UserVerificationModule } from "./components/user-verification";
import { SharedModule } from "./shared.module";
// Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left.
@@ -234,6 +234,7 @@ import { SharedModule } from "./shared.module";
LowKdfComponent,
],
exports: [
UserVerificationModule,
PremiumBadgeComponent,
AcceptEmergencyComponent,
AcceptOrganizationComponent,

View File

@@ -1,12 +1,12 @@
import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
import { firstValueFrom } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EncryptedExportType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@@ -15,7 +15,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { VaultExportServiceAbstraction } from "@bitwarden/exporter/vault-export";
import { UserVerificationPromptComponent } from "../../shared/components/user-verification";
import { openUserVerificationPrompt } from "../../auth/shared/components/user-verification";
@Component({
selector: "app-export",
@@ -37,7 +37,6 @@ export class ExportComponent extends BaseExportComponent {
userVerificationService: UserVerificationService,
formBuilder: UntypedFormBuilder,
fileDownloadService: FileDownloadService,
private modalService: ModalService,
dialogService: DialogServiceAbstraction
) {
super(
@@ -101,8 +100,7 @@ export class ExportComponent extends BaseExportComponent {
confirmDescription = "encExportKeyWarningDesc";
}
const ref = this.modalService.open(UserVerificationPromptComponent, {
allowMultipleModals: true,
const ref = openUserVerificationPrompt(this.dialogService, {
data: {
confirmDescription: confirmDescription,
confirmButtonText: "exportVault",
@@ -114,7 +112,7 @@ export class ExportComponent extends BaseExportComponent {
return;
}
return ref.onClosedPromise();
return firstValueFrom(ref.closed);
}
get isFileEncryptedExport() {

View File

@@ -22,6 +22,19 @@
<input bitInput appAutofocus formControlName="name" />
</bit-form-field>
<bit-form-field *ngIf="showOrgSelector">
<bit-label>{{ "organization" | i18n }}</bit-label>
<bit-select bitInput formControlName="selectedOrg">
<bit-option
*ngFor="let org of organizations$ | async"
icon="bwi-business"
[value]="org.id"
[label]="org.name"
>
</bit-option>
</bit-select>
</bit-form-field>
<bit-form-field>
<bit-label>{{ "externalId" | i18n }}</bit-label>
<input bitInput formControlName="externalId" />

View File

@@ -1,7 +1,16 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { combineLatest, of, shareReplay, Subject, switchMap, takeUntil } from "rxjs";
import {
combineLatest,
map,
Observable,
of,
shareReplay,
Subject,
switchMap,
takeUntil,
} from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
@@ -10,6 +19,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CollectionResponse } from "@bitwarden/common/vault/models/response/collection.response";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { BitValidators } from "@bitwarden/components";
@@ -35,9 +46,16 @@ export interface CollectionDialogParams {
organizationId: string;
initialTab?: CollectionDialogTabType;
parentCollectionId?: string;
showOrgSelector?: boolean;
collectionIds?: string[];
}
export enum CollectionDialogResult {
export interface CollectionDialogResult {
action: CollectionDialogAction;
collection: CollectionResponse;
}
export enum CollectionDialogAction {
Saved = "saved",
Canceled = "canceled",
Deleted = "deleted",
@@ -48,6 +66,7 @@ export enum CollectionDialogResult {
})
export class CollectionDialogComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
protected organizations$: Observable<Organization[]>;
protected tabIndex: CollectionDialogTabType;
protected loading = true;
@@ -56,11 +75,13 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
protected nestOptions: CollectionView[] = [];
protected accessItems: AccessItemView[] = [];
protected deletedParentName: string | undefined;
protected showOrgSelector = false;
protected formGroup = this.formBuilder.group({
name: ["", [Validators.required, BitValidators.forbiddenCharacters(["/"])]],
externalId: "",
parent: undefined as string | undefined,
access: [[] as AccessItemValue[]],
selectedOrg: "",
});
protected PermissionMode = PermissionMode;
@@ -79,8 +100,31 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.tabIndex = params.initialTab ?? CollectionDialogTabType.Info;
}
ngOnInit() {
const organization$ = of(this.organizationService.get(this.params.organizationId)).pipe(
async ngOnInit() {
// Opened from the individual vault
if (this.params.showOrgSelector) {
this.showOrgSelector = true;
this.formGroup.controls.selectedOrg.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe((id) => this.loadOrg(id, this.params.collectionIds));
this.organizations$ = this.organizationService.organizations$.pipe(
map((orgs) =>
orgs
.filter((o) => o.canCreateNewCollections)
.sort(Utils.getSortFunction(this.i18nService, "name"))
)
);
// patchValue will trigger a call to loadOrg() in this case, so no need to call it again here
this.formGroup.patchValue({ selectedOrg: this.params.organizationId });
} else {
// Opened from the org vault
this.formGroup.patchValue({ selectedOrg: this.params.organizationId });
this.loadOrg(this.params.organizationId, this.params.collectionIds);
}
}
async loadOrg(orgId: string, collectionIds: string[]) {
const organization$ = of(this.organizationService.get(orgId)).pipe(
shareReplay({ refCount: true, bufferSize: 1 })
);
const groups$ = organization$.pipe(
@@ -89,20 +133,19 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
return of([] as GroupView[]);
}
return this.groupService.getAll(this.params.organizationId);
return this.groupService.getAll(orgId);
})
);
combineLatest({
organization: organization$,
collections: this.collectionService.getAll(this.params.organizationId),
collections: this.collectionService.getAll(orgId),
collectionDetails: this.params.collectionId
? this.collectionService.get(this.params.organizationId, this.params.collectionId)
? this.collectionService.get(orgId, this.params.collectionId)
: of(null),
groups: groups$,
users: this.organizationUserService.getAllUsers(this.params.organizationId),
users: this.organizationUserService.getAllUsers(orgId),
})
.pipe(takeUntil(this.destroy$))
.pipe(takeUntil(this.formGroup.controls.selectedOrg.valueChanges), takeUntil(this.destroy$))
.subscribe(({ organization, collections, collectionDetails, groups, users }) => {
this.organization = organization;
this.accessItems = [].concat(
@@ -110,6 +153,10 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
users.data.map(mapUserToAccessItemView)
);
if (collectionIds) {
collections = collections.filter((c) => collectionIds.includes(c.id));
}
if (this.params.collectionId) {
this.collection = collections.find((c) => c.id === this.collectionId);
this.nestOptions = collections.filter((c) => c.id !== this.collectionId);
@@ -149,7 +196,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
}
protected async cancel() {
this.close(CollectionDialogResult.Canceled);
this.close(CollectionDialogAction.Canceled);
}
protected submit = async () => {
@@ -168,7 +215,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
const collectionView = new CollectionAdminView();
collectionView.id = this.params.collectionId;
collectionView.organizationId = this.params.organizationId;
collectionView.organizationId = this.formGroup.controls.selectedOrg.value;
collectionView.externalId = this.formGroup.controls.externalId.value;
collectionView.groups = this.formGroup.controls.access.value
.filter((v) => v.type === AccessItemType.Group)
@@ -184,7 +231,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
collectionView.name = this.formGroup.controls.name.value;
}
await this.collectionService.save(collectionView);
const savedCollection = await this.collectionService.save(collectionView);
this.platformUtilsService.showToast(
"success",
@@ -195,7 +242,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
)
);
this.close(CollectionDialogResult.Saved);
this.close(CollectionDialogAction.Saved, savedCollection);
};
protected delete = async () => {
@@ -217,7 +264,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.i18nService.t("deletedCollectionId", this.collection?.name)
);
this.close(CollectionDialogResult.Deleted);
this.close(CollectionDialogAction.Deleted);
};
ngOnDestroy(): void {
@@ -225,8 +272,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
this.destroy$.complete();
}
private close(result: CollectionDialogResult) {
this.dialogRef.close(result);
private close(action: CollectionDialogAction, collection?: CollectionResponse) {
this.dialogRef.close({ action, collection } as CollectionDialogResult);
}
}

View File

@@ -46,7 +46,7 @@ export class CollectionAdminService {
return view;
}
async save(collection: CollectionAdminView): Promise<unknown> {
async save(collection: CollectionAdminView): Promise<CollectionResponse> {
const request = await this.encrypt(collection);
let response: CollectionResponse;
@@ -61,9 +61,7 @@ export class CollectionAdminService {
);
}
// TODO: Implement upsert when in PS-1083: Collection Service refactors
// await this.collectionService.upsert(data);
return;
return response;
}
async delete(organizationId: string, collectionId: string): Promise<void> {

View File

@@ -32,7 +32,6 @@ import { OrganizationOptionsComponent } from "./organization-options.component";
export class VaultFilterComponent implements OnInit, OnDestroy {
filters?: VaultFilterList;
@Input() activeFilter: VaultFilter = new VaultFilter();
@Output() onAddFolder = new EventEmitter<never>();
@Output() onEditFolder = new EventEmitter<FolderFilter>();
@Input() searchText = "";
@@ -142,10 +141,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
filter.selectedCollectionNode = collectionNode;
};
addFolder = async (): Promise<void> => {
this.onAddFolder.emit();
};
editFolder = async (folder: FolderFilter): Promise<void> => {
this.onEditFolder.emit(folder);
};
@@ -249,10 +244,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
text: "editFolder",
action: this.editFolder,
},
add: {
text: "Add Folder",
action: this.addFolder,
},
};
return folderFilterSection;
}

View File

@@ -34,16 +34,6 @@
<h3 *ngIf="!headerInfo.isSelectable" class="filter-title">
&nbsp;{{ headerNode.node.name | i18n }}
</h3>
<button
type="button"
*ngIf="showAddButton"
(click)="onAdd()"
class="text-muted ml-auto add-button"
appA11yTitle="{{ addInfo.text | i18n }}"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
</button>
</div>
<ul
id="{{ headerNode.node.name }}-filters"

View File

@@ -87,10 +87,6 @@ export class VaultFilterSectionComponent implements OnInit, OnDestroy {
return this.section.add;
}
get showAddButton() {
return this.section.add && !this.section.add.route;
}
get showAddLink() {
return this.section.add && this.section.add.route;
}

View File

@@ -40,9 +40,31 @@
</div>
<div *ngIf="filter.type !== 'trash'" class="tw-shrink-0">
<button type="button" bitButton buttonType="primary" (click)="addCipher()">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newItem" | i18n }}
</button>
<div appListDropdown>
<button
bitButton
buttonType="primary"
type="button"
[bitMenuTriggerFor]="addOptions"
id="newItemDropdown"
appA11yTitle="{{ 'new' | i18n }}"
>
{{ "new" | i18n }}<i class="bwi bwi-angle-down tw-ml-2" aria-hidden="true"></i>
</button>
<bit-menu #addOptions aria-labelledby="newItemDropdown">
<button type="button" bitMenuItem (click)="addCipher()">
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
{{ "item" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addFolder()">
<i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i>
{{ "folder" | i18n }}
</button>
<button *ngIf="canCreateCollections" type="button" bitMenuItem (click)="addCollection()">
<i class="bwi bwi-fw bwi-collection" aria-hidden="true"></i>
{{ "collection" | i18n }}
</button>
</bit-menu>
</div>
</div>
</div>

View File

@@ -39,11 +39,26 @@ export class VaultHeaderComponent {
*/
@Input() collection?: TreeNode<CollectionView>;
/**
* Whether 'Collection' option is shown in the 'New' dropdown
*/
@Input() canCreateCollections: boolean;
/**
* Emits an event when the new item button is clicked in the header
*/
@Output() onAddCipher = new EventEmitter<void>();
/**
* Emits an event when the new collection button is clicked in the 'New' dropdown menu
*/
@Output() onAddCollection = new EventEmitter<null>();
/**
* Emits an event when the new folder button is clicked in the 'New' dropdown menu
*/
@Output() onAddFolder = new EventEmitter<null>();
constructor(private i18nService: I18nService) {}
/**
@@ -115,4 +130,12 @@ export class VaultHeaderComponent {
protected addCipher() {
this.onAddCipher.emit();
}
async addFolder(): Promise<void> {
this.onAddFolder.emit();
}
async addCollection(): Promise<void> {
this.onAddCollection.emit();
}
}

View File

@@ -9,7 +9,6 @@
[activeFilter]="activeFilter"
[searchText]="currentSearchText$ | async"
(searchTextChanged)="filterSearchText($event)"
(onAddFolder)="addFolder()"
(onEditFolder)="editFolder($event)"
></app-vault-filter>
</div>
@@ -21,8 +20,11 @@
[filter]="filter"
[loading]="refreshing && !performingInitialLoad"
[organizations]="allOrganizations"
[canCreateCollections]="canCreateCollections"
[collection]="selectedCollection"
(onAddCipher)="addCipher()"
(onAddCollection)="addCollection()"
(onAddFolder)="addFolder()"
></app-vault-header>
<app-callout type="warning" *ngIf="activeFilter.isDeleted" icon="bwi-exclamation-triangle">
{{ trashCleanupWarning }}

View File

@@ -54,11 +54,14 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { Icons } from "@bitwarden/components";
import { UpdateKeyComponent } from "../../settings/update-key.component";
import { CollectionDialogAction, openCollectionDialog } from "../components/collection-dialog";
import { VaultItemEvent } from "../components/vault-items/vault-item-event";
import { getNestedCollectionTree } from "../utils/collection-utils";
@@ -140,6 +143,7 @@ export class VaultComponent implements OnInit, OnDestroy {
protected collections: CollectionView[];
protected isEmpty: boolean;
protected selectedCollection: TreeNode<CollectionView> | undefined;
protected canCreateCollections = false;
protected currentSearchText$: Observable<string>;
private searchText$ = new Subject<string>();
@@ -234,12 +238,9 @@ export class VaultComponent implements OnInit, OnDestroy {
const canAccessPremium$ = Utils.asyncToObservable(() =>
this.stateService.getCanAccessPremium()
).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
const allCollections$ = Utils.asyncToObservable(() =>
this.collectionService.getAllDecrypted()
).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
const allCollections$ = Utils.asyncToObservable(() => this.collectionService.getAllDecrypted());
const nestedCollections$ = allCollections$.pipe(
map((collections) => getNestedCollectionTree(collections)),
shareReplay({ refCount: true, bufferSize: 1 })
map((collections) => getNestedCollectionTree(collections))
);
this.searchText$
@@ -384,6 +385,8 @@ export class VaultComponent implements OnInit, OnDestroy {
this.collections = collections;
this.selectedCollection = selectedCollection;
this.canCreateCollections = allOrganizations?.some((o) => o.canCreateNewCollections);
this.showBulkMove =
filter.type !== "trash" &&
(filter.organizationId === undefined || filter.organizationId === Unassigned);
@@ -639,6 +642,32 @@ export class VaultComponent implements OnInit, OnDestroy {
return childComponent;
}
async addCollection() {
const dialog = openCollectionDialog(this.dialogService, {
data: {
organizationId: this.allOrganizations
.filter((o) => o.canCreateNewCollections)
.sort(Utils.getSortFunction(this.i18nService, "name"))[0].id,
parentCollectionId: this.filter.collectionId,
showOrgSelector: true,
collectionIds: this.allCollections.map((c) => c.id),
},
});
const result = await lastValueFrom(dialog.closed);
if (result.action === CollectionDialogAction.Saved) {
if (result.collection) {
// Update CollectionService with the new collection
const c = new CollectionData(result.collection as CollectionDetailsResponse);
await this.collectionService.upsert(c);
}
this.refresh();
} else if (result.action === CollectionDialogAction.Deleted) {
// TODO: Remove collection from collectionService when collection
// deletion is implemented in the individual vault in AC-1347
this.refresh();
}
}
async cloneCipher(cipher: CipherView) {
const component = await this.editCipher(cipher);
component.cloneMode = true;

View File

@@ -60,7 +60,7 @@ import { openEntityEventsDialog } from "../../admin-console/organizations/manage
import { VaultFilterService } from "../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service";
import { VaultFilter } from "../../vault/individual-vault/vault-filter/shared/models/vault-filter.model";
import {
CollectionDialogResult,
CollectionDialogAction,
CollectionDialogTabType,
openCollectionDialog,
} from "../components/collection-dialog";
@@ -866,7 +866,10 @@ export class VaultComponent implements OnInit, OnDestroy {
});
const result = await lastValueFrom(dialog.closed);
if (result === CollectionDialogResult.Saved || result === CollectionDialogResult.Deleted) {
if (
result.action === CollectionDialogAction.Saved ||
result.action === CollectionDialogAction.Deleted
) {
this.refresh();
}
}
@@ -877,7 +880,10 @@ export class VaultComponent implements OnInit, OnDestroy {
});
const result = await lastValueFrom(dialog.closed);
if (result === CollectionDialogResult.Saved || result === CollectionDialogResult.Deleted) {
if (
result.action === CollectionDialogAction.Saved ||
result.action === CollectionDialogAction.Deleted
) {
this.refresh();
}
}

View File

@@ -964,8 +964,14 @@
"confirmVaultExport": {
"message": "Confirm vault export"
},
"confirmSecretsExport": {
"message": "Confirm secrets export"
},
"exportWarningDesc": {
"message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it."
},
"exportSecretsWarningDesc": {
"message": "This export contains your secrets data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it."
},
"encExportKeyWarningDesc": {
"message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."
@@ -979,6 +985,9 @@
"exportVault": {
"message": "Export vault"
},
"exportSecrets": {
"message": "Export secrets"
},
"fileFormat": {
"message": "File format"
},
@@ -2657,6 +2666,12 @@
"failedLogin2fa": {
"message": "Login attempt failed with incorrect two-step login."
},
"incorrectPassword": {
"message": "Incorrect password"
},
"incorrectCode": {
"message": "Incorrect code"
},
"exportedVault": {
"message": "Vault exported"
},
@@ -6182,10 +6197,6 @@
"message": "Access token created and copied to clipboard",
"description": "Notification to inform the user that the access token has been created and copied to the clipboard."
},
"accessTokenPermissionsBetaNotification": {
"message": "Permissions management is unavailable for beta.",
"description": "Notification to inform the user that the feature for managing access token permissions is not available in the beta version."
},
"revokeAccessToken": {
"message": "Revoke access token",
"description": "Invalidates / cancels an access token and as such removes access to secrets for the client application."