1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-02 16:43:19 +00:00

Merge branch 'master' into feature/flexible-collections

This commit is contained in:
Vincent Salucci
2023-09-14 17:07:54 -05:00
900 changed files with 69193 additions and 19393 deletions

View File

@@ -13,7 +13,8 @@
"dev": {
"proxyApi": "https://api.bitwarden.com",
"proxyIdentity": "https://identity.bitwarden.com",
"proxyEvents": "https://events.bitwarden.com"
"proxyEvents": "https://events.bitwarden.com",
"proxyNotifications": "https://notifications.bitwarden.com"
},
"flags": {
"secretsManager": true,

View File

@@ -7,7 +7,8 @@
"dev": {
"proxyApi": "https://api.qa.bitwarden.pw",
"proxyIdentity": "https://identity.qa.bitwarden.pw",
"proxyEvents": "https://events.qa.bitwarden.pw"
"proxyEvents": "https://events.qa.bitwarden.pw",
"proxyNotifications": "https://notifications.qa.bitwarden.pw"
},
"flags": {
"secretsManager": true,

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
"version": "2023.7.1",
"version": "2023.8.4",
"scripts": {
"build:oss": "webpack",
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",

View File

@@ -1,9 +1,9 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { DialogService } from "@bitwarden/components";
@Injectable({
providedIn: "root",
@@ -13,7 +13,7 @@ export class IsPaidOrgGuard implements CanActivate {
private router: Router,
private organizationService: OrganizationService,
private messagingService: MessagingService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
@@ -31,7 +31,7 @@ export class IsPaidOrgGuard implements CanActivate {
content: { key: "notAvailableForFreeOrganization" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.INFO,
type: "info",
});
return false;
} else {

View File

@@ -3,7 +3,6 @@ import { Component, Inject, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { EventResponse } from "@bitwarden/common/models/response/event.response";
@@ -13,7 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource } from "@bitwarden/components";
import { EventService } from "../../../core";
import { SharedModule } from "../../../shared";
@@ -186,7 +185,7 @@ export class EntityEventsComponent implements OnInit {
* @param config Configuration for the dialog
*/
export const openEntityEventsDialog = (
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
config: DialogConfig<EntityEventsDialogParams>
) => {
return dialogService.open<void, EntityEventsDialogParams>(EntityEventsComponent, config);

View File

@@ -69,13 +69,7 @@
<button bitButton buttonType="primary" bitFormButton type="submit">
{{ "save" | i18n }}
</button>
<button
bitButton
buttonType="secondary"
type="button"
bitDialogClose
[bit-dialog-close]="ResultType.Canceled"
>
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.Canceled">
{{ "cancel" | i18n }}
</button>
<button

View File

@@ -3,7 +3,6 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angula
import { FormBuilder, Validators } from "@angular/forms";
import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil } from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
@@ -14,6 +13,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
import { Collection } from "@bitwarden/common/vault/models/domain/collection";
import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response";
import { DialogService } from "@bitwarden/components";
import { InternalGroupService as GroupService, GroupView } from "../core";
import {
@@ -64,7 +64,7 @@ export enum GroupAddEditDialogResultType {
* @param config Configuration for the dialog
*/
export const openGroupAddEditDialog = (
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
config: DialogConfig<GroupAddEditDialogParams>
) => {
return dialogService.open<GroupAddEditDialogResultType, GroupAddEditDialogParams>(
@@ -181,7 +181,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
private logService: LogService,
private formBuilder: FormBuilder,
private changeDetectorRef: ChangeDetectorRef,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
}
@@ -273,7 +273,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
const confirmed = await this.dialogService.openSimpleDialog({
title: this.group.name,
content: { key: "deleteGroupConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
return false;

View File

@@ -15,7 +15,6 @@ import {
import { first } from "rxjs/operators";
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
@@ -32,6 +31,7 @@ import {
CollectionResponse,
} from "@bitwarden/common/vault/models/response/collection.response";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { InternalGroupService as GroupService, GroupView } from "../core";
@@ -127,7 +127,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private i18nService: I18nService,
private modalService: ModalService,
private dialogService: DialogServiceAbstraction,
private dialogService: DialogService,
private platformUtilsService: PlatformUtilsService,
private searchService: SearchService,
private logService: LogService,
@@ -236,7 +236,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
const confirmed = await this.dialogService.openSimpleDialog({
title: groupRow.details.name,
content: { key: "deleteGroupConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
return false;
@@ -269,7 +269,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
placeholders: [groupsToDelete.length.toString()],
},
content: deleteMessage,
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
return false;

View File

@@ -7,6 +7,7 @@ import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enum
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { BulkUserDetails } from "./bulk-status.component";
@@ -98,7 +99,7 @@ export class BulkConfirmComponent implements OnInit {
);
}
protected getCryptoKey() {
protected getCryptoKey(): Promise<SymmetricCryptoKey> {
return this.cryptoService.getOrgKey(this.organizationId);
}

View File

@@ -1,11 +1,10 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource } from "@bitwarden/components";
import { OrganizationUserView } from "../../../core";
@@ -44,7 +43,7 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit {
this.dialogRef.close();
};
static open(dialogService: DialogServiceAbstraction, data: BulkEnableSecretsManagerDialogData) {
static open(dialogService: DialogService, data: BulkEnableSecretsManagerDialogData) {
return dialogService.open<unknown, BulkEnableSecretsManagerDialogData>(
BulkEnableSecretsManagerDialogComponent,
{ data }

View File

@@ -3,7 +3,6 @@ import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { combineLatest, 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";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
@@ -15,6 +14,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { flagEnabled } from "../../../../../../utils/flags";
import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service";
@@ -134,7 +134,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy {
private groupService: GroupService,
private userService: UserAdminService,
private organizationUserService: OrganizationUserService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async ngOnInit() {
@@ -389,7 +389,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy {
let confirmed = await this.dialogService.openSimpleDialog({
title: { key: "removeUserIdAccess", placeholders: [this.params.name] },
content: { key: message },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -426,7 +426,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy {
title: { key: "revokeUserId", placeholders: [this.params.name] },
content: { key: "revokeUserConfirmation" },
acceptButtonText: { key: "revokeAccess" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -496,7 +496,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy {
key: "removeOrgUserNoMasterPasswordDesc",
placeholders: [this.params.name],
},
type: SimpleDialogType.WARNING,
type: "warning",
});
}
}
@@ -557,7 +557,7 @@ function mapToGroupAccessSelections(groups: string[]): AccessItemValue[] {
* @param config Configuration for the dialog
*/
export function openUserAddEditDialog(
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
config: DialogConfig<MemberDialogParams>
) {
return dialogService.open<MemberDialogResult, MemberDialogParams>(MemberDialogComponent, config);

View File

@@ -10,7 +10,6 @@ import {
import { Subject, takeUntil } from "rxjs";
import zxcvbn from "zxcvbn";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { PasswordStrengthComponent } from "@bitwarden/angular/shared/components/password-strength/password-strength.component";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { OrganizationUserResetPasswordRequest } from "@bitwarden/common/abstractions/organization-user/requests";
@@ -23,8 +22,12 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
SymmetricCryptoKey,
UserKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-reset-password",
@@ -54,7 +57,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
private cryptoService: CryptoService,
private logService: LogService,
private organizationUserService: OrganizationUserService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async ngOnInit() {
@@ -140,7 +143,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
const result = await this.dialogService.openSimpleDialog({
title: { key: "weakMasterPassword" },
content: { key: "weakMasterPasswordDesc" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!result) {
@@ -171,26 +174,32 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
orgSymKey
);
// Decrypt User's Reset Password Key to get EncKey
// Decrypt User's Reset Password Key to get UserKey
const decValue = await this.cryptoService.rsaDecrypt(resetPasswordKey, decPrivateKey);
const userEncKey = new SymmetricCryptoKey(decValue);
const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey;
// Create new key and hash new password
const newKey = await this.cryptoService.makeKey(
// Create new master key and hash new password
const newMasterKey = await this.cryptoService.makeMasterKey(
this.newPassword,
this.email.trim().toLowerCase(),
kdfType,
new KdfConfig(kdfIterations, kdfMemory, kdfParallelism)
);
const newPasswordHash = await this.cryptoService.hashPassword(this.newPassword, newKey);
const newMasterKeyHash = await this.cryptoService.hashMasterKey(
this.newPassword,
newMasterKey
);
// Create new encKey for the User
const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey);
// Create new encrypted user key for the User
const newUserKey = await this.cryptoService.encryptUserKeyWithMasterKey(
newMasterKey,
existingUserKey
);
// Create request
const request = new OrganizationUserResetPasswordRequest();
request.key = newEncKey[1].encryptedString;
request.newMasterPasswordHash = newPasswordHash;
request.key = newUserKey[1].encryptedString;
request.newMasterPasswordHash = newMasterKeyHash;
// Change user's password
return this.organizationUserService.putOrganizationUserResetPassword(

View File

@@ -16,12 +16,6 @@ import {
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import {
DialogServiceAbstraction,
SimpleDialogCloseType,
SimpleDialogOptions,
SimpleDialogType,
} from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
@@ -55,6 +49,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
import { Collection } from "@bitwarden/common/vault/models/domain/collection";
import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response";
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
import { flagEnabled } from "../../../../utils/flags";
import { openEntityEventsDialog } from "../../../admin-console/organizations/manage/entity-events.component";
@@ -125,7 +120,7 @@ export class PeopleComponent
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private organizationUserService: OrganizationUserService,
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
private router: Router,
private groupService: GroupService,
private collectionService: CollectionService
@@ -359,7 +354,7 @@ export class PeopleComponent
: "freeOrgInvLimitReachedNoManageBilling",
this.organization.seats
),
type: SimpleDialogType.PRIMARY,
type: "primary",
};
if (this.organization.canEditSubscription) {
@@ -371,12 +366,12 @@ export class PeopleComponent
const simpleDialog = this.dialogService.openSimpleDialogRef(orgUpgradeSimpleDialogOpts);
firstValueFrom(simpleDialog.closed).then((result: SimpleDialogCloseType | undefined) => {
firstValueFrom(simpleDialog.closed).then((result: boolean | undefined) => {
if (!result) {
return;
}
if (result == SimpleDialogCloseType.ACCEPT && this.organization.canEditSubscription) {
if (result && this.organization.canEditSubscription) {
this.router.navigate(["/organizations", this.organization.id, "billing", "subscription"], {
queryParams: { upgrade: true },
});
@@ -581,7 +576,7 @@ export class PeopleComponent
placeholders: [this.userNamePipe.transform(user)],
},
content: { key: content },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -600,7 +595,7 @@ export class PeopleComponent
title: { key: "revokeAccess", placeholders: [this.userNamePipe.transform(user)] },
content: this.revokeWarningMessage(),
acceptButtonText: { key: "revokeAccess" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -676,7 +671,7 @@ export class PeopleComponent
key: "removeOrgUserNoMasterPasswordDesc",
placeholders: [this.userNamePipe.transform(user)],
},
type: SimpleDialogType.WARNING,
type: "warning",
});
}
}

View File

@@ -1,7 +1,7 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard";
import { AuthGuard } from "@bitwarden/angular/auth/guards";
import {
canAccessOrgAdmin,
canAccessGroupsTab,

View File

@@ -3,7 +3,6 @@ import { FormBuilder, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { combineLatest, lastValueFrom, Subject, switchMap, takeUntil, from, of } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
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";
@@ -15,6 +14,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
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 { DialogService } from "@bitwarden/components";
import { ApiKeyComponent } from "../../../settings/api-key.component";
import { PurgeVaultComponent } from "../../../settings/purge-vault.component";
@@ -77,7 +77,7 @@ export class AccountComponent {
private router: Router,
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private dialogService: DialogServiceAbstraction,
private dialogService: DialogService,
private formBuilder: FormBuilder
) {}

View File

@@ -3,7 +3,6 @@ import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { combineLatest, Subject, takeUntil } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
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";
@@ -15,6 +14,7 @@ import { Verification } from "@bitwarden/common/types/verification";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { DialogService } from "@bitwarden/components";
import { UserVerificationModule } from "../../../../auth/shared/components/user-verification";
import { SharedModule } from "../../../../shared/shared.module";
@@ -168,7 +168,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy {
* @param config Configuration for the dialog
*/
export function openDeleteOrganizationDialog(
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
config: DialogConfig<DeleteOrganizationDialogParams>
) {
return dialogService.open<DeleteOrganizationDialogResult, DeleteOrganizationDialogParams>(

View File

@@ -1,8 +1,11 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { concatMap, takeUntil } from "rxjs";
import { tap } from "rxjs/operators";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -24,17 +27,23 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
messagingService: MessagingService,
policyService: PolicyService,
private route: ActivatedRoute,
stateService: StateService
stateService: StateService,
private organizationService: OrganizationService
) {
super(apiService, modalService, messagingService, policyService, stateService);
}
async ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await super.ngOnInit();
});
this.route.params
.pipe(
tap((params) => {
this.organizationId = params.organizationId;
this.organization = this.organizationService.get(this.organizationId);
}),
concatMap(async () => await super.ngOnInit()),
takeUntil(this.destroy$)
)
.subscribe();
}
async manage(type: TwoFactorProviderType) {
@@ -43,8 +52,7 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent);
duoComp.type = TwoFactorProviderType.OrganizationDuo;
duoComp.organizationId = this.organizationId;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
duoComp.onUpdated.subscribe((enabled: boolean) => {
duoComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => {
this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo);
});
break;

View File

@@ -3,7 +3,6 @@ import { ActivatedRoute, Router } from "@angular/router";
import { lastValueFrom, Observable, Subject } from "rxjs";
import { first, map, takeUntil } from "rxjs/operators";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
@@ -14,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { OrganizationPlansComponent } from "../../../billing/settings/organization-plans.component";
import {
@@ -62,7 +62,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
private syncService: SyncService,
private validationService: ValidationService,
private organizationService: OrganizationService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async ngOnInit() {

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
import { UntypedFormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
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";
@@ -12,6 +11,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
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";
import { DialogService } from "@bitwarden/components";
import { VaultExportServiceAbstraction } from "@bitwarden/exporter/vault-export";
import { ExportComponent } from "../../../../tools/import-export/export.component";
@@ -34,7 +34,7 @@ export class OrganizationExportComponent extends ExportComponent {
userVerificationService: UserVerificationService,
formBuilder: UntypedFormBuilder,
fileDownloadService: FileDownloadService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
cryptoService,

View File

@@ -3,7 +3,6 @@ import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { switchMap, takeUntil } from "rxjs/operators";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import {
canAccessVaultTab,
@@ -17,6 +16,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { ImportServiceAbstraction } from "@bitwarden/importer";
import { ImportComponent } from "../../../../tools/import-export/import.component";
@@ -44,7 +44,7 @@ export class OrganizationImportComponent extends ImportComponent {
logService: LogService,
modalService: ModalService,
syncService: SyncService,
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
folderService: FolderService,
collectionService: CollectionService,
formBuilder: FormBuilder
@@ -91,7 +91,7 @@ export class OrganizationImportComponent extends ImportComponent {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" },
content: { key: "importWarning", placeholders: [this.organization.name] },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {

View File

@@ -58,8 +58,8 @@ export class EnrollMasterPasswordReset {
const publicKey = Utils.fromB64ToArray(orgKeys.publicKey);
// RSA Encrypt user's encKey.key with organization public key
const encKey = await this.cryptoService.getEncKey();
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey);
const userKey = await this.cryptoService.getUserKey();
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey);
keyString = encryptedKey.encryptedString;
toastStringRef = "enrollPasswordResetSuccess";

View File

@@ -2,12 +2,12 @@ import { formatDate } from "@angular/common";
import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
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";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "[sponsoring-org-row]",
@@ -32,7 +32,7 @@ export class SponsoringOrgRowComponent implements OnInit {
private i18nService: I18nService,
private logService: LogService,
private platformUtilsService: PlatformUtilsService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async ngOnInit() {
@@ -73,7 +73,7 @@ export class SponsoringOrgRowComponent implements OnInit {
title: `${this.i18nService.t("remove")} ${this.sponsoringOrg.familySponsorshipFriendlyName}?`,
content: { key: "revokeSponsorshipConfirmation" },
acceptButtonText: { key: "remove" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {

View File

@@ -5,14 +5,12 @@ import { NavigationEnd, Router } from "@angular/router";
import * as jq from "jquery";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { Subject, takeUntil } from "rxjs";
import Swal from "sweetalert2";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
@@ -27,6 +25,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { PolicyListService } from "./admin-console/core/policy-list.service";
import {
@@ -82,7 +81,7 @@ export class AppComponent implements OnDestroy, OnInit {
protected policyListService: PolicyListService,
private keyConnectorService: KeyConnectorService,
private configService: ConfigServiceAbstraction,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
ngOnInit() {
@@ -138,14 +137,14 @@ export class AppComponent implements OnDestroy, OnInit {
case "syncStarted":
break;
case "syncCompleted":
await this.configService.fetchServerConfig();
this.configService.triggerServerConfigFetch();
break;
case "upgradeOrganization": {
const upgradeConfirmed = await this.dialogService.openSimpleDialog({
title: { key: "upgradeOrganization" },
content: { key: "upgradeOrganizationDesc" },
acceptButtonText: { key: "upgradeOrganization" },
type: SimpleDialogType.INFO,
type: "info",
});
if (upgradeConfirmed) {
this.router.navigate([
@@ -162,7 +161,7 @@ export class AppComponent implements OnDestroy, OnInit {
title: { key: "premiumRequired" },
content: { key: "premiumRequiredDesc" },
acceptButtonText: { key: "upgrade" },
type: SimpleDialogType.SUCCESS,
type: "success",
});
if (premiumConfirmed) {
this.router.navigate(["settings/subscription/premium"]);
@@ -174,7 +173,7 @@ export class AppComponent implements OnDestroy, OnInit {
title: { key: "emailVerificationRequired" },
content: { key: "emailVerificationRequiredDesc" },
acceptButtonText: { key: "learnMore" },
type: SimpleDialogType.INFO,
type: "info",
});
if (emailVerificationConfirmed) {
this.platformUtilsService.launchUri(
@@ -204,10 +203,6 @@ export class AppComponent implements OnDestroy, OnInit {
for (const modal of modals) {
(jq(modal) as any).modal("hide");
}
if (document.querySelector(".swal-modal") != null) {
Swal.close(undefined);
}
}
});
@@ -258,7 +253,6 @@ export class AppComponent implements OnDestroy, OnInit {
}
await this.stateService.clean({ userId: userId });
Swal.close();
if (redirect) {
this.router.navigate(["/"]);
}

View File

@@ -9,9 +9,14 @@ import { AppComponent } from "./app.component";
import { CoreModule } from "./core";
import { OssRoutingModule } from "./oss-routing.module";
import { OssModule } from "./oss.module";
import { SendComponent } from "./tools/send/send.component";
import { WildcardRoutingModule } from "./wildcard-routing.module";
/**
* This is the AppModule for the OSS version of Bitwarden.
* `bitwarden_license/bit-web/app.module.ts` contains the commercial version.
*
* You probably do not want to modify this file. Consider editing `oss.module.ts` instead.
*/
@NgModule({
imports: [
OssModule,
@@ -22,7 +27,6 @@ import { WildcardRoutingModule } from "./wildcard-routing.module";
DragDropModule,
LayoutModule,
OssRoutingModule,
SendComponent,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
declarations: [AppComponent],

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import {
OrganizationUserAcceptInitRequest,
@@ -18,6 +19,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { OrgKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { BaseAcceptComponent } from "../common/base.accept.component";
@@ -42,7 +44,8 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
private logService: LogService,
private organizationApiService: OrganizationApiServiceAbstraction,
private organizationUserService: OrganizationUserService,
private messagingService: MessagingService
private messagingService: MessagingService,
private apiService: ApiService
) {
super(router, platformUtilsService, i18nService, route, stateService);
}
@@ -66,6 +69,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
}
await this.actionPromise;
await this.apiService.refreshIdentityToken();
await this.stateService.setOrganizationInvitation(null);
this.platformUtilService.showToast(
"success",
@@ -108,16 +112,14 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
const request = new OrganizationUserAcceptInitRequest();
request.token = qParams.token;
const [encryptedOrgShareKey, orgShareKey] = await this.cryptoService.makeShareKey();
const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair(
orgShareKey
);
const [encryptedOrgKey, orgKey] = await this.cryptoService.makeOrgKey<OrgKey>();
const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair(orgKey);
const collection = await this.cryptoService.encrypt(
this.i18nService.t("defaultCollection"),
orgShareKey
orgKey
);
request.key = encryptedOrgShareKey.encryptedString;
request.key = encryptedOrgKey.encryptedString;
request.keys = new OrganizationKeysRequest(
orgPublicKey,
encryptedOrgPrivateKey.encryptedString
@@ -141,8 +143,8 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
const publicKey = Utils.fromB64ToArray(response.publicKey);
// RSA Encrypt user's encKey.key with organization public key
const encKey = await this.cryptoService.getEncKey();
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey);
const userKey = await this.cryptoService.getUserKey();
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey);
// Add reset password key to accept request
request.resetPasswordKey = encryptedKey.encryptedString;

View File

@@ -2,13 +2,13 @@ import { Component, NgZone } from "@angular/core";
import { Router } from "@angular/router";
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.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 { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -17,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { DialogService } from "@bitwarden/components";
import { RouterService } from "../core";
@@ -38,12 +39,13 @@ export class LockComponent extends BaseLockComponent {
stateService: StateService,
apiService: ApiService,
logService: LogService,
keyConnectorService: KeyConnectorService,
ngZone: NgZone,
policyApiService: PolicyApiServiceAbstraction,
policyService: InternalPolicyService,
passwordStrengthService: PasswordStrengthServiceAbstraction,
dialogService: DialogServiceAbstraction
dialogService: DialogService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
userVerificationService: UserVerificationService
) {
super(
router,
@@ -57,12 +59,13 @@ export class LockComponent extends BaseLockComponent {
stateService,
apiService,
logService,
keyConnectorService,
ngZone,
policyApiService,
policyService,
passwordStrengthService,
dialogService
dialogService,
deviceTrustCryptoService,
userVerificationService
);
}

View File

@@ -0,0 +1,105 @@
<div class="tw-container tw-mx-auto">
<div
class="tw-mx-auto tw-mt-5 tw-flex tw-max-w-lg tw-flex-col tw-items-center tw-justify-center tw-p-8"
>
<div class="tw-mb-6">
<img class="logo logo-themed" alt="Bitwarden" />
</div>
<ng-container *ngIf="loading">
<p class="text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</p>
</ng-container>
<div
*ngIf="!loading"
class="tw-w-full tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<ng-container *ngIf="data.state == State.ExistingUserUntrustedDevice">
<h2 bitTypography="h2" class="tw-mb-6">{{ "loginInitiated" | i18n }}</h2>
<p bitTypography="body1" class="tw-mb-6">
{{ "deviceApprovalRequired" | i18n }}
</p>
<form [formGroup]="rememberDeviceForm">
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="rememberDevice" />
<bit-label>{{ "rememberThisDevice" | i18n }} </bit-label>
<bit-hint bitTypography="body2">{{ "uncheckIfPublicDevice" | i18n }}</bit-hint>
</bit-form-control>
</form>
<div class="tw-mb-6 tw-flex tw-flex-col tw-space-y-3">
<button
*ngIf="data.showApproveFromOtherDeviceBtn"
(click)="approveFromOtherDevice()"
bitButton
type="button"
buttonType="primary"
block
>
{{ "approveFromYourOtherDevice" | i18n }}
</button>
<button
*ngIf="data.showReqAdminApprovalBtn"
(click)="requestAdminApproval()"
bitButton
type="button"
buttonType="secondary"
>
{{ "requestAdminApproval" | i18n }}
</button>
<button
*ngIf="data.showApproveWithMasterPasswordBtn"
(click)="approveWithMasterPassword()"
bitButton
type="button"
buttonType="secondary"
block
>
{{ "approveWithMasterPassword" | i18n }}
</button>
</div>
</ng-container>
<ng-container *ngIf="data.state == State.NewUser">
<h2 bitTypography="h2" class="tw-mb-6">{{ "loggedInExclamation" | i18n }}</h2>
<form [formGroup]="rememberDeviceForm">
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="rememberDevice" />
<bit-label>{{ "rememberThisDevice" | i18n }} </bit-label>
<bit-hint bitTypography="body2">{{ "uncheckIfPublicDevice" | i18n }}</bit-hint>
</bit-form-control>
</form>
<button
bitButton
type="button"
buttonType="primary"
block
class="tw-mb-6"
[bitAction]="createUserAction"
>
{{ "continue" | i18n }}
</button>
</ng-container>
<hr class="tw-mb-6 tw-mt-0" />
<div class="tw-m-0 tw-text-sm">
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ data.userEmail }}</p>
<a [routerLink]="[]" (click)="logOut()">{{ "notYou" | i18n }}</a>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,21 @@
import { Component } from "@angular/core";
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
@Component({
selector: "web-login-decryption-options",
templateUrl: "login-decryption-options.component.html",
})
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
override async createUser(): Promise<void> {
try {
await super.createUser();
await this.router.navigate(["/vault"]);
} catch (error) {
this.validationService.showError(error);
}
}
createUserAction = async (): Promise<void> => {
return this.createUser();
};
}

View File

@@ -5,42 +5,71 @@
>
<div>
<img class="logo logo-themed" alt="Bitwarden" />
<p class="tw-mx-4 tw-mb-4 tw-mt-3 tw-text-center tw-text-xl">
{{ "loginOrCreateNewAccount" | i18n }}
</p>
<div
class="tw-mt-3 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<h2 class="tw-mb-6 tw-text-xl tw-font-semibold">{{ "logInInitiated" | i18n }}</h2>
<ng-container *ngIf="state == StateEnum.StandardAuthRequest">
<p class="tw-mx-4 tw-mb-4 tw-mt-3 tw-text-center tw-text-xl">
{{ "loginOrCreateNewAccount" | i18n }}
</p>
<div class="tw-text-light">
<p class="tw-mb-6">{{ "notificationSentDevice" | i18n }}</p>
<div
class="tw-mt-3 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<h2 class="tw-mb-6 tw-text-xl tw-font-semibold">{{ "loginInitiated" | i18n }}</h2>
<p class="tw-mb-6">
{{ "fingerprintMatchInfo" | i18n }}
</p>
<div class="tw-text-light">
<p class="tw-mb-6">{{ "notificationSentDevice" | i18n }}</p>
<p class="tw-mb-6">
{{ "fingerprintMatchInfo" | i18n }}
</p>
</div>
<div class="tw-mb-6">
<h4 class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</h4>
<p>
<code>{{ fingerprintPhrase }}</code>
</p>
</div>
<div class="tw-my-10" *ngIf="showResendNotification">
<a [routerLink]="[]" disabled="true" (click)="startPasswordlessLogin()">{{
"resendNotification" | i18n
}}</a>
</div>
<hr />
<div class="tw-text-light tw-mt-3">
{{ "loginWithDeviceEnabledNote" | i18n }}
<a routerLink="/login">{{ "viewAllLoginOptions" | i18n }}</a>
</div>
</div>
</ng-container>
<ng-container *ngIf="state == StateEnum.AdminAuthRequest">
<div
class="tw-mt-3 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<h2 class="tw-mb-6 tw-text-xl tw-font-semibold">{{ "adminApprovalRequested" | i18n }}</h2>
<div class="tw-mb-6">
<h4 class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</h4>
<p>
<code>{{ fingerprintPhrase }}</code>
</p>
<div class="tw-text-light">
<p class="tw-mb-6">{{ "adminApprovalRequestSentToAdmins" | i18n }}</p>
<p class="tw-mb-6">{{ "youWillBeNotifiedOnceApproved" | i18n }}</p>
</div>
<div class="tw-mb-6">
<h4 class="tw-font-semibold">{{ "fingerprintPhraseHeader" | i18n }}</h4>
<p>
<code>{{ fingerprintPhrase }}</code>
</p>
</div>
<hr />
<div class="tw-text-light tw-mt-3">
{{ "troubleLoggingIn" | i18n }}
<a routerLink="/login-initiated">{{ "viewAllLoginOptions" | i18n }}</a>
</div>
</div>
<div class="tw-my-10" *ngIf="showResendNotification">
<a [routerLink]="[]" disabled="true" (click)="startPasswordlessLogin()">{{
"resendNotification" | i18n
}}</a>
</div>
<hr />
<div class="tw-text-light tw-mt-3">
{{ "loginWithDeviceEnabledNote" | i18n }}
<a routerLink="/login">{{ "viewAllLoginOptions" | i18n }}</a>
</div>
</div>
</ng-container>
</div>
</div>

View File

@@ -4,7 +4,9 @@ import { Router } from "@angular/router";
import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component";
import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -41,7 +43,9 @@ export class LoginWithDeviceComponent
anonymousHubService: AnonymousHubService,
validationService: ValidationService,
stateService: StateService,
loginService: LoginService
loginService: LoginService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
authReqCryptoService: AuthRequestCryptoServiceAbstraction
) {
super(
router,
@@ -58,7 +62,9 @@ export class LoginWithDeviceComponent
anonymousHubService,
validationService,
stateService,
loginService
loginService,
deviceTrustCryptoService,
authReqCryptoService
);
}
}

View File

@@ -83,7 +83,11 @@
</div>
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
<iframe
id="hcaptcha_iframe"
height="80"
sandbox="allow-scripts allow-same-origin"
></iframe>
</div>
<div class="tw-mb-3 tw-flex tw-space-x-4">

View File

@@ -6,7 +6,6 @@ import { first } from "rxjs/operators";
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
@@ -14,6 +13,7 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";

View File

@@ -4,12 +4,13 @@ import { CheckboxModule } from "@bitwarden/components";
import { SharedModule } from "../../../app/shared";
import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component";
import { LoginWithDeviceComponent } from "./login-with-device.component";
import { LoginComponent } from "./login.component";
@NgModule({
imports: [SharedModule, CheckboxModule],
declarations: [LoginComponent, LoginWithDeviceComponent],
exports: [LoginComponent, LoginWithDeviceComponent],
declarations: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent],
exports: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent],
})
export class LoginModule {}

View File

@@ -35,7 +35,7 @@ export class RecoverTwoFactorComponent {
request.recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase();
request.email = this.email.trim().toLowerCase();
const key = await this.authService.makePreloginKey(this.masterPassword, request.email);
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(this.masterPassword, key);
this.formPromise = this.apiService.postTwoFactorRecover(request);
await this.formPromise;
this.platformUtilsService.showToast(

View File

@@ -89,7 +89,7 @@
</div>
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
<iframe id="hcaptcha_iframe" height="80" sandbox="allow-scripts allow-same-origin"></iframe>
</div>
<div class="tw-mb-4 tw-flex tw-items-start">
<input
@@ -123,8 +123,7 @@
</bit-label>
</div>
<hr />
<div class="tw-flex tw-space-x-2 tw-pt-2">
<div class="tw-space-x-2 tw-pt-2">
<ng-container *ngIf="!accountCreated">
<button
[block]="true"
@@ -135,10 +134,6 @@
>
{{ "createAccount" | i18n }}
</button>
<a bitButton [block]="true" buttonType="secondary" routerLink="/login">
<i class="bwi bwi-sign-in tw-mr-2"></i>
{{ "logIn" | i18n }}
</a>
</ng-container>
<ng-container *ngIf="accountCreated">
<button
@@ -152,6 +147,10 @@
</button>
</ng-container>
</div>
<p class="tw-m-0 tw-mt-5 tw-text-sm">
{{ "alreadyHaveAccount" | i18n }}
<a routerLink="/login">{{ "logIn" | i18n }}</a>
</p>
<bit-error-summary *ngIf="showErrorSummary" [formGroup]="formGroup"></bit-error-summary>
</div>
</form>

View File

@@ -4,7 +4,6 @@ import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -18,6 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-register-form",
@@ -46,7 +46,7 @@ export class RegisterFormComponent extends BaseRegisterComponent {
environmentService: EnvironmentService,
logService: LogService,
auditService: AuditService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
formValidationErrorService,

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/components/set-password.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
@@ -15,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-set-password",
@@ -36,7 +36,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent {
stateService: StateService,
organizationApiService: OrganizationApiServiceAbstraction,
organizationUserService: OrganizationUserService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
i18nService,

View File

@@ -89,12 +89,12 @@
<input
class="form-check-input"
type="checkbox"
id="rotateEncKey"
name="RotateEncKey"
[(ngModel)]="rotateEncKey"
(change)="rotateEncKeyClicked()"
id="rotateUserKey"
name="RotateUserKey"
[(ngModel)]="rotateUserKey"
(change)="rotateUserKeyClicked()"
/>
<label class="form-check-label" for="rotateEncKey">
<label class="form-check-label" for="rotateUserKey">
{{ "rotateAccountEncKey" | i18n }}
</label>
<a

View File

@@ -3,7 +3,6 @@ import { Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
@@ -11,7 +10,9 @@ import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/commo
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EmergencyAccessStatusType } from "@bitwarden/common/auth/enums/emergency-access-status-type";
import { EmergencyAccessUpdateRequest } from "@bitwarden/common/auth/models/request/emergency-access-update.request";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
@@ -23,7 +24,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
MasterKey,
SymmetricCryptoKey,
UserKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { SendWithIdRequest } from "@bitwarden/common/tools/send/models/request/send-with-id.request";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
@@ -32,13 +37,14 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherWithIdRequest } from "@bitwarden/common/vault/models/request/cipher-with-id.request";
import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-change-password",
templateUrl: "change-password.component.html",
})
export class ChangePasswordComponent extends BaseChangePasswordComponent {
rotateEncKey = false;
rotateUserKey = false;
currentMasterPassword: string;
masterPasswordHint: string;
checkForBreaches = true;
@@ -63,7 +69,9 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
private router: Router,
private organizationApiService: OrganizationApiServiceAbstraction,
private organizationUserService: OrganizationUserService,
dialogService: DialogServiceAbstraction
dialogService: DialogService,
private userVerificationService: UserVerificationService,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction
) {
super(
i18nService,
@@ -78,7 +86,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
}
async ngOnInit() {
if (await this.keyConnectorService.getUsesKeyConnector()) {
if (!(await this.userVerificationService.hasMasterPassword())) {
this.router.navigate(["/settings/security/two-factor"]);
}
@@ -88,8 +96,8 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
this.characterMinimumMessage = this.i18nService.t("characterMinimum", this.minimumLength);
}
async rotateEncKeyClicked() {
if (this.rotateEncKey) {
async rotateUserKeyClicked() {
if (this.rotateUserKey) {
const ciphers = await this.cipherService.getAllDecrypted();
let hasOldAttachments = false;
if (ciphers != null) {
@@ -107,7 +115,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
content: { key: "oldAttachmentsNeedFixDesc" },
acceptButtonText: { key: "learnMore" },
cancelButtonText: { key: "close" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (learnMore) {
@@ -115,7 +123,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
"https://bitwarden.com/help/attachments/#add-storage-space"
);
}
this.rotateEncKey = false;
this.rotateUserKey = false;
return;
}
@@ -127,18 +135,18 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
this.i18nService.t("updateEncryptionKeyExportWarning") +
" " +
this.i18nService.t("rotateEncKeyConfirmation"),
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!result) {
this.rotateEncKey = false;
this.rotateUserKey = false;
}
}
}
async submit() {
const hasEncKey = await this.cryptoService.hasEncKey();
if (!hasEncKey) {
const hasUserKey = await this.cryptoService.hasUserKey();
if (!hasUserKey) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("updateKey"));
return;
}
@@ -170,7 +178,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
return false;
}
if (this.rotateEncKey) {
if (this.rotateUserKey) {
await this.syncService.fullSync(true);
}
@@ -179,22 +187,23 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
async performSubmitActions(
newMasterPasswordHash: string,
newKey: SymmetricCryptoKey,
newEncKey: [SymmetricCryptoKey, EncString]
newMasterKey: MasterKey,
newUserKey: [UserKey, EncString]
) {
const masterKey = await this.cryptoService.getOrDeriveMasterKey(this.currentMasterPassword);
const request = new PasswordRequest();
request.masterPasswordHash = await this.cryptoService.hashPassword(
request.masterPasswordHash = await this.cryptoService.hashMasterKey(
this.currentMasterPassword,
null
masterKey
);
request.masterPasswordHint = this.masterPasswordHint;
request.newMasterPasswordHash = newMasterPasswordHash;
request.key = newEncKey[1].encryptedString;
request.key = newUserKey[1].encryptedString;
try {
if (this.rotateEncKey) {
if (this.rotateUserKey) {
this.formPromise = this.apiService.postPassword(request).then(() => {
return this.updateKey(newKey, request.newMasterPasswordHash);
return this.updateKey(newMasterKey, request.newMasterPasswordHash);
});
} else {
this.formPromise = this.apiService.postPassword(request);
@@ -213,16 +222,16 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
}
}
private async updateKey(key: SymmetricCryptoKey, masterPasswordHash: string) {
const encKey = await this.cryptoService.makeEncKey(key);
const privateKey = await this.cryptoService.getPrivateKey();
private async updateKey(masterKey: MasterKey, masterPasswordHash: string) {
const [newUserKey, masterKeyEncUserKey] = await this.cryptoService.makeUserKey(masterKey);
const userPrivateKey = await this.cryptoService.getPrivateKey();
let encPrivateKey: EncString = null;
if (privateKey != null) {
encPrivateKey = await this.cryptoService.encrypt(privateKey, encKey[0]);
if (userPrivateKey != null) {
encPrivateKey = await this.cryptoService.encrypt(userPrivateKey, newUserKey);
}
const request = new UpdateKeyRequest();
request.privateKey = encPrivateKey != null ? encPrivateKey.encryptedString : null;
request.key = encKey[1].encryptedString;
request.key = masterKeyEncUserKey.encryptedString;
request.masterPasswordHash = masterPasswordHash;
const folders = await firstValueFrom(this.folderService.folderViews$);
@@ -230,7 +239,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
if (folders[i].id == null) {
continue;
}
const folder = await this.folderService.encrypt(folders[i], encKey[0]);
const folder = await this.folderService.encrypt(folders[i], newUserKey);
request.folders.push(new FolderWithIdRequest(folder));
}
@@ -240,24 +249,26 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
continue;
}
const cipher = await this.cipherService.encrypt(ciphers[i], encKey[0]);
const cipher = await this.cipherService.encrypt(ciphers[i], newUserKey);
request.ciphers.push(new CipherWithIdRequest(cipher));
}
const sends = await firstValueFrom(this.sendService.sends$);
await Promise.all(
sends.map(async (send) => {
const cryptoKey = await this.cryptoService.decryptToBytes(send.key, null);
send.key = (await this.cryptoService.encrypt(cryptoKey, encKey[0])) ?? send.key;
const sendKey = await this.cryptoService.decryptToBytes(send.key, null);
send.key = (await this.cryptoService.encrypt(sendKey, newUserKey)) ?? send.key;
request.sends.push(new SendWithIdRequest(send));
})
);
await this.deviceTrustCryptoService.rotateDevicesTrust(newUserKey, masterPasswordHash);
await this.apiService.postAccountKey(request);
await this.updateEmergencyAccesses(encKey[0]);
await this.updateEmergencyAccesses(newUserKey);
await this.updateAllResetPasswordKeys(encKey[0], masterPasswordHash);
await this.updateAllResetPasswordKeys(newUserKey, masterPasswordHash);
}
private async updateEmergencyAccesses(encKey: SymmetricCryptoKey) {
@@ -285,7 +296,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
}
}
private async updateAllResetPasswordKeys(encKey: SymmetricCryptoKey, masterPasswordHash: string) {
private async updateAllResetPasswordKeys(userKey: UserKey, masterPasswordHash: string) {
const orgs = await this.organizationService.getAll();
for (const org of orgs) {
@@ -299,7 +310,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
const publicKey = Utils.fromB64ToArray(response?.publicKey);
// Re-enroll - encrypt user's encKey.key with organization public key
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey);
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey);
// Create/Execute request
const request = new OrganizationUserResetPasswordEnrollmentRequest();

View File

@@ -1,6 +1,5 @@
import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -11,6 +10,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "emergency-access-attachments",
@@ -29,7 +29,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
apiService: ApiService,
logService: LogService,
fileDownloadService: FileDownloadService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
cipherService,

View File

@@ -2,7 +2,6 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angu
import { takeUntil } from "rxjs";
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
@@ -17,8 +16,12 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
SymmetricCryptoKey,
UserKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "emergency-access-takeover",
@@ -48,7 +51,7 @@ export class EmergencyAccessTakeoverComponent
policyService: PolicyService,
private apiService: ApiService,
private logService: LogService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
i18nService,
@@ -91,9 +94,9 @@ export class EmergencyAccessTakeoverComponent
);
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted);
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
const oldUserKey = new SymmetricCryptoKey(oldKeyBuffer) as UserKey;
if (oldEncKey == null) {
if (oldUserKey == null) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@@ -102,7 +105,7 @@ export class EmergencyAccessTakeoverComponent
return;
}
const key = await this.cryptoService.makeKey(
const masterKey = await this.cryptoService.makeMasterKey(
this.masterPassword,
this.email,
takeoverResponse.kdf,
@@ -112,12 +115,12 @@ export class EmergencyAccessTakeoverComponent
takeoverResponse.kdfParallelism
)
);
const masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const masterKeyHash = await this.cryptoService.hashMasterKey(this.masterPassword, masterKey);
const encKey = await this.cryptoService.remakeEncKey(key, oldEncKey);
const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, oldUserKey);
const request = new EmergencyAccessPasswordRequest();
request.newMasterPasswordHash = masterPasswordHash;
request.newMasterPasswordHash = masterKeyHash;
request.key = encKey[1].encryptedString;
this.apiService.postEmergencyAccessPassword(this.emergencyAccessId, request);

View File

@@ -5,7 +5,10 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EmergencyAccessViewResponse } from "@bitwarden/common/auth/models/response/emergency-access.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
SymmetricCryptoKey,
UserKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
@@ -87,13 +90,13 @@ export class EmergencyAccessViewComponent implements OnInit {
const decCiphers: CipherView[] = [];
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(response.keyEncrypted);
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
const oldUserKey = new SymmetricCryptoKey(oldKeyBuffer) as UserKey;
const promises: any[] = [];
ciphers.forEach((cipherResponse) => {
const cipherData = new CipherData(cipherResponse);
const cipher = new Cipher(cipherData);
promises.push(cipher.decrypt(oldEncKey).then((c) => decCiphers.push(c)));
promises.push(cipher.decrypt(oldUserKey).then((c) => decCiphers.push(c)));
});
await Promise.all(promises);

View File

@@ -1,7 +1,6 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -19,6 +18,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { EmergencyAccessAddEditComponent } from "./emergency-access-add-edit.component";
import { EmergencyAccessConfirmComponent } from "./emergency-access-confirm.component";
@@ -56,7 +56,7 @@ export class EmergencyAccessComponent implements OnInit {
private logService: LogService,
private stateService: StateService,
private organizationService: OrganizationService,
protected dialogService: DialogServiceAbstraction
protected dialogService: DialogService
) {}
async ngOnInit() {
@@ -174,7 +174,7 @@ export class EmergencyAccessComponent implements OnInit {
const confirmed = await this.dialogService.openSimpleDialog({
title: this.userNamePipe.transform(details),
content: { key: "removeUserConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -207,7 +207,7 @@ export class EmergencyAccessComponent implements OnInit {
placeholders: [details.waitTimeDays.toString()],
},
acceptButtonText: { key: "requestAccess" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -236,7 +236,7 @@ export class EmergencyAccessComponent implements OnInit {
placeholders: [this.userNamePipe.transform(details), type],
},
acceptButtonText: { key: "approve" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -300,9 +300,12 @@ export class EmergencyAccessComponent implements OnInit {
}
}
// Encrypt the master password hash using the grantees public key, and send it to bitwarden for escrow.
// Encrypt the user key with the grantees public key, and send it to bitwarden for escrow.
private async doConfirmation(details: EmergencyAccessGranteeDetailsResponse) {
const encKey = await this.cryptoService.getEncKey();
const userKey = await this.cryptoService.getUserKey();
if (!userKey) {
throw new Error("No user key found");
}
const publicKeyResponse = await this.apiService.getUserPublicKey(details.granteeId);
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
@@ -315,7 +318,7 @@ export class EmergencyAccessComponent implements OnInit {
// Ignore errors since it's just a debug message
}
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey);
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey);
const request = new EmergencyAccessConfirmRequest();
request.key = encryptedKey.encryptedString;
await this.apiService.postEmergencyAccessConfirm(details.id, request);

View File

@@ -1,6 +1,5 @@
import { Component } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
@@ -18,6 +17,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { DialogService } from "@bitwarden/components";
import { AddEditComponent as BaseAddEditComponent } from "../../../vault/individual-vault/add-edit.component";
@@ -47,7 +47,7 @@ export class EmergencyAddEditComponent extends BaseAddEditComponent {
organizationService: OrganizationService,
logService: LogService,
sendApiService: SendApiService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
cipherService,

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -12,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -52,7 +52,7 @@ export class TwoFactorAuthenticatorComponent
platformUtilsService: PlatformUtilsService,
logService: LogService,
private stateService: StateService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
apiService,

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -11,6 +10,7 @@ import { AuthResponseBase } from "@bitwarden/common/auth/types/auth-response";
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";
import { DialogService } from "@bitwarden/components";
@Directive()
export abstract class TwoFactorBaseComponent {
@@ -32,7 +32,7 @@ export abstract class TwoFactorBaseComponent {
protected platformUtilsService: PlatformUtilsService,
protected logService: LogService,
protected userVerificationService: UserVerificationService,
protected dialogService: DialogServiceAbstraction
protected dialogService: DialogService
) {}
protected auth(authResponse: AuthResponseBase) {
@@ -54,7 +54,7 @@ export abstract class TwoFactorBaseComponent {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "disable" },
content: { key: "twoStepDisableDesc" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -10,6 +9,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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";
import { DialogService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -32,7 +32,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
platformUtilsService: PlatformUtilsService,
logService: LogService,
userVerificationService: UserVerificationService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
apiService,

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -12,6 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -36,7 +36,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
logService: LogService,
userVerificationService: UserVerificationService,
private stateService: StateService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
apiService,

View File

@@ -1,16 +1,25 @@
<div [ngClass]="tabbedHeader ? 'tabbed-header' : 'page-header'">
<h1 *ngIf="!organizationId">{{ "twoStepLogin" | i18n }}</h1>
<h1 *ngIf="organizationId">{{ "twoStepLoginEnforcement" | i18n }}</h1>
<h1 *ngIf="!organizationId || !isEnterpriseOrg">{{ "twoStepLogin" | i18n }}</h1>
<h1 *ngIf="organizationId && isEnterpriseOrg">{{ "twoStepLoginEnforcement" | i18n }}</h1>
</div>
<p *ngIf="!organizationId">{{ "twoStepLoginDesc" | i18n }}</p>
<ng-container *ngIf="organizationId">
<p>
{{ "twoStepLoginOrganizationDescStart" | i18n }}
<a routerLink="../policies">{{ "twoStepLoginPolicy" | i18n }}.</a>
<br />
{{ "twoStepLoginOrganizationDuoDesc" | i18n }}
<ng-container *ngIf="isEnterpriseOrg; else teamsDescription">
{{ "twoStepLoginEnterpriseDescStart" | i18n }}
<a routerLink="../policies">{{ "twoStepLoginPolicy" | i18n }}.</a>
<br />
{{ "twoStepLoginOrganizationDuoDesc" | i18n }}
<br />
<br />
<p>{{ "twoStepLoginOrganizationSsoDesc" | i18n }}</p>
</ng-container>
<ng-template #teamsDescription>
{{ "twoStepLoginTeamsDesc" | i18n }}
<br />
{{ "twoStepLoginOrganizationDuoDesc" | i18n }}
</ng-template>
</p>
<p>{{ "twoStepLoginOrganizationSsoDesc" | i18n }}</p>
</ng-container>
<bit-callout type="warning" *ngIf="!organizationId">
<p>{{ "twoStepLoginRecoveryWarning" | i18n }}</p>

View File

@@ -6,8 +6,10 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service";
import { ProductType } from "@bitwarden/common/enums";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@@ -36,6 +38,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
webAuthnModalRef: ViewContainerRef;
organizationId: string;
organization: Organization;
providers: any[] = [];
canAccessPremium: boolean;
showPolicyWarning = false;
@@ -45,7 +48,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
tabbedHeader = true;
private destroy$ = new Subject<void>();
protected destroy$ = new Subject<void>();
private twoFactorAuthPolicyAppliesToActiveUser: boolean;
constructor(
@@ -202,11 +205,15 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
this.evaluatePolicies();
}
private async evaluatePolicies() {
private evaluatePolicies() {
if (this.organizationId == null && this.providers.filter((p) => p.enabled).length === 1) {
this.showPolicyWarning = this.twoFactorAuthPolicyAppliesToActiveUser;
} else {
this.showPolicyWarning = false;
}
}
get isEnterpriseOrg() {
return this.organization?.planProductType === ProductType.Enterprise;
}
}

View File

@@ -1,6 +1,5 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="modal-body">
<p>{{ "twoStepLoginAuthDesc" | i18n }}</p>
<app-user-verification [(ngModel)]="secret" ngDefaultControl name="secret">
</app-user-verification>
</div>

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -15,6 +14,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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";
import { DialogService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -51,7 +51,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
private ngZone: NgZone,
logService: LogService,
userVerificationService: UserVerificationService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
apiService,
@@ -98,7 +98,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
const confirmed = await this.dialogService.openSimpleDialog({
title: name,
content: { key: "removeU2fConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {

View File

@@ -1,6 +1,5 @@
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/auth/abstractions/user-verification/user-verification.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -10,6 +9,7 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
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";
import { DialogService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -38,7 +38,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
platformUtilsService: PlatformUtilsService,
logService: LogService,
userVerificationService: UserVerificationService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
apiService,

View File

@@ -3,11 +3,11 @@ 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";
import { DialogService } from "@bitwarden/components";
export interface UserVerificationPromptParams {
confirmDescription: string;
@@ -50,7 +50,7 @@ export class UserVerificationPromptComponent extends BaseUserVerificationPrompt
* @param config Configuration for the dialog
*/
export const openUserVerificationPrompt = (
dialogService: DialogServiceAbstraction,
dialogService: DialogService,
config: DialogConfig<UserVerificationPromptParams>
) => {
return dialogService.open<boolean, UserVerificationPromptParams>(

View File

@@ -1,4 +1,4 @@
<ng-container *ngIf="!usesKeyConnector">
<ng-container *ngIf="hasMasterPassword">
<bit-form-field disableMargin>
<bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
@@ -14,7 +14,7 @@
<bit-hint>{{ "confirmIdentity" | i18n }}</bit-hint>
</bit-form-field>
</ng-container>
<ng-container *ngIf="usesKeyConnector">
<ng-container *ngIf="!hasMasterPassword">
<div class="tw-mb-6">
<label class="tw-block">{{ "sendVerificationCode" | i18n }}</label>
<button type="button" bitButton buttonType="secondary" [bitAction]="requestOTP" appAutofocus>

View File

@@ -10,6 +10,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -39,7 +40,8 @@ export class SsoComponent extends BaseSsoComponent {
logService: LogService,
private orgDomainApiService: OrgDomainApiServiceAbstraction,
private loginService: LoginService,
private validationService: ValidationService
private validationService: ValidationService,
configService: ConfigServiceAbstraction
) {
super(
authService,
@@ -52,7 +54,8 @@ export class SsoComponent extends BaseSsoComponent {
cryptoFunctionService,
environmentService,
passwordGenerationService,
logService
logService,
configService
);
this.redirectUri = window.location.origin + "/sso-connector.html";
this.clientId = "web";

View File

@@ -63,7 +63,6 @@
{{ "startYour7DayFreeTrialOfBitwardenFor" | i18n : org }}
</h2>
<environment-selector
[hasFlags]="true"
class="tw-mr-4 tw-mt-6 tw-flex-shrink-0 tw-text-end"
></environment-selector>
</div>

View File

@@ -94,6 +94,13 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
if (this.referenceData.id === "") {
this.referenceData.id = null;
} else {
// Matches "_ga_QBRN562QQQ=value1.value2.session" and captures values and session.
const regex = /_ga_QBRN562QQQ=([^.]+)\.([^.]+)\.(\d+)/;
const match = document.cookie.match(regex);
if (match) {
this.referenceData.session = match[3];
}
}
}

View File

@@ -80,7 +80,7 @@
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
<div id="web-authn-frame" class="mb-3">
<iframe id="webauthn_iframe"></iframe>
<iframe id="webauthn_iframe" sandbox="allow-scripts allow-same-origin"></iframe>
</div>
</ng-container>
<ng-container
@@ -90,7 +90,10 @@
"
>
<div id="duo-frame" class="mb-3">
<iframe id="duo_iframe"></iframe>
<iframe
id="duo_iframe"
sandbox="allow-scripts allow-forms allow-same-origin"
></iframe>
</div>
</ng-container>
<i
@@ -115,7 +118,11 @@
</ng-container>
<hr />
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
<iframe
id="hcaptcha_iframe"
height="80"
sandbox="allow-scripts allow-same-origin"
></iframe>
</div>
<div class="d-flex mb-3">
<button

View File

@@ -1,7 +1,8 @@
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { Component, Inject, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component";
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
@@ -9,6 +10,7 @@ import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -42,7 +44,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
twoFactorService: TwoFactorService,
appIdService: AppIdService,
private routerService: RouterService,
loginService: LoginService
loginService: LoginService,
configService: ConfigServiceAbstraction,
@Inject(WINDOW) protected win: Window
) {
super(
authService,
@@ -50,14 +54,15 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
i18nService,
apiService,
platformUtilsService,
window,
win,
environmentService,
stateService,
route,
logService,
twoFactorService,
appIdService,
loginService
loginService,
configService
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}
@@ -81,7 +86,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
);
}
async goAfterLogIn() {
goAfterLogIn = async () => {
this.loginService.clearValues();
const previousUrl = this.routerService.getPreviousUrl();
if (previousUrl) {
@@ -103,9 +108,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
this.router.navigate([this.successRoute], {
queryParams: {
identifier: this.identifier,
identifier: this.orgIdentifier,
},
});
}
}
};
}

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
@@ -13,6 +12,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-update-password",
@@ -31,7 +31,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
logService: LogService,
stateService: StateService,
userVerificationService: UserVerificationService,
dialogService: DialogServiceAbstraction
dialogService: DialogService
) {
super(
router,

View File

@@ -3,7 +3,6 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { concatMap, Subject, takeUntil } from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
@@ -19,6 +18,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import {
BillingSyncApiKeyComponent,
@@ -47,8 +47,8 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
firstLoaded = false;
loading: boolean;
private readonly _smBetaEndingDate = new Date(2023, 7, 25);
private readonly _smGracePeriodEndingDate = new Date(2023, 9, 24);
private readonly _smBetaEndingDate = new Date(2023, 7, 15);
private readonly _smGracePeriodEndingDate = new Date(2023, 10, 14);
private destroy$ = new Subject<void>();
@@ -61,7 +61,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private route: ActivatedRoute,
private dialogService: DialogServiceAbstraction,
private dialogService: DialogService,
private configService: ConfigServiceAbstraction,
private datePipe: DatePipe
) {}
@@ -135,7 +135,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
this.loading = false;
// Remove the remaining lines when the sm-ga-billing flag is deleted
const smBillingEnabled = await this.configService.getFeatureFlagBool(
const smBillingEnabled = await this.configService.getFeatureFlag<boolean>(
FeatureFlag.SecretsManagerBilling
);
this.showSecretsManagerSubscribe = this.showSecretsManagerSubscribe && smBillingEnabled;
@@ -279,7 +279,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "cancelSubscription" },
content: { key: "cancelConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -307,7 +307,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "reinstateSubscription" },
content: { key: "reinstateConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -379,7 +379,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
title: { key: "removeSponsorship" },
content: { key: "removeSponsorshipConfirmation" },
acceptButtonText: { key: "remove" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {

View File

@@ -7,6 +7,7 @@ import {
Output,
ViewChild,
} from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -79,7 +80,7 @@ export class AddCreditComponent implements OnInit {
this.email = this.subject;
this.ppButtonCustomField = "user_id:" + this.userId;
}
this.region = await this.configService.getCloudRegion();
this.region = await firstValueFrom(this.configService.cloudRegion$);
this.ppButtonCustomField += ",account_credit:1";
this.ppButtonCustomField += `,region:${this.region}`;
this.returnUrl = window.location.href;

View File

@@ -329,7 +329,7 @@
>
{{ "submit" | i18n }}
</button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" *ngIf="showCancel">
<button type="button" buttonType="secondary" bitButton (click)="cancel()" *ngIf="showCancel">
{{ "cancel" | i18n }}
</button>
</div>

View File

@@ -31,7 +31,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import {
OrgKey,
SymmetricCryptoKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { secretsManagerSubscribeFormFactory } from "../organizations/secrets-manager/sm-subscribe.component";
@@ -166,7 +169,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser;
});
this.showSecretsManagerSubscribe = await this.configService.getFeatureFlagBool(
this.showSecretsManagerSubscribe = await this.configService.getFeatureFlag<boolean>(
FeatureFlag.SecretsManagerBilling,
false
);
@@ -314,16 +317,11 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
return 0;
}
let subTotal = plan.basePrice;
if (plan.hasAdditionalSeatsOption && formValues.userSeats) {
subTotal += this.seatTotal(plan, formValues.userSeats);
}
if (plan.hasAdditionalStorageOption && formValues.additionalServiceAccounts) {
subTotal += this.additionalServiceAccountTotal(this.selectedPlan);
}
return subTotal;
return (
plan.basePrice +
this.seatTotal(plan, formValues.userSeats) +
this.additionalServiceAccountTotal(plan)
);
}
get freeTrial() {
@@ -420,19 +418,19 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
const doSubmit = async (): Promise<string> => {
let orgId: string = null;
if (this.createOrganization) {
const shareKey = await this.cryptoService.makeShareKey();
const key = shareKey[0].encryptedString;
const orgKey = await this.cryptoService.makeOrgKey<OrgKey>();
const key = orgKey[0].encryptedString;
const collection = await this.cryptoService.encrypt(
this.i18nService.t("defaultCollection"),
shareKey[1]
orgKey[1]
);
const collectionCt = collection.encryptedString;
const orgKeys = await this.cryptoService.makeKeyPair(shareKey[1]);
const orgKeys = await this.cryptoService.makeKeyPair(orgKey[1]);
if (this.selfHosted) {
orgId = await this.createSelfHosted(key, collectionCt, orgKeys);
} else {
orgId = await this.createCloudHosted(key, collectionCt, orgKeys, shareKey[1]);
orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]);
}
this.platformUtilsService.showToast(

View File

@@ -112,6 +112,7 @@
*ngIf="showAdjustPayment"
>
</app-adjust-payment>
<p *ngIf="isUnpaid">{{ "paymentChargedWithUnpaidSubscription" | i18n }}</p>
<ng-container *ngIf="forOrganization">
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>
<p>{{ "taxInformationDesc" | i18n }}</p>

View File

@@ -2,16 +2,17 @@ import { Component, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { BillingPaymentResponse } from "@bitwarden/common/billing/models/response/billing-payment.response";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { VerifyBankRequest } from "@bitwarden/common/models/request/verify-bank.request";
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";
import { DialogService } from "@bitwarden/components";
import { TaxInfoComponent } from "./tax-info.component";
@@ -28,9 +29,11 @@ export class PaymentMethodComponent implements OnInit {
showAdjustPayment = false;
showAddCredit = false;
billing: BillingPaymentResponse;
org: OrganizationResponse;
org: OrganizationSubscriptionResponse;
sub: SubscriptionResponse;
paymentMethodType = PaymentMethodType;
organizationId: string;
isUnpaid = false;
verifyBankPromise: Promise<any>;
taxFormPromise: Promise<any>;
@@ -57,7 +60,7 @@ export class PaymentMethodComponent implements OnInit {
private logService: LogService,
private route: ActivatedRoute,
private formBuilder: FormBuilder,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {}
async ngOnInit() {
@@ -83,13 +86,23 @@ export class PaymentMethodComponent implements OnInit {
if (this.forOrganization) {
const billingPromise = this.organizationApiService.getBilling(this.organizationId);
const orgPromise = this.organizationApiService.get(this.organizationId);
const organizationSubscriptionPromise = this.organizationApiService.getSubscription(
this.organizationId
);
[this.billing, this.org] = await Promise.all([billingPromise, orgPromise]);
[this.billing, this.org] = await Promise.all([
billingPromise,
organizationSubscriptionPromise,
]);
} else {
this.billing = await this.apiService.getUserBillingPayment();
const billingPromise = this.apiService.getUserBillingPayment();
const subPromise = this.apiService.getUserSubscription();
[this.billing, this.sub] = await Promise.all([billingPromise, subPromise]);
}
this.isUnpaid = this.subscription?.status === "unpaid" ?? false;
this.loading = false;
}
@@ -100,7 +113,7 @@ export class PaymentMethodComponent implements OnInit {
content: { key: "cannotPerformInAppPurchase" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.WARNING,
type: "warning",
});
return;
@@ -122,11 +135,12 @@ export class PaymentMethodComponent implements OnInit {
content: { key: "cannotPerformInAppPurchase" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.WARNING,
type: "warning",
});
return;
}
this.showAdjustPayment = true;
}
@@ -214,4 +228,8 @@ export class PaymentMethodComponent implements OnInit {
this.paymentSource.type === PaymentMethodType.GoogleInApp)
);
}
get subscription() {
return this.sub?.subscription ?? this.org?.subscription ?? null;
}
}

View File

@@ -4,7 +4,7 @@ import { RouterModule, Routes } from "@angular/router";
import { BillingHistoryViewComponent } from "../../billing/settings/billing-history-view.component";
import { PaymentMethodComponent } from "../../billing/settings/payment-method.component";
import { UserSubscriptionComponent } from "../../billing/settings/user-subscription.component";
import { PremiumComponent } from "../../settings/premium.component";
import { PremiumComponent } from "../../vault/settings/premium.component";
import { SubscriptionComponent } from "./subscription.component";

View File

@@ -21,247 +21,72 @@
<option value="AU">Australia</option>
<option value="IN">India</option>
<option value="-" disabled></option>
<option value="AF">Afghanistan</option>
<option value="AX">Åland Islands</option>
<option value="AL">Albania</option>
<option value="DZ">Algeria</option>
<option value="AS">American Samoa</option>
<option value="AD">Andorra</option>
<option value="AO">Angola</option>
<option value="AI">Anguilla</option>
<option value="AQ">Antarctica</option>
<option value="AG">Antigua and Barbuda</option>
<option value="AR">Argentina</option>
<option value="AM">Armenia</option>
<option value="AW">Aruba</option>
<option value="AT">Austria</option>
<option value="AZ">Azerbaijan</option>
<option value="BS">Bahamas</option>
<option value="BH">Bahrain</option>
<option value="BD">Bangladesh</option>
<option value="BB">Barbados</option>
<option value="BY">Belarus</option>
<option value="BE">Belgium</option>
<option value="BZ">Belize</option>
<option value="BJ">Benin</option>
<option value="BM">Bermuda</option>
<option value="BT">Bhutan</option>
<option value="BO">Bolivia, Plurinational State of</option>
<option value="BQ">Bonaire, Sint Eustatius and Saba</option>
<option value="BA">Bosnia and Herzegovina</option>
<option value="BW">Botswana</option>
<option value="BV">Bouvet Island</option>
<option value="BR">Brazil</option>
<option value="IO">British Indian Ocean Territory</option>
<option value="BN">Brunei Darussalam</option>
<option value="BG">Bulgaria</option>
<option value="BF">Burkina Faso</option>
<option value="BI">Burundi</option>
<option value="KH">Cambodia</option>
<option value="CM">Cameroon</option>
<option value="CV">Cape Verde</option>
<option value="KY">Cayman Islands</option>
<option value="CF">Central African Republic</option>
<option value="TD">Chad</option>
<option value="CL">Chile</option>
<option value="CX">Christmas Island</option>
<option value="CC">Cocos (Keeling) Islands</option>
<option value="CO">Colombia</option>
<option value="KM">Comoros</option>
<option value="CG">Congo</option>
<option value="CD">Congo, the Democratic Republic of the</option>
<option value="CK">Cook Islands</option>
<option value="CR">Costa Rica</option>
<option value="CI">Côte d'Ivoire</option>
<option value="HR">Croatia</option>
<option value="CU">Cuba</option>
<option value="CW">Curaçao</option>
<option value="CY">Cyprus</option>
<option value="CZ">Czech Republic</option>
<option value="DK">Denmark</option>
<option value="DJ">Djibouti</option>
<option value="DM">Dominica</option>
<option value="DO">Dominican Republic</option>
<option value="EC">Ecuador</option>
<option value="EG">Egypt</option>
<option value="SV">El Salvador</option>
<option value="GQ">Equatorial Guinea</option>
<option value="ER">Eritrea</option>
<option value="EE">Estonia</option>
<option value="ET">Ethiopia</option>
<option value="FK">Falkland Islands (Malvinas)</option>
<option value="FO">Faroe Islands</option>
<option value="FJ">Fiji</option>
<option value="FI">Finland</option>
<option value="GF">French Guiana</option>
<option value="PF">French Polynesia</option>
<option value="TF">French Southern Territories</option>
<option value="GA">Gabon</option>
<option value="GM">Gambia</option>
<option value="GE">Georgia</option>
<option value="GH">Ghana</option>
<option value="GI">Gibraltar</option>
<option value="GR">Greece</option>
<option value="GL">Greenland</option>
<option value="GD">Grenada</option>
<option value="GP">Guadeloupe</option>
<option value="GU">Guam</option>
<option value="GT">Guatemala</option>
<option value="GG">Guernsey</option>
<option value="GN">Guinea</option>
<option value="GW">Guinea-Bissau</option>
<option value="GY">Guyana</option>
<option value="HT">Haiti</option>
<option value="HM">Heard Island and McDonald Islands</option>
<option value="VA">Holy See (Vatican City State)</option>
<option value="HN">Honduras</option>
<option value="HK">Hong Kong</option>
<option value="HU">Hungary</option>
<option value="IS">Iceland</option>
<option value="ID">Indonesia</option>
<option value="IR">Iran, Islamic Republic of</option>
<option value="IQ">Iraq</option>
<option value="IE">Ireland</option>
<option value="IM">Isle of Man</option>
<option value="IL">Israel</option>
<option value="IT">Italy</option>
<option value="JM">Jamaica</option>
<option value="JP">Japan</option>
<option value="JE">Jersey</option>
<option value="JO">Jordan</option>
<option value="KZ">Kazakhstan</option>
<option value="KE">Kenya</option>
<option value="KI">Kiribati</option>
<option value="KP">Korea, Democratic People's Republic of</option>
<option value="KR">Korea, Republic of</option>
<option value="KW">Kuwait</option>
<option value="KG">Kyrgyzstan</option>
<option value="LA">Lao People's Democratic Republic</option>
<option value="LV">Latvia</option>
<option value="LB">Lebanon</option>
<option value="LS">Lesotho</option>
<option value="LR">Liberia</option>
<option value="LY">Libya</option>
<option value="LI">Liechtenstein</option>
<option value="LT">Lithuania</option>
<option value="LU">Luxembourg</option>
<option value="MO">Macao</option>
<option value="MK">Macedonia, the former Yugoslav Republic of</option>
<option value="MG">Madagascar</option>
<option value="MW">Malawi</option>
<option value="MY">Malaysia</option>
<option value="MV">Maldives</option>
<option value="ML">Mali</option>
<option value="MT">Malta</option>
<option value="MH">Marshall Islands</option>
<option value="MQ">Martinique</option>
<option value="MR">Mauritania</option>
<option value="MU">Mauritius</option>
<option value="YT">Mayotte</option>
<option value="MX">Mexico</option>
<option value="FM">Micronesia, Federated States of</option>
<option value="MD">Moldova, Republic of</option>
<option value="MC">Monaco</option>
<option value="MN">Mongolia</option>
<option value="ME">Montenegro</option>
<option value="MS">Montserrat</option>
<option value="MA">Morocco</option>
<option value="MZ">Mozambique</option>
<option value="MM">Myanmar</option>
<option value="NA">Namibia</option>
<option value="NR">Nauru</option>
<option value="NP">Nepal</option>
<option value="NL">Netherlands</option>
<option value="NC">New Caledonia</option>
<option value="NZ">New Zealand</option>
<option value="NI">Nicaragua</option>
<option value="NE">Niger</option>
<option value="NG">Nigeria</option>
<option value="NU">Niue</option>
<option value="NF">Norfolk Island</option>
<option value="MP">Northern Mariana Islands</option>
<option value="NO">Norway</option>
<option value="OM">Oman</option>
<option value="PK">Pakistan</option>
<option value="PW">Palau</option>
<option value="PS">Palestinian Territory, Occupied</option>
<option value="PA">Panama</option>
<option value="PG">Papua New Guinea</option>
<option value="PY">Paraguay</option>
<option value="PE">Peru</option>
<option value="PH">Philippines</option>
<option value="PN">Pitcairn</option>
<option value="PL">Poland</option>
<option value="PT">Portugal</option>
<option value="PR">Puerto Rico</option>
<option value="QA">Qatar</option>
<option value="RE">Réunion</option>
<option value="RO">Romania</option>
<option value="RU">Russian Federation</option>
<option value="RW">Rwanda</option>
<option value="BL">Saint Barthélemy</option>
<option value="SH">Saint Helena, Ascension and Tristan da Cunha</option>
<option value="KN">Saint Kitts and Nevis</option>
<option value="LC">Saint Lucia</option>
<option value="MF">Saint Martin (French part)</option>
<option value="PM">Saint Pierre and Miquelon</option>
<option value="VC">Saint Vincent and the Grenadines</option>
<option value="WS">Samoa</option>
<option value="SM">San Marino</option>
<option value="ST">Sao Tome and Principe</option>
<option value="SA">Saudi Arabia</option>
<option value="SN">Senegal</option>
<option value="RS">Serbia</option>
<option value="SC">Seychelles</option>
<option value="SL">Sierra Leone</option>
<option value="SG">Singapore</option>
<option value="SX">Sint Maarten (Dutch part)</option>
<option value="SK">Slovakia</option>
<option value="SI">Slovenia</option>
<option value="SB">Solomon Islands</option>
<option value="SO">Somalia</option>
<option value="ZA">South Africa</option>
<option value="GS">South Georgia and the South Sandwich Islands</option>
<option value="SS">South Sudan</option>
<option value="ES">Spain</option>
<option value="LK">Sri Lanka</option>
<option value="SD">Sudan</option>
<option value="SR">Suriname</option>
<option value="SJ">Svalbard and Jan Mayen</option>
<option value="SZ">Swaziland</option>
<option value="SE">Sweden</option>
<option value="CH">Switzerland</option>
<option value="SY">Syrian Arab Republic</option>
<option value="TW">Taiwan</option>
<option value="TJ">Tajikistan</option>
<option value="TZ">Tanzania, United Republic of</option>
<option value="TH">Thailand</option>
<option value="TL">Timor-Leste</option>
<option value="TG">Togo</option>
<option value="TK">Tokelau</option>
<option value="TO">Tonga</option>
<option value="TT">Trinidad and Tobago</option>
<option value="TN">Tunisia</option>
<option value="TR">Turkey</option>
<option value="TM">Turkmenistan</option>
<option value="TC">Turks and Caicos Islands</option>
<option value="TV">Tuvalu</option>
<option value="UG">Uganda</option>
<option value="UA">Ukraine</option>
<option value="AE">United Arab Emirates</option>
<option value="UM">United States Minor Outlying Islands</option>
<option value="UY">Uruguay</option>
<option value="UZ">Uzbekistan</option>
<option value="VU">Vanuatu</option>
<option value="VE">Venezuela, Bolivarian Republic of</option>
<option value="VN">Viet Nam</option>
<option value="VG">Virgin Islands, British</option>
<option value="VI">Virgin Islands, U.S.</option>
<option value="WF">Wallis and Futuna</option>
<option value="EH">Western Sahara</option>
<option value="YE">Yemen</option>
<option value="ZM">Zambia</option>
<option value="ZW">Zimbabwe</option>
</select>
</div>
</div>

View File

@@ -1,7 +1,6 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@@ -10,6 +9,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-user-subscription",
@@ -36,7 +36,7 @@ export class UserSubscriptionComponent implements OnInit {
private router: Router,
private logService: LogService,
private fileDownloadService: FileDownloadService,
private dialogService: DialogServiceAbstraction,
private dialogService: DialogService,
private environmentService: EnvironmentService
) {
this.selfHosted = platformUtilsService.isSelfHost();
@@ -75,7 +75,7 @@ export class UserSubscriptionComponent implements OnInit {
content: { key: "manageSubscriptionFromStore" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.WARNING,
type: "warning",
});
return;
@@ -84,7 +84,7 @@ export class UserSubscriptionComponent implements OnInit {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "reinstateSubscription" },
content: { key: "reinstateConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -112,7 +112,7 @@ export class UserSubscriptionComponent implements OnInit {
content: { key: "manageSubscriptionFromStore" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.WARNING,
type: "warning",
});
return;
@@ -121,7 +121,7 @@ export class UserSubscriptionComponent implements OnInit {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "cancelSubscription" },
content: { key: "cancelConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -175,7 +175,7 @@ export class UserSubscriptionComponent implements OnInit {
content: { key: "cannotPerformInAppPurchase" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: SimpleDialogType.WARNING,
type: "warning",
});
return;

View File

@@ -2,7 +2,6 @@ import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
@@ -21,6 +20,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { OrganizationUserView } from "../admin-console/organizations/core/views/organization-user.view";
import { UserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
@@ -110,7 +110,7 @@ export abstract class BasePeopleComponent<
private searchPipe: SearchPipe,
protected userNamePipe: UserNamePipe,
protected stateService: StateService,
protected dialogService: DialogServiceAbstraction
protected dialogService: DialogService
) {}
abstract edit(user: UserType): void;
@@ -222,7 +222,7 @@ export abstract class BasePeopleComponent<
return this.dialogService.openSimpleDialog({
title: this.userNamePipe.transform(user),
content: { key: "removeUserConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
}
@@ -252,7 +252,7 @@ export abstract class BasePeopleComponent<
title: { key: "revokeAccess", placeholders: [this.userNamePipe.transform(user)] },
content: this.revokeWarningMessage(),
acceptButtonText: { key: "revokeAccess" },
type: SimpleDialogType.WARNING,
type: "warning",
});
}

View File

@@ -2,7 +2,9 @@
<bit-menu #environmentOptions>
<a
bitMenuItem
[attr.href]="isUsServer ? 'javascript:void(0)' : 'https://vault.bitwarden.com'"
[attr.href]="
isUsServer ? 'javascript:void(0)' : 'https://vault.bitwarden.com' + routeAndParams
"
class="pr-4"
>
<i
@@ -10,12 +12,13 @@
aria-hidden="true"
[style.visibility]="isUsServer ? 'visible' : 'hidden'"
></i>
<img src="../../images/us_flag.png" alt="{{ 'usFlag' | i18n }}" class="pb-1 mr-1" />
{{ "usDomain" | i18n }}
</a>
<a
bitMenuItem
[attr.href]="isEuServer ? 'javascript:void(0)' : 'https://vault.bitwarden.eu'"
[attr.href]="
isEuServer ? 'javascript:void(0)' : 'https://vault.bitwarden.eu' + routeAndParams
"
class="pr-4"
*ngIf="euServerFlagEnabled"
>
@@ -24,28 +27,10 @@
aria-hidden="true"
[style.visibility]="isEuServer ? 'visible' : 'hidden'"
></i>
<img src="../../images/eu_flag.png" alt="{{ 'euFlag' | i18n }}" class="pb-1 mr-1" />
{{ "euDomain" | i18n }}
</a>
</bit-menu>
<span *ngIf="hasFlags" class="!tw-inline-block !tw-min-w-52">
<label>{{ "server" | i18n }}:</label>
<a
[routerLink]="[]"
[bitMenuTriggerFor]="environmentOptions"
class="tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-2"
>
<img
[src]="'../../images/' + selectedRegionImageName + '.png'"
alt="{{ 'selectedRegionFlag' | i18n }}"
class="pb-1 mr-1 tw-ml-1"
/><label class="tw-cursor-pointer !tw-font-normal"
>{{ isEuServer ? ("euDomain" | i18n) : ("usDomain" | i18n) }}
<i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i
></label>
</a>
</span>
<div *ngIf="!hasFlags">
<div>
{{ "server" | i18n }}:
<a [routerLink]="[]" [bitMenuTriggerFor]="environmentOptions">
<b>{{ isEuServer ? ("euDomain" | i18n) : ("usDomain" | i18n) }}</b

View File

@@ -1,4 +1,5 @@
import { Component, Input, OnInit } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
@@ -13,31 +14,24 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
export class EnvironmentSelectorComponent implements OnInit {
constructor(
private configService: ConfigServiceAbstraction,
private platformUtilsService: PlatformUtilsService
private platformUtilsService: PlatformUtilsService,
private router: Router
) {}
@Input() hasFlags: boolean;
isEuServer: boolean;
isUsServer: boolean;
showRegionSelector = false;
euServerFlagEnabled: boolean;
selectedRegionImageName: string;
routeAndParams: string;
async ngOnInit() {
this.euServerFlagEnabled = await this.configService.getFeatureFlagBool(
this.euServerFlagEnabled = await this.configService.getFeatureFlag<boolean>(
FeatureFlag.DisplayEuEnvironmentFlag
);
const domain = Utils.getDomain(window.location.href);
this.isEuServer = domain.includes(RegionDomain.EU);
this.isUsServer = domain.includes(RegionDomain.US) || domain.includes(RegionDomain.USQA);
this.selectedRegionImageName = this.getRegionImage();
this.showRegionSelector = !this.platformUtilsService.isSelfHost();
}
getRegionImage(): string {
if (this.isEuServer) {
return "eu_flag";
} else {
return "us_flag";
}
this.routeAndParams = `/#${this.router.url}`;
}
}

View File

@@ -17,7 +17,6 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/platform/abstractions/state-migration.service";
import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@@ -27,7 +26,6 @@ import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@
import { PolicyListService } from "../admin-console/core/policy-list.service";
import { HtmlStorageService } from "../core/html-storage.service";
import { I18nService } from "../core/i18n.service";
import { StateMigrationService } from "../core/state-migration.service";
import { CollectionAdminService } from "../vault/core/collection-admin.service";
import { PasswordRepromptService } from "../vault/core/password-reprompt.service";
@@ -84,11 +82,6 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service";
},
{ provide: MessagingServiceAbstraction, useClass: BroadcasterMessagingService },
{ provide: ModalServiceAbstraction, useClass: ModalService },
{
provide: StateMigrationServiceAbstraction,
useClass: StateMigrationService,
deps: [AbstractStorageService, SECURE_STORAGE, STATE_FACTORY],
},
StateService,
{
provide: BaseStateServiceAbstraction,

View File

@@ -80,6 +80,9 @@ export class EventService {
case EventType.User_MigratedKeyToKeyConnector:
msg = humanReadableMsg = this.i18nService.t("migratedKeyConnector");
break;
case EventType.User_RequestedDeviceApproval:
msg = humanReadableMsg = this.i18nService.t("requestedDeviceApproval");
break;
// Cipher
case EventType.Cipher_Created:
msg = this.i18nService.t("createdItemId", this.formatCipherId(ev, options));
@@ -307,6 +310,20 @@ export class EventService {
this.getShortId(ev.organizationUserId)
);
break;
case EventType.OrganizationUser_ApprovedAuthRequest:
msg = this.i18nService.t("approvedAuthRequest", this.formatOrgUserId(ev));
humanReadableMsg = this.i18nService.t(
"approvedAuthRequest",
this.getShortId(ev.organizationUserId)
);
break;
case EventType.OrganizationUser_RejectedAuthRequest:
msg = this.i18nService.t("rejectedAuthRequest", this.formatOrgUserId(ev));
humanReadableMsg = this.i18nService.t(
"rejectedAuthRequest",
this.getShortId(ev.organizationUserId)
);
break;
// Org
case EventType.Organization_Updated:
msg = humanReadableMsg = this.i18nService.t("editedOrgSettings");

View File

@@ -4,7 +4,6 @@ import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
@@ -14,9 +13,10 @@ import {
} from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { VaultTimeoutService as VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
import { I18nService } from "../core/i18n.service";
@@ -26,14 +26,15 @@ export class InitService {
@Inject(WINDOW) private win: Window,
private environmentService: EnvironmentServiceAbstraction,
private notificationsService: NotificationsServiceAbstraction,
private vaultTimeoutService: VaultTimeoutServiceAbstraction,
private vaultTimeoutService: VaultTimeoutService,
private i18nService: I18nServiceAbstraction,
private eventUploadService: EventUploadServiceAbstraction,
private twoFactorService: TwoFactorServiceAbstraction,
private stateService: StateServiceAbstraction,
private cryptoService: CryptoServiceAbstraction,
private themingService: AbstractThemingService,
private encryptService: EncryptService
private encryptService: EncryptService,
private configService: ConfigService
) {}
init() {
@@ -48,7 +49,7 @@ export class InitService {
this.environmentService.initialized = true;
setTimeout(() => this.notificationsService.init(), 3000);
(this.vaultTimeoutService as VaultTimeoutService).init(true);
await this.vaultTimeoutService.init(true);
const locale = await this.stateService.getLocale();
await (this.i18nService as I18nService).init(locale);
(this.eventUploadService as EventUploadService).init(true);
@@ -58,6 +59,8 @@ export class InitService {
await this.themingService.monitorThemeChanges();
const containerService = new ContainerService(this.cryptoService, this.encryptService);
containerService.attachToGlobal(this.win);
this.configService.init();
};
}
}

View File

@@ -1,13 +0,0 @@
import { StateMigrationService as BaseStateMigrationService } from "@bitwarden/common/platform/services/state-migration.service";
import { Account } from "./state/account";
import { GlobalState } from "./state/global-state";
export class StateMigrationService extends BaseStateMigrationService<GlobalState, Account> {
protected async migrationStateFrom1To2(): Promise<void> {
await super.migrateStateFrom1To2();
const globals = (await this.get<GlobalState>("global")) ?? this.stateFactory.createGlobal(null);
globals.rememberEmail = (await this.get<boolean>("rememberEmail")) ?? globals.rememberEmail;
await this.set("global", globals);
}
}

View File

@@ -7,7 +7,6 @@ import {
STATE_SERVICE_USE_CACHE,
} from "@bitwarden/angular/services/injection-tokens";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service";
import {
AbstractMemoryStorageService,
AbstractStorageService,
@@ -30,7 +29,6 @@ export class StateService extends BaseStateService<GlobalState, Account> {
@Inject(SECURE_STORAGE) secureStorageService: AbstractStorageService,
@Inject(MEMORY_STORAGE) memoryStorageService: AbstractMemoryStorageService,
logService: LogService,
stateMigrationService: StateMigrationService,
@Inject(STATE_FACTORY) stateFactory: StateFactory<GlobalState, Account>,
@Inject(STATE_SERVICE_USE_CACHE) useAccountCache = true
) {
@@ -39,7 +37,6 @@ export class StateService extends BaseStateService<GlobalState, Account> {
secureStorageService,
memoryStorageService,
logService,
stateMigrationService,
stateFactory,
useAccountCache
);

View File

@@ -1,24 +0,0 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
@Injectable({
providedIn: "root",
})
export class HomeGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) {}
async canActivate(route: ActivatedRouteSnapshot) {
const authStatus = await this.authService.getAuthStatus();
if (authStatus === AuthenticationStatus.LoggedOut) {
return this.router.createUrlTree(["/login"], { queryParams: route.queryParams });
}
if (authStatus === AuthenticationStatus.Locked) {
return this.router.createUrlTree(["/lock"], { queryParams: route.queryParams });
}
return this.router.createUrlTree(["/vault"], { queryParams: route.queryParams });
}
}

View File

@@ -1,6 +1,6 @@
<router-outlet></router-outlet>
<div class="container my-5 text-muted text-center">
<environment-selector [hasFlags]="false"></environment-selector>
<environment-selector></environment-selector>
&copy; {{ year }} Bitwarden Inc. <br />
{{ "versionNumber" | i18n : version }}
</div>

View File

@@ -84,7 +84,7 @@
{{ "getApps" | i18n }}
</a>
<bit-menu-divider></bit-menu-divider>
<button bitMenuItem type="button" (click)="lock()">
<button *ngIf="canLock$ | async" bitMenuItem type="button" (click)="lock()">
<i class="bwi bwi-fw bwi-lock" aria-hidden="true"></i>
{{ "lockNow" | i18n }}
</button>

View File

@@ -1,6 +1,7 @@
import { Component, OnInit } from "@angular/core";
import { Observable } from "rxjs";
import { map, Observable } from "rxjs";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import {
canAccessAdmin,
OrganizationService,
@@ -8,6 +9,7 @@ import {
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { Provider } from "@bitwarden/common/models/domain/provider";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -26,6 +28,7 @@ export class NavbarComponent implements OnInit {
providers: Provider[] = [];
userId: string;
organizations$: Observable<Organization[]>;
canLock$: Observable<boolean>;
constructor(
private messagingService: MessagingService,
@@ -34,6 +37,7 @@ export class NavbarComponent implements OnInit {
private providerService: ProviderService,
private syncService: SyncService,
private organizationService: OrganizationService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private i18nService: I18nService
) {
this.selfHosted = this.platformUtilsService.isSelfHost();
@@ -56,6 +60,9 @@ export class NavbarComponent implements OnInit {
this.organizations$ = this.organizationService.memberOrganizations$.pipe(
canAccessAdmin(this.i18nService)
);
this.canLock$ = this.vaultTimeoutSettingsService
.availableVaultTimeoutActions$()
.pipe(map((actions) => actions.includes(VaultTimeoutAction.Lock)));
}
lock() {

View File

@@ -1,9 +1,15 @@
import { NgModule } from "@angular/core";
import { Route, RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard";
import { LockGuard } from "@bitwarden/angular/auth/guards/lock.guard";
import { UnauthGuard } from "@bitwarden/angular/auth/guards/unauth.guard";
import {
AuthGuard,
lockGuard,
redirectGuard,
tdeDecryptionRequiredGuard,
UnauthGuard,
} from "@bitwarden/angular/auth/guards";
import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { SubscriptionRoutingModule } from "../app/billing/settings/subscription-routing.module";
import { flagEnabled, Flags } from "../utils/flags";
@@ -16,6 +22,7 @@ import { AcceptEmergencyComponent } from "./auth/accept-emergency.component";
import { AcceptOrganizationComponent } from "./auth/accept-organization.component";
import { HintComponent } from "./auth/hint.component";
import { LockComponent } from "./auth/lock.component";
import { LoginDecryptionOptionsComponent } from "./auth/login/login-decryption-options/login-decryption-options.component";
import { LoginWithDeviceComponent } from "./auth/login/login-with-device.component";
import { LoginComponent } from "./auth/login/login.component";
import { RecoverDeleteComponent } from "./auth/recover-delete.component";
@@ -31,7 +38,6 @@ import { UpdatePasswordComponent } from "./auth/update-password.component";
import { UpdateTempPasswordComponent } from "./auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "./auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "./auth/verify-recover-delete.component";
import { HomeGuard } from "./guards/home.guard";
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
import { UserLayoutComponent } from "./layouts/user-layout.component";
import { ReportsModule } from "./reports";
@@ -56,7 +62,7 @@ const routes: Routes = [
path: "",
pathMatch: "full",
children: [], // Children lets us have an empty component.
canActivate: [HomeGuard], // Redirects either to vault, login or lock page.
canActivate: [redirectGuard()], // Redirects either to vault, login, or lock page.
},
{ path: "login", component: LoginComponent, canActivate: [UnauthGuard] },
{
@@ -64,7 +70,20 @@ const routes: Routes = [
component: LoginWithDeviceComponent,
data: { titleId: "loginWithDevice" },
},
{
path: "admin-approval-requested",
component: LoginWithDeviceComponent,
data: { titleId: "loginWithDevice" },
},
{ path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] },
{
path: "login-initiated",
component: LoginDecryptionOptionsComponent,
canActivate: [
tdeDecryptionRequiredGuard(),
canAccessFeature(FeatureFlag.TrustedDeviceEncryption),
],
},
{
path: "register",
component: TrialInitiationComponent,
@@ -96,7 +115,7 @@ const routes: Routes = [
{
path: "lock",
component: LockComponent,
canActivate: [LockGuard],
canActivate: [lockGuard()],
},
{ path: "verify-email", component: VerifyEmailTokenComponent },
{

View File

@@ -1,7 +1,7 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard";
import { AuthGuard } from "@bitwarden/angular/auth/guards";
import { HasPremiumGuard } from "../core/guards/has-premium.guard";

View File

@@ -1,9 +1,7 @@
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { DeauthorizeSessionsComponent } from "../auth/settings/deauthorize-sessions.component";
@@ -26,13 +24,11 @@ export class AccountComponent {
constructor(
private modalService: ModalService,
private apiService: ApiService,
private keyConnectorService: KeyConnectorService,
private stateService: StateService
private userVerificationService: UserVerificationService
) {}
async ngOnInit() {
this.showChangeEmail = !(await this.keyConnectorService.getUsesKeyConnector());
this.showChangeEmail = await this.userVerificationService.hasMasterPassword();
}
async deauthorizeSessions() {

View File

@@ -42,8 +42,8 @@ export class ChangeEmailComponent implements OnInit {
}
async submit() {
const hasEncKey = await this.cryptoService.hasEncKey();
if (!hasEncKey) {
const hasUserKey = await this.cryptoService.hasUserKey();
if (!hasUserKey) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("updateKey"));
return;
}
@@ -52,7 +52,10 @@ export class ChangeEmailComponent implements OnInit {
if (!this.tokenSent) {
const request = new EmailTokenRequest();
request.newEmail = this.newEmail;
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(
this.masterPassword,
await this.cryptoService.getOrDeriveMasterKey(this.masterPassword)
);
try {
this.formPromise = this.apiService.postEmailToken(request);
await this.formPromise;
@@ -64,21 +67,24 @@ export class ChangeEmailComponent implements OnInit {
const request = new EmailRequest();
request.token = this.token;
request.newEmail = this.newEmail;
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(
this.masterPassword,
await this.cryptoService.getOrDeriveMasterKey(this.masterPassword)
);
const kdf = await this.stateService.getKdfType();
const kdfConfig = await this.stateService.getKdfConfig();
const newKey = await this.cryptoService.makeKey(
const newMasterKey = await this.cryptoService.makeMasterKey(
this.masterPassword,
this.newEmail,
kdf,
kdfConfig
);
request.newMasterPasswordHash = await this.cryptoService.hashPassword(
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
this.masterPassword,
newKey
newMasterKey
);
const newEncKey = await this.cryptoService.remakeEncKey(newKey);
request.key = newEncKey[1].encryptedString;
const newUserKey = await this.cryptoService.encryptUserKeyWithMasterKey(newMasterKey);
request.key = newUserKey[1].encryptedString;
try {
this.formPromise = this.apiService.postEmail(request);
await this.formPromise;

View File

@@ -46,8 +46,8 @@ export class ChangeKdfConfirmationComponent {
async submit() {
this.loading = true;
const hasEncKey = await this.cryptoService.hasEncKey();
if (!hasEncKey) {
const hasUserKey = await this.cryptoService.hasUserKey();
if (!hasUserKey) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("updateKey"));
return;
}
@@ -75,17 +75,21 @@ export class ChangeKdfConfirmationComponent {
request.kdfIterations = this.kdfConfig.iterations;
request.kdfMemory = this.kdfConfig.memory;
request.kdfParallelism = this.kdfConfig.parallelism;
request.masterPasswordHash = await this.cryptoService.hashPassword(masterPassword, null);
const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
const email = await this.stateService.getEmail();
const newKey = await this.cryptoService.makeKey(
const newMasterKey = await this.cryptoService.makeMasterKey(
masterPassword,
email,
this.kdf,
this.kdfConfig
);
request.newMasterPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey);
const newEncKey = await this.cryptoService.remakeEncKey(newKey);
request.key = newEncKey[1].encryptedString;
request.newMasterPasswordHash = await this.cryptoService.hashMasterKey(
masterPassword,
newMasterKey
);
const newUserKey = await this.cryptoService.encryptUserKeyWithMasterKey(newMasterKey);
request.key = newUserKey[1].encryptedString;
await this.apiService.postAccountKdf(request);
}

View File

@@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import {
DEFAULT_KDF_CONFIG,
@@ -11,6 +10,7 @@ import {
KdfType,
} from "@bitwarden/common/enums";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.component";
@@ -25,7 +25,7 @@ export class ChangeKdfComponent implements OnInit {
kdfOptions: any[] = [];
recommendedPbkdf2Iterations = DEFAULT_PBKDF2_ITERATIONS;
constructor(private stateService: StateService, private dialogService: DialogServiceAbstraction) {
constructor(private stateService: StateService, private dialogService: DialogService) {
this.kdfOptions = [
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
{ name: "Argon2id", value: KdfType.Argon2id },

View File

@@ -27,37 +27,45 @@
</app-vault-timeout-input>
</div>
</div>
<div class="form-group">
<label>{{ "vaultTimeoutAction" | i18n }}</label>
<div class="form-check form-check-block">
<input
class="form-check-input"
type="radio"
name="vaultTimeoutAction"
id="vaultTimeoutActionLock"
value="{{ VaultTimeoutAction.Lock }}"
formControlName="vaultTimeoutAction"
/>
<label class="form-check-label" for="vaultTimeoutActionLock">
{{ "lock" | i18n }}
<small>{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
</label>
<ng-container *ngIf="availableVaultTimeoutActions$ | async as availableVaultTimeoutActions">
<div *ngIf="availableVaultTimeoutActions.length > 1" class="form-group">
<label>{{ "vaultTimeoutAction" | i18n }}</label>
<div
*ngIf="availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)"
class="form-check form-check-block"
>
<input
class="form-check-input"
type="radio"
name="vaultTimeoutAction"
id="vaultTimeoutActionLock"
value="{{ VaultTimeoutAction.Lock }}"
formControlName="vaultTimeoutAction"
/>
<label class="form-check-label" for="vaultTimeoutActionLock">
{{ "lock" | i18n }}
<small>{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
</label>
</div>
<div
*ngIf="availableVaultTimeoutActions.includes(VaultTimeoutAction.LogOut)"
class="form-check mt-2 form-check-block"
>
<input
class="form-check-input"
type="radio"
name="vaultTimeoutAction"
id="vaultTimeoutActionLogOut"
value="{{ VaultTimeoutAction.LogOut }}"
formControlName="vaultTimeoutAction"
/>
<label class="form-check-label" for="vaultTimeoutActionLogOut">
{{ "logOut" | i18n }}
<small>{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
</label>
</div>
</div>
<div class="form-check mt-2 form-check-block">
<input
class="form-check-input"
type="radio"
name="vaultTimeoutAction"
id="vaultTimeoutActionLogOut"
value="{{ VaultTimeoutAction.LogOut }}"
formControlName="vaultTimeoutAction"
/>
<label class="form-check-label" for="vaultTimeoutActionLogOut">
{{ "logOut" | i18n }}
<small>{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
</label>
</div>
</div>
</ng-container>
<div class="row">
<div class="col-6">
<div class="form-group">

View File

@@ -1,11 +1,10 @@
import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { concatMap, filter, map, Observable, Subject, takeUntil, tap } from "rxjs";
import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs";
import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog";
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { ThemeType } from "@bitwarden/common/enums";
@@ -15,6 +14,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-preferences",
@@ -24,6 +24,8 @@ export class PreferencesComponent implements OnInit {
// For use in template
protected readonly VaultTimeoutAction = VaultTimeoutAction;
protected availableVaultTimeoutActions$: Observable<VaultTimeoutAction[]>;
vaultTimeoutPolicyCallout: Observable<{
timeout: { hours: number; minutes: number };
action: VaultTimeoutAction;
@@ -55,7 +57,7 @@ export class PreferencesComponent implements OnInit {
private messagingService: MessagingService,
private themingService: AbstractThemingService,
private settingsService: SettingsService,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {
this.vaultTimeoutOptions = [
{ name: i18nService.t("oneMinute"), value: 1 },
@@ -89,6 +91,9 @@ export class PreferencesComponent implements OnInit {
}
async ngOnInit() {
this.availableVaultTimeoutActions$ =
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$();
this.vaultTimeoutPolicyCallout = this.policyService.get$(PolicyType.MaximumVaultTimeout).pipe(
filter((policy) => policy != null),
map((policy) => {
@@ -117,7 +122,7 @@ export class PreferencesComponent implements OnInit {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "vaultTimeoutLogOutConfirmationTitle" },
content: { key: "vaultTimeoutLogOutConfirmation" },
type: SimpleDialogType.WARNING,
type: "warning",
});
if (!confirmed) {
@@ -133,7 +138,9 @@ export class PreferencesComponent implements OnInit {
.subscribe();
const initialFormValues = {
vaultTimeout: await this.vaultTimeoutSettingsService.getVaultTimeout(),
vaultTimeoutAction: await this.vaultTimeoutSettingsService.getVaultTimeoutAction(),
vaultTimeoutAction: await firstValueFrom(
this.vaultTimeoutSettingsService.vaultTimeoutAction$()
),
enableFavicons: !(await this.settingsService.getDisableFavicon()),
enableFullWidth: await this.stateService.getEnableFullWidth(),
theme: await this.stateService.getTheme(),

View File

@@ -2,7 +2,7 @@ import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ApiKeyComponent } from "./api-key.component";
@@ -20,14 +20,14 @@ export class SecurityKeysComponent implements OnInit {
showChangeKdf = true;
constructor(
private keyConnectorService: KeyConnectorService,
private userVerificationService: UserVerificationService,
private stateService: StateService,
private modalService: ModalService,
private apiService: ApiService
) {}
async ngOnInit() {
this.showChangeKdf = !(await this.keyConnectorService.getUsesKeyConnector());
this.showChangeKdf = await this.userVerificationService.hasMasterPassword();
}
async viewUserApiKey() {

View File

@@ -1,9 +1,9 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { ChangePasswordComponent } from "../auth/settings/change-password.component";
import { TwoFactorSetupComponent } from "../auth/settings/two-factor-setup.component";
import { ChangePasswordComponent } from "./change-password.component";
import { SecurityKeysComponent } from "./security-keys.component";
import { SecurityComponent } from "./security.component";

View File

@@ -1,6 +1,6 @@
import { Component } from "@angular/core";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
@Component({
selector: "app-security",
@@ -9,9 +9,9 @@ import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-con
export class SecurityComponent {
showChangePassword = true;
constructor(private keyConnectorService: KeyConnectorService) {}
constructor(private userVerificationService: UserVerificationService) {}
async ngOnInit() {
this.showChangePassword = !(await this.keyConnectorService.getUsesKeyConnector());
this.showChangePassword = await this.userVerificationService.hasMasterPassword();
}
}

View File

@@ -1,4 +1,4 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="updateEncKeyTitle">
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="updateUserKeyTitle">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<form
class="modal-content"
@@ -8,7 +8,7 @@
ngNativeValidate
>
<div class="modal-header">
<h1 class="modal-title" id="updateEncKeyTitle">{{ "updateEncryptionKey" | i18n }}</h1>
<h1 class="modal-title" id="updateUserKeyTitle">{{ "updateEncryptionKey" | i18n }}</h1>
<button
type="button"
class="close"

View File

@@ -36,8 +36,8 @@ export class UpdateKeyComponent {
) {}
async submit() {
const hasEncKey = await this.cryptoService.hasEncKey();
if (hasEncKey) {
const hasUserKey = await this.cryptoService.hasUserKey();
if (hasUserKey) {
return;
}
@@ -68,17 +68,20 @@ export class UpdateKeyComponent {
}
private async makeRequest(): Promise<UpdateKeyRequest> {
const key = await this.cryptoService.getKey();
const encKey = await this.cryptoService.makeEncKey(key);
const masterKey = await this.cryptoService.getMasterKey();
const newUserKey = await this.cryptoService.makeUserKey(masterKey);
const privateKey = await this.cryptoService.getPrivateKey();
let encPrivateKey: EncString = null;
if (privateKey != null) {
encPrivateKey = await this.cryptoService.encrypt(privateKey, encKey[0]);
encPrivateKey = await this.cryptoService.encrypt(privateKey, newUserKey[0]);
}
const request = new UpdateKeyRequest();
request.privateKey = encPrivateKey != null ? encPrivateKey.encryptedString : null;
request.key = encKey[1].encryptedString;
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
request.key = newUserKey[1].encryptedString;
request.masterPasswordHash = await this.cryptoService.hashMasterKey(
this.masterPassword,
await this.cryptoService.getOrDeriveMasterKey(this.masterPassword)
);
await this.syncService.fullSync(true);
@@ -87,7 +90,7 @@ export class UpdateKeyComponent {
if (folders[i].id == null) {
continue;
}
const folder = await this.folderService.encrypt(folders[i], encKey[0]);
const folder = await this.folderService.encrypt(folders[i], newUserKey[0]);
request.folders.push(new FolderWithIdRequest(folder));
}
@@ -96,7 +99,7 @@ export class UpdateKeyComponent {
if (ciphers[i].organizationId != null) {
continue;
}
const cipher = await this.cipherService.encrypt(ciphers[i], encKey[0]);
const cipher = await this.cipherService.encrypt(ciphers[i], newUserKey[0]);
request.ciphers.push(new CipherWithIdRequest(cipher));
}

View File

@@ -9,7 +9,9 @@
>
<option *ngFor="let o of vaultTimeoutOptions" [ngValue]="o.value">{{ o.name }}</option>
</select>
<small class="form-text text-muted">{{ "vaultTimeoutDesc" | i18n }}</small>
<small class="form-text text-muted">{{
((canLockVault$ | async) ? "vaultTimeoutDesc" : "vaultTimeoutLogoutDesc") | i18n
}}</small>
</div>
<div class="form-group" *ngIf="showCustom" formGroupName="custom">
<label for="customVaultTimeout">{{ "customVaultTimeout" | i18n }}</label>

View File

@@ -26,6 +26,7 @@ import { RecoverTwoFactorComponent } from "../auth/recover-two-factor.component"
import { RegisterFormModule } from "../auth/register-form/register-form.module";
import { RemovePasswordComponent } from "../auth/remove-password.component";
import { SetPasswordComponent } from "../auth/set-password.component";
import { ChangePasswordComponent } from "../auth/settings/change-password.component";
import { DeauthorizeSessionsComponent } from "../auth/settings/deauthorize-sessions.component";
import { EmergencyAccessAddEditComponent } from "../auth/settings/emergency-access/emergency-access-add-edit.component";
import { EmergencyAccessAttachmentsComponent } from "../auth/settings/emergency-access/emergency-access-attachments.component";
@@ -76,12 +77,10 @@ import { ApiKeyComponent } from "../settings/api-key.component";
import { ChangeAvatarComponent } from "../settings/change-avatar.component";
import { ChangeEmailComponent } from "../settings/change-email.component";
import { ChangeKdfModule } from "../settings/change-kdf/change-kdf.module";
import { ChangePasswordComponent } from "../settings/change-password.component";
import { DeleteAccountComponent } from "../settings/delete-account.component";
import { DomainRulesComponent } from "../settings/domain-rules.component";
import { LowKdfComponent } from "../settings/low-kdf.component";
import { PreferencesComponent } from "../settings/preferences.component";
import { PremiumComponent } from "../settings/premium.component";
import { ProfileComponent } from "../settings/profile.component";
import { PurgeVaultComponent } from "../settings/purge-vault.component";
import { SecurityKeysComponent } from "../settings/security-keys.component";
@@ -94,7 +93,6 @@ import { GeneratorComponent } from "../tools/generator.component";
import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component";
import { AccessComponent } from "../tools/send/access.component";
import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component";
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/send/efflux-dates.component";
import { ToolsComponent } from "../tools/tools.component";
import { PasswordRepromptComponent } from "../vault/components/password-reprompt.component";
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
@@ -107,6 +105,7 @@ import { ShareComponent } from "../vault/individual-vault/share.component";
import { AddEditComponent as OrgAddEditComponent } from "../vault/org-vault/add-edit.component";
import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-vault/attachments.component";
import { CollectionsComponent as OrgCollectionsComponent } from "../vault/org-vault/collections.component";
import { PremiumComponent } from "../vault/settings/premium.component";
import { EnvironmentSelectorModule } from "./../components/environment-selector/environment-selector.module";
import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component";
@@ -198,7 +197,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,
ShareComponent,
@@ -302,7 +300,6 @@ import { SharedModule } from "./shared.module";
SecurityKeysComponent,
SelectableAvatarComponent,
SendAddEditComponent,
SendEffluxDatesComponent,
SetPasswordComponent,
SettingsComponent,
ShareComponent,

View File

@@ -1,7 +1,6 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog";
import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -9,6 +8,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
import { DialogService } from "@bitwarden/components";
import { PasswordGeneratorHistoryComponent } from "./password-generator-history.component";
@@ -25,7 +25,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
i18nService: I18nService,
logService: LogService,
route: ActivatedRoute,
private dialogService: DialogServiceAbstraction
private dialogService: DialogService
) {
super(
passwordGenerationService,

View File

@@ -0,0 +1,29 @@
<bit-dialog>
<span bitDialogTitle>
{{ "importError" | i18n }}
</span>
<span bitDialogContent>
<div>{{ "resolveTheErrorsBelowAndTryAgain" | i18n }}</div>
<bit-table [dataSource]="dataSource">
<ng-container header>
<tr>
<th bitCell>{{ "name" | i18n }}</th>
<th bitCell>{{ "description" | i18n }}</th>
</tr>
</ng-container>
<ng-template body let-rows$>
<tr bitRow *ngFor="let r of rows$ | async">
<td bitCell>{{ r.type }}</td>
<td bitCell>{{ r.message }}</td>
</tr>
</ng-template>
</bit-table>
</span>
<div bitDialogFooter>
<button bitButton bitDialogClose buttonType="primary" type="button">
{{ "ok" | i18n }}
</button>
</div>
</bit-dialog>

View File

@@ -0,0 +1,33 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { TableDataSource } from "@bitwarden/components";
export interface ErrorListItem {
type: string;
message: string;
}
@Component({
selector: "app-import-error-dialog",
templateUrl: "./import-error-dialog.component.html",
})
export class ImportErrorDialogComponent implements OnInit {
protected dataSource = new TableDataSource<ErrorListItem>();
constructor(public dialogRef: DialogRef, @Inject(DIALOG_DATA) public data: Error) {}
ngOnInit(): void {
const split = this.data.message.split("\n\n");
if (split.length == 1) {
this.dataSource.data = [{ type: "", message: this.data.message }];
return;
}
const data: ErrorListItem[] = [];
split.forEach((line) => {
data.push({ type: "", message: line });
});
this.dataSource.data = data;
}
}

Some files were not shown because too many files have changed in this diff Show More