1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-03 17:13:47 +00:00

merge main

This commit is contained in:
rr-bw
2024-08-30 11:40:21 -07:00
615 changed files with 20276 additions and 5305 deletions

View File

@@ -96,7 +96,7 @@ export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
abstract edit(user: UserView): void;
abstract getUsers(): Promise<ListResponse<UserView> | UserView[]>;
abstract deleteUser(id: string): Promise<void>;
abstract removeUser(id: string): Promise<void>;
abstract reinviteUser(id: string): Promise<void>;
abstract confirmUser(user: UserView, publicKey: Uint8Array): Promise<void>;
@@ -132,7 +132,7 @@ export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
return false;
}
this.actionPromise = this.deleteUser(user.id);
this.actionPromise = this.removeUser(user.id);
try {
await this.actionPromise;
this.toastService.showToast({

View File

@@ -8,6 +8,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 { ToastService } from "@bitwarden/components";
import { EventService } from "../../core";
import { EventExportService } from "../../tools/event-export";
@@ -34,6 +35,7 @@ export abstract class BaseEventsComponent {
protected platformUtilsService: PlatformUtilsService,
protected logService: LogService,
protected fileDownloadService: FileDownloadService,
private toastService: ToastService,
) {
const defaultDates = this.eventService.getDefaultDateFilters();
this.start = defaultDates[0];
@@ -164,11 +166,11 @@ export abstract class BaseEventsComponent {
try {
dates = this.eventService.formatDateFilters(this.start, this.end);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidDateRange"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidDateRange"),
});
return null;
}
return dates;

View File

@@ -22,7 +22,7 @@ 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 { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserView } from "../organizations/core/views/organization-user.view";
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
@@ -127,6 +127,7 @@ export abstract class BasePeopleComponent<
protected userNamePipe: UserNamePipe,
protected dialogService: DialogService,
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
protected toastService: ToastService,
) {}
abstract edit(user: UserType): void;
@@ -251,11 +252,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.deleteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.userNamePipe.transform(user)),
});
this.removeUser(user);
} catch (e) {
this.validationService.showError(e);
@@ -282,11 +283,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.revokeUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)),
});
await this.load();
} catch (e) {
this.validationService.showError(e);
@@ -298,11 +299,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.restoreUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)),
});
await this.load();
} catch (e) {
this.validationService.showError(e);
@@ -318,11 +319,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.reinviteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)),
});
} catch (e) {
this.validationService.showError(e);
}
@@ -344,11 +345,11 @@ export abstract class BasePeopleComponent<
this.actionPromise = this.confirmUser(user, publicKey);
await this.actionPromise;
updateUser(this);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)),
});
} catch (e) {
this.validationService.showError(e);
throw e;

View File

@@ -12,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 { DialogService, TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
import { EventService } from "../../../core";
import { SharedModule } from "../../../shared";
@@ -63,6 +63,7 @@ export class EntityEventsComponent implements OnInit {
private organizationUserService: OrganizationUserService,
private formBuilder: FormBuilder,
private validationService: ValidationService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -109,11 +110,11 @@ export class EntityEventsComponent implements OnInit {
this.filterFormGroup.value.end,
);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidDateRange"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidDateRange"),
});
return;
}

View File

@@ -14,6 +14,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 { ToastService } from "@bitwarden/components";
import { EventService } from "../../../core";
import { EventExportService } from "../../../tools/event-export";
@@ -22,6 +23,7 @@ import { BaseEventsComponent } from "../../common/base.events.component";
const EVENT_SYSTEM_USER_TO_TRANSLATION: Record<EventSystemUser, string> = {
[EventSystemUser.SCIM]: null, // SCIM acronym not able to be translated so just display SCIM
[EventSystemUser.DomainVerification]: "domainVerification",
[EventSystemUser.PublicApi]: "publicApi",
};
@Component({
@@ -50,6 +52,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
private organizationUserService: OrganizationUserService,
private providerService: ProviderService,
fileDownloadService: FileDownloadService,
toastService: ToastService,
) {
super(
eventService,
@@ -58,6 +61,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
platformUtilsService,
logService,
fileDownloadService,
toastService,
);
}

View File

@@ -24,7 +24,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 { UserId } from "@bitwarden/common/types/guid";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { CollectionAdminService } from "../../../vault/core/collection-admin.service";
import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view";
@@ -213,6 +213,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private accountService: AccountService,
private collectionAdminService: CollectionAdminService,
private toastService: ToastService,
) {
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
}
@@ -280,11 +281,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
if (this.groupForm.invalid) {
if (this.tabIndex !== GroupAddEditTabType.Info) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("groupInfo")),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t(
"fieldOnTabRequiresAttention",
this.i18nService.t("groupInfo"),
),
});
}
return;
}
@@ -300,11 +304,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
await this.groupService.save(groupView);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedGroupId" : "createdGroupId", formValue.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedGroupId" : "createdGroupId",
formValue.name,
),
});
this.dialogRef.close(GroupAddEditDialogResultType.Saved);
};
@@ -325,11 +332,11 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
await this.groupService.delete(this.organizationId, this.groupId);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deletedGroupId", this.group.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedGroupId", this.group.name),
});
this.dialogRef.close(GroupAddEditDialogResultType.Deleted);
};
}

View File

@@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { OrganizationVerifyDeleteRecoverRequest } from "@bitwarden/common/admin-console/models/request/organization-verify-delete-recover.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { SharedModule } from "../../../shared/shared.module";
@@ -27,6 +28,7 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private route: ActivatedRoute,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -44,11 +46,11 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit {
submit = async () => {
const request = new OrganizationVerifyDeleteRecoverRequest(this.token);
await this.apiService.deleteUsingToken(this.orgId, request);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("organizationDeleted"),
this.i18nService.t("organizationDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("organizationDeleted"),
message: this.i18nService.t("organizationDeletedDesc"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -4,7 +4,7 @@ import { Component, Inject, OnInit } from "@angular/core";
import { OrganizationUserService } from "@bitwarden/common/admin-console/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 { DialogService, TableDataSource } from "@bitwarden/components";
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
import { OrganizationUserView } from "../../../core";
@@ -24,6 +24,7 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit {
private organizationUserService: OrganizationUserService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -35,11 +36,11 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit {
this.data.orgId,
this.dataSource.data.map((u) => u.id),
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("activatedAccessToSecretsManager"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("activatedAccessToSecretsManager"),
});
this.dialogRef.close();
};

View File

@@ -45,7 +45,7 @@ export class BulkRemoveComponent {
submit = async () => {
this.loading = true;
try {
const response = await this.deleteUsers();
const response = await this.removeUsers();
response.data.forEach((entry) => {
const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkRemovedMessage");
@@ -59,8 +59,8 @@ export class BulkRemoveComponent {
this.loading = false;
};
protected async deleteUsers() {
return await this.organizationUserService.deleteManyOrganizationUsers(
protected async removeUsers() {
return await this.organizationUserService.removeManyOrganizationUsers(
this.organizationId,
this.users.map((user) => user.id),
);

View File

@@ -26,7 +26,7 @@ import { ProductTierType } from "@bitwarden/common/billing/enums";
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 { DialogService, ToastService } from "@bitwarden/components";
import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service";
import { CollectionAdminView } from "../../../../../vault/core/views/collection-admin.view";
@@ -143,6 +143,7 @@ export class MemberDialogComponent implements OnDestroy {
private dialogService: DialogService,
private accountService: AccountService,
organizationService: OrganizationService,
private toastService: ToastService,
) {
this.organization$ = organizationService
.get$(this.params.organizationId)
@@ -376,11 +377,11 @@ export class MemberDialogComponent implements OnDestroy {
) {
this.permissionsGroup.value.manageUsers = true;
(document.getElementById("manageUsers") as HTMLInputElement).checked = true;
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("accountRecoveryManageUsers"),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("accountRecoveryManageUsers"),
});
}
}
@@ -389,11 +390,11 @@ export class MemberDialogComponent implements OnDestroy {
if (this.formGroup.invalid) {
if (this.tabIndex !== MemberDialogTab.Role) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")),
});
}
return;
}
@@ -401,11 +402,11 @@ export class MemberDialogComponent implements OnDestroy {
const organization = await firstValueFrom(this.organization$);
if (!organization.useCustomPermissions && this.customUserTypeSelected) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("customNonEnterpriseError"),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("customNonEnterpriseError"),
});
return;
}
@@ -452,11 +453,14 @@ export class MemberDialogComponent implements OnDestroy {
await this.userService.invite(emails, userView);
}
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedUserId" : "invitedUsers",
this.params.name,
),
});
this.close(MemberDialogResult.Saved);
};
@@ -487,16 +491,16 @@ export class MemberDialogComponent implements OnDestroy {
}
}
await this.organizationUserService.deleteOrganizationUser(
await this.organizationUserService.removeOrganizationUser(
this.params.organizationId,
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.params.name),
});
this.close(MemberDialogResult.Deleted);
};
@@ -529,11 +533,11 @@ export class MemberDialogComponent implements OnDestroy {
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("revokedUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("revokedUserId", this.params.name),
});
this.isRevoked = true;
this.close(MemberDialogResult.Revoked);
};
@@ -548,11 +552,11 @@ export class MemberDialogComponent implements OnDestroy {
this.params.organizationUserId,
);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("restoredUserId", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("restoredUserId", this.params.name),
});
this.isRevoked = false;
this.close(MemberDialogResult.Restored);
};

View File

@@ -17,7 +17,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 { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { OrganizationUserResetPasswordService } from "../services/organization-user-reset-password/organization-user-reset-password.service";
@@ -50,6 +50,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
private policyService: PolicyService,
private logService: LogService,
private dialogService: DialogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -88,30 +89,30 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
}
this.platformUtilsService.copyToClipboard(value, { window: window });
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("valueCopied", this.i18nService.t("password")),
);
this.toastService.showToast({
variant: "info",
title: null,
message: this.i18nService.t("valueCopied", this.i18nService.t("password")),
});
}
async submit() {
// Validation
if (this.newPassword == null || this.newPassword === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordRequired"),
});
return false;
}
if (this.newPassword.length < Utils.minimumPasswordLength) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength),
});
return false;
}
@@ -123,11 +124,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
this.enforcedPolicyOptions,
)
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
});
return;
}
@@ -151,11 +152,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy {
this.organizationId,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("resetPasswordSuccess"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("resetPasswordSuccess"),
});
this.passwordReset.emit();
} catch (e) {
this.logService.error(e);

View File

@@ -32,7 +32,7 @@ import {
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -269,8 +269,8 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
return collectionMap;
}
deleteUser(id: string): Promise<void> {
return this.organizationUserService.deleteOrganizationUser(this.organization.id, id);
removeUser(id: string): Promise<void> {
return this.organizationUserService.removeOrganizationUser(this.organization.id, id);
}
revokeUser(id: string): Promise<void> {

View File

@@ -15,7 +15,7 @@ import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/po
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { BasePolicy, BasePolicyComponent } from "../policies";
@@ -58,6 +58,7 @@ export class PolicyEditComponent implements AfterViewInit {
private cdr: ChangeDetectorRef,
private formBuilder: FormBuilder,
private dialogRef: DialogRef<PolicyEditDialogResult>,
private toastService: ToastService,
) {}
get policy(): BasePolicy {
return this.data.policy;
@@ -95,7 +96,7 @@ export class PolicyEditComponent implements AfterViewInit {
try {
request = await this.policyComponent.buildRequest(this.data.policiesEnabledMap);
} catch (e) {
this.platformUtilsService.showToast("error", null, e.message);
this.toastService.showToast({ variant: "error", title: null, message: e.message });
return;
}
this.formPromise = this.policyApiService.putPolicy(
@@ -104,11 +105,11 @@ export class PolicyEditComponent implements AfterViewInit {
request,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
});
this.dialogRef.close(PolicyEditDialogResult.Saved);
};

View File

@@ -14,7 +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 { DialogService, ToastService } from "@bitwarden/components";
import { ApiKeyComponent } from "../../../auth/settings/security/api-key.component";
import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.component";
@@ -77,6 +77,7 @@ export class AccountComponent implements OnInit, OnDestroy {
private organizationApiService: OrganizationApiServiceAbstraction,
private dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -167,7 +168,11 @@ export class AccountComponent implements OnInit, OnDestroy {
await this.organizationApiService.save(this.organizationId, request);
this.platformUtilsService.showToast("success", null, this.i18nService.t("organizationUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("organizationUpdated"),
});
};
submitCollectionManagement = async () => {
@@ -184,11 +189,11 @@ export class AccountComponent implements OnInit, OnDestroy {
await this.organizationApiService.updateCollectionManagement(this.organizationId, request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("updatedCollectionManagement"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("updatedCollectionManagement"),
});
};
async deleteOrganization() {

View File

@@ -14,7 +14,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { UserVerificationModule } from "../../../../auth/shared/components/user-verification";
import { SharedModule } from "../../../../shared/shared.module";
@@ -94,6 +94,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
ngOnDestroy(): void {
@@ -121,11 +122,11 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy {
.buildRequest(this.formGroup.value.secret)
.then((request) => this.organizationApiService.delete(this.organization.id, request));
this.platformUtilsService.showToast(
"success",
this.i18nService.t("organizationDeleted"),
this.i18nService.t("organizationDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("organizationDeleted"),
message: this.i18nService.t("organizationDeletedDesc"),
});
this.dialogRef.close(DeleteOrganizationDialogResult.Deleted);
};

View File

@@ -14,7 +14,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 { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationPlansComponent } from "../../../billing";
import { SharedModule } from "../../../shared";
@@ -68,6 +68,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -76,12 +77,12 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
const error = qParams.token == null;
if (error) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("sponsoredFamiliesAcceptFailed"),
{ timeout: 10000 },
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("sponsoredFamiliesAcceptFailed"),
timeout: 10000,
});
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/"]);
@@ -139,11 +140,11 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
request.sponsoredOrganizationId = organizationId;
await this.apiService.postRedeemSponsorship(this.token, request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("sponsoredFamiliesOfferRedeemed"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("sponsoredFamiliesOfferRedeemed"),
});
await this.syncService.fullSync(true);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.

View File

@@ -8,7 +8,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 { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserResetPasswordService } from "../members/services/organization-user-reset-password/organization-user-reset-password.service";
@@ -29,6 +29,7 @@ export class EnrollMasterPasswordReset {
syncService: SyncService,
logService: LogService,
userVerificationService: UserVerificationService,
toastService: ToastService,
) {
const result = await UserVerificationDialogComponent.open(dialogService, {
title: "enrollAccountRecovery",
@@ -71,7 +72,11 @@ export class EnrollMasterPasswordReset {
// Enrollment succeeded
try {
platformUtilsService.showToast("success", null, i18nService.t("enrollPasswordResetSuccess"));
toastService.showToast({
variant: "success",
title: null,
message: i18nService.t("enrollPasswordResetSuccess"),
});
await syncService.fullSync(true);
} catch (e) {
logService.error(e);

View File

@@ -7,6 +7,7 @@ import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-cons
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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-verify-recover-delete-provider",
@@ -27,6 +28,7 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit {
private i18nService: I18nService,
private route: ActivatedRoute,
private logService: LogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -48,11 +50,11 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit {
request,
);
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("providerDeleted"),
this.i18nService.t("providerDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("providerDeleted"),
message: this.i18nService.t("providerDeletedDesc"),
});
await this.router.navigate(["/"]);
} catch (e) {
this.logService.error(e);

View File

@@ -8,6 +8,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-hint",
@@ -30,8 +31,17 @@ export class HintComponent extends BaseHintComponent implements OnInit {
logService: LogService,
loginEmailService: LoginEmailServiceAbstraction,
private formBuilder: FormBuilder,
protected toastService: ToastService,
) {
super(router, i18nService, apiService, platformUtilsService, logService, loginEmailService);
super(
router,
i18nService,
apiService,
platformUtilsService,
logService,
loginEmailService,
toastService,
);
}
ngOnInit(): void {

View File

@@ -28,6 +28,8 @@ 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 { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { flagEnabled } from "../../../utils/flags";
@@ -71,6 +73,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
ssoLoginService: SsoLoginServiceAbstraction,
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
registerRouteService: RegisterRouteService,
toastService: ToastService,
) {
super(
devicesApiService,
@@ -92,6 +95,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
ssoLoginService,
webAuthnLoginService,
registerRouteService,
toastService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
this.showPasswordless = flagEnabled("showPasswordless");
@@ -138,7 +142,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
}
}
async goAfterLogIn() {
async goAfterLogIn(userId: UserId) {
const masterPassword = this.formGroup.value.masterPassword;
// Check master password against policy
@@ -159,7 +163,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
) {
const policiesData: { [id: string]: PolicyData } = {};
this.policies.map((p) => (policiesData[p.id] = PolicyData.fromPolicy(p)));
await this.policyService.replace(policiesData);
await this.policyService.replace(policiesData, userId);
await this.router.navigate(["update-password"]);
return;
}

View File

@@ -9,6 +9,7 @@ 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 { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ToastService } from "@bitwarden/components";
import { SharedModule } from "../../shared";
import { UserKeyRotationModule } from "../key-rotation/user-key-rotation.module";
@@ -35,6 +36,7 @@ export class MigrateFromLegacyEncryptionComponent {
private messagingService: MessagingService,
private logService: LogService,
private syncService: SyncService,
private toastService: ToastService,
) {}
submit = async () => {
@@ -59,12 +61,12 @@ export class MigrateFromLegacyEncryptionComponent {
await this.keyRotationService.rotateUserKeyAndEncryptedData(masterPassword, activeUser);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("keyUpdated"),
this.i18nService.t("logBackInOthersToo"),
{ timeout: 15000 },
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("keyUpdated"),
message: this.i18nService.t("logBackInOthersToo"),
timeout: 15000,
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { DeleteRecoverRequest } from "@bitwarden/common/models/request/delete-recover.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-recover-delete",
@@ -25,6 +26,7 @@ export class RecoverDeleteComponent {
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private toastService: ToastService,
) {}
submit = async () => {
@@ -35,11 +37,11 @@ export class RecoverDeleteComponent {
const request = new DeleteRecoverRequest();
request.email = this.email.value.trim().toLowerCase();
await this.apiService.postAccountRecoverDelete(request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deleteRecoverEmailSent"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deleteRecoverEmailSent"),
});
await this.router.navigate(["/"]);
};

View File

@@ -8,6 +8,7 @@ import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-recover-two-factor",
@@ -27,6 +28,7 @@ export class RecoverTwoFactorComponent {
private i18nService: I18nService,
private cryptoService: CryptoService,
private loginStrategyService: LoginStrategyServiceAbstraction,
private toastService: ToastService,
) {}
get email(): string {
@@ -53,11 +55,11 @@ export class RecoverTwoFactorComponent {
const key = await this.loginStrategyService.makePreloginKey(this.masterPassword, request.email);
request.masterPasswordHash = await this.cryptoService.hashMasterKey(this.masterPassword, key);
await this.apiService.postTwoFactorRecover(request);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("twoStepRecoverDisabled"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepRecoverDisabled"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -17,7 +17,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 { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service";
@@ -52,6 +52,7 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
auditService: AuditService,
dialogService: DialogService,
acceptOrgInviteService: AcceptOrganizationInviteService,
toastService: ToastService,
) {
super(
formValidationErrorService,
@@ -68,6 +69,7 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
logService,
auditService,
dialogService,
toastService,
);
super.modifyRegisterRequest = async (request: RegisterRequest) => {
// Org invites are deep linked. Non-existent accounts are redirected to the register page.
@@ -104,11 +106,11 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
this.enforcedPolicyOptions,
)
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
});
return;
}

View File

@@ -15,7 +15,7 @@ import { ProfileResponse } from "@bitwarden/common/models/response/profile.respo
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 { DialogService, ToastService } from "@bitwarden/components";
type ChangeAvatarDialogData = {
profile: ProfileResponse;
@@ -55,6 +55,7 @@ export class ChangeAvatarDialogComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private avatarService: AvatarService,
private dialogRef: DialogRef,
private toastService: ToastService,
) {
this.profile = data.profile;
}
@@ -93,9 +94,17 @@ export class ChangeAvatarDialogComponent implements OnInit, OnDestroy {
if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) {
await this.avatarService.setAvatarColor(this.currentSelection);
this.dialogRef.close();
this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("avatarUpdated"),
});
} else {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
};

View File

@@ -12,6 +12,7 @@ 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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-change-email",
@@ -39,6 +40,7 @@ export class ChangeEmailComponent implements OnInit {
private stateService: StateService,
private formBuilder: FormBuilder,
private kdfConfigService: KdfConfigService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -100,11 +102,11 @@ export class ChangeEmailComponent implements OnInit {
try {
await this.apiService.postEmail(request);
this.reset();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("emailChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("emailChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -7,6 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-deauthorize-sessions",
@@ -23,6 +24,7 @@ export class DeauthorizeSessionsComponent {
private userVerificationService: UserVerificationService,
private messagingService: MessagingService,
private logService: LogService,
private toastService: ToastService,
) {}
async submit() {
@@ -31,11 +33,11 @@ export class DeauthorizeSessionsComponent {
.buildRequest(this.masterPassword)
.then((request) => this.apiService.postSecurityStamp(request));
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("sessionsDeauthorized"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("sessionsDeauthorized"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);

View File

@@ -7,7 +7,7 @@ import { Verification } from "@bitwarden/common/auth/types/verification";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
@Component({
templateUrl: "delete-account-dialog.component.html",
@@ -24,6 +24,7 @@ export class DeleteAccountDialogComponent {
private formBuilder: FormBuilder,
private accountApiService: AccountApiService,
private dialogRef: DialogRef,
private toastService: ToastService,
) {}
submit = async () => {
@@ -31,11 +32,11 @@ export class DeleteAccountDialogComponent {
const verification = this.deleteForm.get("verification").value;
await this.accountApiService.deleteAccount(verification);
this.dialogRef.close();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("accountDeleted"),
this.i18nService.t("accountDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("accountDeleted"),
message: this.i18nService.t("accountDeletedDesc"),
});
} catch (e) {
if (e instanceof ErrorResponse && e.statusCode === 400) {
this.invalidSecret = true;

View File

@@ -8,7 +8,7 @@ import { ProfileResponse } from "@bitwarden/common/models/response/profile.respo
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.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 { DialogService, ToastService } from "@bitwarden/components";
import { ChangeAvatarDialogComponent } from "./change-avatar-dialog.component";
@@ -33,6 +33,7 @@ export class ProfileComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private stateService: StateService,
private dialogService: DialogService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -64,6 +65,10 @@ export class ProfileComponent implements OnInit, OnDestroy {
submit = async () => {
const request = new UpdateProfileRequest(this.formGroup.get("name").value);
await this.apiService.putProfile(request);
this.platformUtilsService.showToast("success", null, this.i18nService.t("accountUpdated"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("accountUpdated"),
});
};
}

View File

@@ -22,7 +22,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { UserKeyRotationService } from "../key-rotation/user-key-rotation.service";
@@ -60,6 +60,7 @@ export class ChangePasswordComponent
kdfConfigService: KdfConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
toastService: ToastService,
) {
super(
i18nService,
@@ -73,6 +74,7 @@ export class ChangePasswordComponent
kdfConfigService,
masterPasswordService,
accountService,
toastService,
);
}
@@ -141,11 +143,11 @@ export class ChangePasswordComponent
this.masterPasswordHint != null &&
this.masterPasswordHint.toLowerCase() === this.masterPassword.toLowerCase()
) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("hintEqualsPassword"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("hintEqualsPassword"),
});
return;
}
@@ -159,11 +161,11 @@ export class ChangePasswordComponent
async setupSubmitActions() {
if (this.currentMasterPassword == null || this.currentMasterPassword === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPasswordRequired"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("masterPasswordRequired"),
});
return false;
}
@@ -194,11 +196,11 @@ export class ChangePasswordComponent
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
if (userKey == null) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("invalidMasterPassword"),
);
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("invalidMasterPassword"),
});
return;
}
@@ -225,14 +227,18 @@ export class ChangePasswordComponent
await this.formPromise;
this.platformUtilsService.showToast(
"success",
this.i18nService.t("masterPasswordChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("masterPasswordChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
} catch {
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("errorOccurred"),
});
}
}

View File

@@ -5,7 +5,7 @@ import { FormBuilder, Validators } from "@angular/forms";
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 { DialogService, ToastService } from "@bitwarden/components";
import { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessType } from "../../emergency-access/enums/emergency-access-type";
@@ -51,6 +51,7 @@ export class EmergencyAccessAddEditComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private logService: LogService,
private dialogRef: DialogRef<EmergencyAccessAddEditDialogResult>,
private toastService: ToastService,
) {}
async ngOnInit() {
this.editMode = this.loading = this.params.emergencyAccessId != null;
@@ -104,11 +105,14 @@ export class EmergencyAccessAddEditComponent implements OnInit {
this.addEditForm.value.waitTime,
);
}
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
this.editMode ? "editedUserId" : "invitedUsers",
this.params.name,
),
});
this.dialogRef.close(EmergencyAccessAddEditDialogResult.Saved);
} catch (e) {
this.logService.error(e);

View File

@@ -10,7 +10,7 @@ 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 { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { EmergencyAccessService } from "../../emergency-access";
import { EmergencyAccessStatusType } from "../../emergency-access/enums/emergency-access-status-type";
@@ -66,6 +66,7 @@ export class EmergencyAccessComponent implements OnInit {
protected dialogService: DialogService,
billingAccountProfileStateService: BillingAccountProfileStateService,
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
private toastService: ToastService,
) {
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
}
@@ -121,11 +122,11 @@ export class EmergencyAccessComponent implements OnInit {
}
this.actionPromise = this.emergencyAccessService.reinvite(contact.id);
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenReinvited", contact.email),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenReinvited", contact.email),
});
this.actionPromise = null;
}
@@ -153,11 +154,11 @@ export class EmergencyAccessComponent implements OnInit {
if (result === EmergencyAccessConfirmDialogResult.Confirmed) {
await this.emergencyAccessService.confirm(contact.id, contact.granteeId);
updateUser();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
});
}
return;
}
@@ -166,11 +167,11 @@ export class EmergencyAccessComponent implements OnInit {
await this.actionPromise;
updateUser();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact)),
});
this.actionPromise = null;
}
@@ -187,11 +188,11 @@ export class EmergencyAccessComponent implements OnInit {
try {
await this.emergencyAccessService.delete(details.id);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("removedUserId", this.userNamePipe.transform(details)),
});
if (details instanceof GranteeEmergencyAccess) {
this.removeGrantee(details);
@@ -221,11 +222,11 @@ export class EmergencyAccessComponent implements OnInit {
await this.emergencyAccessService.requestAccess(details.id);
details.status = EmergencyAccessStatusType.RecoveryInitiated;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("requestSent", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("requestSent", this.userNamePipe.transform(details)),
});
}
async approve(details: GranteeEmergencyAccess) {
@@ -250,22 +251,22 @@ export class EmergencyAccessComponent implements OnInit {
await this.emergencyAccessService.approve(details.id);
details.status = EmergencyAccessStatusType.RecoveryApproved;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("emergencyApproved", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emergencyApproved", this.userNamePipe.transform(details)),
});
}
async reject(details: GranteeEmergencyAccess) {
await this.emergencyAccessService.reject(details.id);
details.status = EmergencyAccessStatusType.Confirmed;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("emergencyRejected", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emergencyRejected", this.userNamePipe.transform(details)),
});
}
takeover = async (details: GrantorEmergencyAccess) => {
@@ -278,11 +279,11 @@ export class EmergencyAccessComponent implements OnInit {
});
const result = await lastValueFrom(dialogRef.closed);
if (result === EmergencyAccessTakeoverResultType.Done) {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details)),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details)),
});
}
};

View File

@@ -15,7 +15,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 { KdfType } from "@bitwarden/common/platform/enums";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { EmergencyAccessService } from "../../../emergency-access";
@@ -64,6 +64,7 @@ export class EmergencyAccessTakeoverComponent
kdfConfigService: KdfConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
protected toastService: ToastService,
) {
super(
i18nService,
@@ -77,6 +78,7 @@ export class EmergencyAccessTakeoverComponent
kdfConfigService,
masterPasswordService,
accountService,
toastService,
);
}
@@ -114,11 +116,11 @@ export class EmergencyAccessTakeoverComponent
);
} catch (e) {
this.logService.error(e);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("unexpectedError"),
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("unexpectedError"),
});
}
this.dialogRef.close(EmergencyAccessTakeoverResultType.Done);
};

View File

@@ -12,6 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-change-kdf-confirmation",
@@ -35,6 +36,7 @@ export class ChangeKdfConfirmationComponent {
private messagingService: MessagingService,
@Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig },
private accountService: AccountService,
private toastService: ToastService,
) {
this.kdfConfig = params.kdfConfig;
this.masterPassword = null;
@@ -46,11 +48,11 @@ export class ChangeKdfConfirmationComponent {
}
this.loading = true;
await this.makeKeyAndSaveAsync();
this.platformUtilsService.showToast(
"success",
this.i18nService.t("encKeySettingsChanged"),
this.i18nService.t("logBackIn"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("encKeySettingsChanged"),
message: this.i18nService.t("logBackIn"),
});
this.messagingService.send("logout");
this.loading = false;
};

View File

@@ -17,7 +17,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 { DialogService, ToastService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -68,6 +68,7 @@ export class TwoFactorAuthenticatorComponent
private accountService: AccountService,
dialogService: DialogService,
private configService: ConfigService,
protected toastService: ToastService,
) {
super(
apiService,
@@ -76,6 +77,7 @@ export class TwoFactorAuthenticatorComponent
logService,
userVerificationService,
dialogService,
toastService,
);
this.qrScript = window.document.createElement("script");
this.qrScript.src = "scripts/qrious.min.js";
@@ -148,7 +150,11 @@ export class TwoFactorAuthenticatorComponent
request.userVerificationToken = this.userVerificationToken;
await this.apiService.deleteTwoFactorAuthenticator(request);
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
}

View File

@@ -10,7 +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";
import { DialogService, ToastService } from "@bitwarden/components";
@Directive()
export abstract class TwoFactorBaseComponent {
@@ -33,6 +33,7 @@ export abstract class TwoFactorBaseComponent {
protected logService: LogService,
protected userVerificationService: UserVerificationService,
protected dialogService: DialogService,
protected toastService: ToastService,
) {}
protected auth(authResponse: AuthResponseBase) {
@@ -76,7 +77,11 @@ export abstract class TwoFactorBaseComponent {
}
await promise;
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
} catch (e) {
this.logService.error(e);
@@ -102,7 +107,11 @@ export abstract class TwoFactorBaseComponent {
await this.apiService.putTwoFactorDisable(request);
}
this.enabled = false;
this.platformUtilsService.showToast("success", null, this.i18nService.t("twoStepDisabled"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("twoStepDisabled"),
});
this.onUpdated.emit(false);
}

View File

@@ -11,7 +11,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 { DialogService, ToastService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -40,6 +40,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent implements OnI
dialogService: DialogService,
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
protected toastService: ToastService,
) {
super(
apiService,
@@ -48,6 +49,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent implements OnI
logService,
userVerificationService,
dialogService,
toastService,
);
}

View File

@@ -14,7 +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 { DialogService, ToastService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -45,6 +45,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent implements O
dialogService: DialogService,
private formBuilder: FormBuilder,
private dialogRef: DialogRef,
protected toastService: ToastService,
) {
super(
apiService,
@@ -53,6 +54,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent implements O
logService,
userVerificationService,
dialogService,
toastService,
);
}
get token() {

View File

@@ -16,7 +16,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 { DialogService, ToastService } from "@bitwarden/components";
import { TwoFactorBaseComponent } from "./two-factor-base.component";
@@ -61,6 +61,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
logService: LogService,
userVerificationService: UserVerificationService,
dialogService: DialogService,
toastService: ToastService,
) {
super(
apiService,
@@ -69,6 +70,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
logService,
userVerificationService,
dialogService,
toastService,
);
this.auth(data);
}

View File

@@ -55,7 +55,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent implements
userVerificationService: UserVerificationService,
dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
protected toastService: ToastService,
) {
super(
apiService,
@@ -64,6 +64,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent implements
logService,
userVerificationService,
dialogService,
toastService,
);
}

View File

@@ -6,7 +6,13 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { AsyncActionsModule, BannerModule, ButtonModule, LinkModule } from "@bitwarden/components";
import {
AsyncActionsModule,
BannerModule,
ButtonModule,
LinkModule,
ToastService,
} from "@bitwarden/components";
@Component({
standalone: true,
@@ -25,22 +31,27 @@ export class VerifyEmailComponent {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private tokenService: TokenService,
private toastService: ToastService,
) {}
async verifyEmail(): Promise<void> {
await this.apiService.refreshIdentityToken();
if (await this.tokenService.getEmailVerified()) {
this.onVerified.emit(true);
this.platformUtilsService.showToast("success", null, this.i18nService.t("emailVerified"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emailVerified"),
});
return;
}
await this.apiService.postAccountVerifyEmail();
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("checkInboxForVerification"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("checkInboxForVerification"),
});
}
send = async () => {

View File

@@ -9,7 +9,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.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 { DialogService, ToastService } from "@bitwarden/components";
import { WebauthnLoginAdminService } from "../../../core";
import { CredentialCreateOptionsView } from "../../../core/views/credential-create-options.view";
@@ -60,6 +60,7 @@ export class CreateCredentialDialogComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private logService: LogService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -102,11 +103,11 @@ export class CreateCredentialDialogComponent implements OnInit {
this.invalidSecret = true;
} else {
this.logService?.error(error);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("unexpectedError"),
error.message,
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("unexpectedError"),
message: error.message,
});
}
return;
}
@@ -162,17 +163,17 @@ export class CreateCredentialDialogComponent implements OnInit {
);
if (await firstValueFrom(this.hasPasskeys$)) {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("passkeySaved", name),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passkeySaved", name),
});
} else {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("loginWithPasskeyEnabled"),
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("loginWithPasskeyEnabled"),
});
}
this.dialogRef.close(CreateCredentialDialogResult.Success);

View File

@@ -8,7 +8,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.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 { DialogService, ToastService } from "@bitwarden/components";
import { WebauthnLoginAdminService } from "../../../core";
import { WebauthnLoginCredentialView } from "../../../core/views/webauthn-login-credential.view";
@@ -38,6 +38,7 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private logService: LogService,
private toastService: ToastService,
) {}
ngOnInit(): void {
@@ -55,17 +56,21 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy {
this.dialogRef.disableClose = true;
try {
await this.webauthnService.deleteCredential(this.credential.id, this.formGroup.value.secret);
this.platformUtilsService.showToast("success", null, this.i18nService.t("passkeyRemoved"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("passkeyRemoved"),
});
} catch (error) {
if (error instanceof ErrorResponse && error.statusCode === 400) {
this.invalidSecret = true;
} else {
this.logService?.error(error);
this.platformUtilsService.showToast(
"error",
this.i18nService.t("unexpectedError"),
error.message,
);
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("unexpectedError"),
message: error.message,
});
}
return false;
} finally {

View File

@@ -9,7 +9,7 @@ import {
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";
import { DialogService, ToastService } from "@bitwarden/components";
/**
* @deprecated Jan 24, 2024: Use new libs/auth UserVerificationDialogComponent instead.
@@ -25,8 +25,17 @@ export class UserVerificationPromptComponent extends BaseUserVerificationPrompt
formBuilder: FormBuilder,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService,
toastService: ToastService,
) {
super(null, data, userVerificationService, formBuilder, platformUtilsService, i18nService);
super(
null,
data,
userVerificationService,
formBuilder,
platformUtilsService,
i18nService,
toastService,
);
}
override close(success: boolean) {

View File

@@ -25,6 +25,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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { ToastService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
@Component({
@@ -60,6 +61,7 @@ export class SsoComponent extends BaseSsoComponent implements OnInit {
configService: ConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
toastService: ToastService,
) {
super(
ssoLoginService,
@@ -78,6 +80,7 @@ export class SsoComponent extends BaseSsoComponent implements OnInit {
configService,
masterPasswordService,
accountService,
toastService,
);
this.redirectUri = window.location.origin + "/sso-connector.html";
this.clientId = "web";

View File

@@ -17,7 +17,13 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
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 { LinkModule, TypographyModule, CheckboxModule, DialogService } from "@bitwarden/components";
import {
LinkModule,
TypographyModule,
CheckboxModule,
DialogService,
ToastService,
} from "@bitwarden/components";
import { TwoFactorAuthAuthenticatorComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-authenticator.component";
import { TwoFactorAuthEmailComponent } from "../../../../../libs/angular/src/auth/components/two-factor-auth/two-factor-auth-email.component";
@@ -81,6 +87,7 @@ export class TwoFactorAuthComponent extends BaseTwoFactorAuthComponent {
accountService: AccountService,
formBuilder: FormBuilder,
@Inject(WINDOW) protected win: Window,
toastService: ToastService,
) {
super(
loginStrategyService,
@@ -100,6 +107,7 @@ export class TwoFactorAuthComponent extends BaseTwoFactorAuthComponent {
accountService,
formBuilder,
win,
toastService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}

View File

@@ -8,6 +8,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 { ToastService } from "@bitwarden/components";
@Component({
selector: "app-verify-email-token",
@@ -23,6 +24,7 @@ export class VerifyEmailTokenComponent implements OnInit {
private apiService: ApiService,
private logService: LogService,
private stateService: StateService,
private toastService: ToastService,
) {}
ngOnInit() {
@@ -36,7 +38,11 @@ export class VerifyEmailTokenComponent implements OnInit {
if (await this.stateService.getIsAuthenticated()) {
await this.apiService.refreshIdentityToken();
}
this.platformUtilsService.showToast("success", null, this.i18nService.t("emailVerified"));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("emailVerified"),
});
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/"]);
@@ -45,7 +51,11 @@ export class VerifyEmailTokenComponent implements OnInit {
this.logService.error(e);
}
}
this.platformUtilsService.showToast("error", null, this.i18nService.t("emailVerifiedFailed"));
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t("emailVerifiedFailed"),
});
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/"]);

View File

@@ -7,6 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VerifyDeleteRecoverRequest } from "@bitwarden/common/models/request/verify-delete-recover.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-verify-recover-delete",
@@ -26,6 +27,7 @@ export class VerifyRecoverDeleteComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private route: ActivatedRoute,
private toastService: ToastService,
) {}
ngOnInit() {
@@ -44,11 +46,11 @@ export class VerifyRecoverDeleteComponent implements OnInit {
submit = async () => {
const request = new VerifyDeleteRecoverRequest(this.userId, this.token);
await this.apiService.postAccountRecoverDeleteToken(request);
this.platformUtilsService.showToast(
"success",
this.i18nService.t("accountDeleted"),
this.i18nService.t("accountDeletedDesc"),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("accountDeleted"),
message: this.i18nService.t("accountDeletedDesc"),
});
await this.router.navigate(["/"]);
};
}

View File

@@ -56,4 +56,3 @@
{{ "save" | i18n }}
</button>
</form>
<app-payment [showMethods]="false"></app-payment>

View File

@@ -1,43 +1,36 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject, takeUntil } from "rxjs";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@Component({
selector: "app-adjust-subscription",
templateUrl: "adjust-subscription.component.html",
})
export class AdjustSubscription implements OnInit, OnDestroy {
export class AdjustSubscription {
@Input() organizationId: string;
@Input() maxAutoscaleSeats: number;
@Input() currentSeatCount: number;
@Input() seatPrice = 0;
@Input() interval = "year";
@Output() onAdjusted = new EventEmitter();
private destroy$ = new Subject<void>();
adjustSubscriptionForm = this.formBuilder.group({
newSeatCount: [0, [Validators.min(0)]],
limitSubscription: [false],
newMaxSeats: [0, [Validators.min(0)]],
});
get limitSubscription(): boolean {
return this.adjustSubscriptionForm.value.limitSubscription;
}
constructor(
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private organizationApiService: OrganizationApiServiceAbstraction,
private formBuilder: FormBuilder,
private toastService: ToastService,
) {}
ngOnInit() {
) {
this.adjustSubscriptionForm.patchValue({
newSeatCount: this.currentSeatCount,
limitSubscription: this.maxAutoscaleSeats != null,
@@ -45,7 +38,7 @@ export class AdjustSubscription implements OnInit, OnDestroy {
});
this.adjustSubscriptionForm
.get("limitSubscription")
.valueChanges.pipe(takeUntil(this.destroy$))
.valueChanges.pipe(takeUntilDestroyed())
.subscribe((value: boolean) => {
if (value) {
this.adjustSubscriptionForm
@@ -63,10 +56,6 @@ export class AdjustSubscription implements OnInit, OnDestroy {
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
submit = async () => {
this.adjustSubscriptionForm.markAllAsTouched();
if (this.adjustSubscriptionForm.invalid) {
@@ -99,18 +88,15 @@ export class AdjustSubscription implements OnInit, OnDestroy {
: 0;
}
get additionalMaxSeatCount(): number {
return this.adjustSubscriptionForm.value.newMaxSeats
? this.adjustSubscriptionForm.value.newMaxSeats - this.currentSeatCount
: 0;
}
get maxSeatTotal(): number {
return Math.abs((this.adjustSubscriptionForm.value.newMaxSeats ?? 0) * this.seatPrice);
}
get seatTotalCost(): number {
const totalSeat = Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice);
return totalSeat;
return Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice);
}
get limitSubscription(): boolean {
return this.adjustSubscriptionForm.value.limitSubscription;
}
}

View File

@@ -30,6 +30,8 @@
bitIconButton="bwi-clone"
bitSuffix
type="button"
showToast
[valueLabel]="'billingSyncKey' | i18n"
[appCopyClick]="clientSecret"
[appA11yTitle]="'copyValue' | i18n"
></button>

View File

@@ -37,7 +37,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "../shared/payment.component";
import { PaymentComponent } from "../shared/payment/payment.component";
import { TaxInfoComponent } from "../shared/tax-info.component";
type ChangePlanDialogParams = {

View File

@@ -1,7 +1,9 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { organizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard";
@@ -11,6 +13,7 @@ import { PaymentMethodComponent } from "../shared";
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
const routes: Routes = [
{
@@ -25,17 +28,21 @@ const routes: Routes = [
: OrganizationSubscriptionCloudComponent,
data: { titleId: "subscription" },
},
{
path: "payment-method",
component: PaymentMethodComponent,
canActivate: [
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
organizationIsUnmanaged,
],
data: {
titleId: "paymentMethod",
...featureFlaggedRoute({
defaultComponent: PaymentMethodComponent,
flaggedComponent: OrganizationPaymentMethodComponent,
featureFlag: FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
routeOptions: {
path: "payment-method",
canActivate: [
organizationPermissionsGuard((org) => org.canEditPaymentMethods),
organizationIsUnmanaged,
],
data: {
titleId: "paymentMethod",
},
},
},
}),
{
path: "history",
component: OrgBillingHistoryViewComponent,

View File

@@ -15,6 +15,7 @@ import { OrganizationBillingRoutingModule } from "./organization-billing-routing
import { OrganizationPlansComponent } from "./organization-plans.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { OrganizationPaymentMethodComponent } from "./payment-method/organization-payment-method.component";
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
@@ -42,6 +43,7 @@ import { SubscriptionStatusComponent } from "./subscription-status.component";
SubscriptionHiddenComponent,
SubscriptionStatusComponent,
ChangePlanDialogComponent,
OrganizationPaymentMethodComponent,
],
})
export class OrganizationBillingModule {}

View File

@@ -41,7 +41,7 @@ import { ToastService } from "@bitwarden/components";
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
import { PaymentComponent } from "../shared/payment.component";
import { PaymentComponent } from "../shared/payment/payment.component";
import { TaxInfoComponent } from "../shared/tax-info.component";
interface OnSuccessArgs {

View File

@@ -222,8 +222,10 @@
</ng-container>
</ng-container>
<h2 bitTypography="h2" class="tw-mt-7">{{ "selfHostingTitle" | i18n }}</h2>
<p bitTypography="body1">
<h2 bitTypography="h2" *ngIf="shownSelfHost()" class="tw-mt-7">
{{ "selfHostingTitle" | i18n }}
</h2>
<p bitTypography="body1" *ngIf="shownSelfHost()">
{{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }}
<a
href="https://bitwarden.com/help/licensing-on-premise/#retrieve-organization-license"
@@ -240,7 +242,7 @@
buttonType="secondary"
type="button"
(click)="downloadLicense()"
*ngIf="canDownloadLicense"
*ngIf="canDownloadLicense && shownSelfHost()"
[disabled]="showDownloadLicense"
>
{{ "downloadLicense" | i18n }}

View File

@@ -345,6 +345,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
);
}
shownSelfHost(): boolean {
return (
this.sub?.plan.productTier !== ProductTierType.Teams &&
this.sub?.plan.productTier !== ProductTierType.Free
);
}
cancelSubscription = async () => {
const reference = openOffboardingSurvey(this.dialogService, {
data: {

View File

@@ -0,0 +1,63 @@
<app-header></app-header>
<bit-container>
<ng-container *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="!loading">
<!-- Account Credit -->
<bit-section>
<h2 bitTypography="h2">
{{ accountCreditHeaderText }}
</h2>
<p class="tw-text-lg tw-font-bold">{{ Math.abs(accountCredit) | currency: "$" }}</p>
<p bitTypography="body1">{{ "creditAppliedDesc" | i18n }}</p>
<button type="button" bitButton buttonType="secondary" [bitAction]="addAccountCredit">
{{ "addCredit" | i18n }}
</button>
</bit-section>
<!-- Payment Method -->
<bit-section>
<h2 bitTypography="h2">{{ "paymentMethod" | i18n }}</h2>
<p *ngIf="!paymentSource" bitTypography="body1">{{ "noPaymentMethod" | i18n }}</p>
<ng-container *ngIf="paymentSource">
<app-verify-bank-account
*ngIf="paymentSource.needsVerification"
[onSubmit]="verifyBankAccount"
(submitted)="load()"
>
</app-verify-bank-account>
<p>
<i class="bwi bwi-fw" [ngClass]="paymentSourceClasses"></i>
{{ paymentSource.description }}
<span *ngIf="paymentSource.needsVerification">- {{ "unverified" | i18n }}</span>
</p>
</ng-container>
<button type="button" bitButton buttonType="secondary" [bitAction]="updatePaymentMethod">
{{ updatePaymentSourceButtonText }}
</button>
<p *ngIf="subscriptionIsUnpaid" bitTypography="body1">
{{ "paymentChargedWithUnpaidSubscription" | i18n }}
</p>
</bit-section>
<!-- Tax Information -->
<bit-section>
<h2 bitTypography="h2">{{ "taxInformation" | i18n }}</h2>
<p bitTypography="body1">{{ "taxInformationDesc" | i18n }}</p>
<app-tax-info></app-tax-info>
<button
type="button"
bitButton
bitFormButton
buttonType="primary"
[bitAction]="updateTaxInformation"
>
{{ "save" | i18n }}
</button>
</bit-section>
</ng-container>
</bit-container>

View File

@@ -0,0 +1,169 @@
import { Component, ViewChild } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { from, lastValueFrom, switchMap } from "rxjs";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { TaxInfoComponent } from "../../shared";
import {
AddCreditDialogResult,
openAddCreditDialog,
} from "../../shared/add-credit-dialog.component";
import {
AdjustPaymentDialogV2Component,
AdjustPaymentDialogV2ResultType,
} from "../../shared/adjust-payment-dialog/adjust-payment-dialog-v2.component";
@Component({
templateUrl: "./organization-payment-method.component.html",
})
export class OrganizationPaymentMethodComponent {
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
organizationId: string;
accountCredit: number;
paymentSource?: PaymentSourceResponse;
subscriptionStatus?: string;
loading = true;
protected readonly Math = Math;
constructor(
private activatedRoute: ActivatedRoute,
private billingApiService: BillingApiServiceAbstraction,
private dialogService: DialogService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private router: Router,
private toastService: ToastService,
) {
this.activatedRoute.params
.pipe(
takeUntilDestroyed(),
switchMap(({ organizationId }) => {
if (this.platformUtilsService.isSelfHost()) {
return from(this.router.navigate(["/settings/subscription"]));
}
this.organizationId = organizationId;
return from(this.load());
}),
)
.subscribe();
}
protected addAccountCredit = async (): Promise<void> => {
const dialogRef = openAddCreditDialog(this.dialogService, {
data: {
organizationId: this.organizationId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AddCreditDialogResult.Added) {
await this.load();
}
};
protected load = async (): Promise<void> => {
this.loading = true;
const { accountCredit, paymentSource, subscriptionStatus } =
await this.billingApiService.getOrganizationPaymentMethod(this.organizationId);
this.accountCredit = accountCredit;
this.paymentSource = paymentSource;
this.subscriptionStatus = subscriptionStatus;
this.loading = false;
};
protected updatePaymentMethod = async (): Promise<void> => {
const dialogRef = AdjustPaymentDialogV2Component.open(this.dialogService, {
data: {
initialPaymentMethod: this.paymentSource?.type,
organizationId: this.organizationId,
},
});
const result = await lastValueFrom(dialogRef.closed);
if (result === AdjustPaymentDialogV2ResultType.Submitted) {
await this.load();
}
};
protected updateTaxInformation = async (): Promise<void> => {
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
if (this.taxInfoComponent.taxFormGroup.invalid) {
return;
}
const request = new ExpandedTaxInfoUpdateRequest();
request.country = this.taxInfoComponent.country;
request.postalCode = this.taxInfoComponent.postalCode;
request.taxId = this.taxInfoComponent.taxId;
request.line1 = this.taxInfoComponent.line1;
request.line2 = this.taxInfoComponent.line2;
request.city = this.taxInfoComponent.city;
request.state = this.taxInfoComponent.state;
await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("taxInfoUpdated"),
});
};
protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise<void> => {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("verifiedBankAccount"),
});
};
protected get accountCreditHeaderText(): string {
const key = this.accountCredit <= 0 ? "accountBalance" : "accountCredit";
return this.i18nService.t(key);
}
protected get paymentSourceClasses() {
if (this.paymentSource == null) {
return [];
}
switch (this.paymentSource.type) {
case PaymentMethodType.Card:
return ["bwi-credit-card"];
case PaymentMethodType.BankAccount:
return ["bwi-bank"];
case PaymentMethodType.Check:
return ["bwi-money"];
case PaymentMethodType.PayPal:
return ["bwi-paypal text-primary"];
default:
return [];
}
}
protected get subscriptionIsUnpaid(): boolean {
return this.subscriptionStatus === "unpaid";
}
protected get updatePaymentSourceButtonText(): string {
const key = this.paymentSource == null ? "addPaymentMethod" : "changePaymentMethod";
return this.i18nService.t(key);
}
}

View File

@@ -0,0 +1,24 @@
<bit-dialog dialogSize="large" [title]="dialogHeader">
<ng-container bitDialogContent>
<app-payment-v2
[showAccountCredit]="false"
[showBankAccount]="!!organizationId"
[initialPaymentMethod]="initialPaymentMethod"
></app-payment-v2>
<app-tax-info (onCountryChanged)="onCountryChanged()"></app-tax-info>
</ng-container>
<ng-container bitDialogFooter>
<button type="submit" bitButton bitFormButton buttonType="primary" [bitAction]="submit">
{{ "submit" }}
</button>
<button
type="button"
bitButton
bitFormButton
buttonType="secondary"
[bitDialogClose]="ResultType.Closed"
>
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -0,0 +1,123 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { forwardRef, Component, Inject, ViewChild } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { TaxInfoComponent } from "../";
import { PaymentV2Component } from "../payment/payment-v2.component";
export interface AdjustPaymentDialogV2Params {
initialPaymentMethod?: PaymentMethodType;
organizationId?: string;
}
export enum AdjustPaymentDialogV2ResultType {
Closed = "closed",
Submitted = "submitted",
}
@Component({
templateUrl: "./adjust-payment-dialog-v2.component.html",
})
export class AdjustPaymentDialogV2Component {
@ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component;
@ViewChild(forwardRef(() => TaxInfoComponent)) taxInfoComponent: TaxInfoComponent;
protected readonly PaymentMethodType = PaymentMethodType;
protected readonly ResultType = AdjustPaymentDialogV2ResultType;
protected dialogHeader: string;
protected initialPaymentMethod: PaymentMethodType;
protected organizationId?: string;
constructor(
private apiService: ApiService,
private billingApiService: BillingApiServiceAbstraction,
@Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params,
private dialogRef: DialogRef<AdjustPaymentDialogV2ResultType>,
private i18nService: I18nService,
private toastService: ToastService,
) {
const key = this.dialogParams.initialPaymentMethod ? "changePaymentMethod" : "addPaymentMethod";
this.dialogHeader = this.i18nService.t(key);
this.initialPaymentMethod = this.dialogParams.initialPaymentMethod ?? PaymentMethodType.Card;
this.organizationId = this.dialogParams.organizationId;
}
onCountryChanged = () => {
if (this.taxInfoComponent.taxInfo.country === "US") {
this.paymentComponent.showBankAccount = !!this.organizationId;
} else {
this.paymentComponent.showBankAccount = false;
if (this.paymentComponent.selected === PaymentMethodType.BankAccount) {
this.paymentComponent.select(PaymentMethodType.Card);
}
}
};
submit = async (): Promise<void> => {
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
if (this.taxInfoComponent.taxFormGroup.invalid) {
return;
}
if (!this.organizationId) {
await this.updatePremiumUserPaymentMethod();
} else {
await this.updateOrganizationPaymentMethod();
}
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("updatedPaymentMethod"),
});
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
};
private updateOrganizationPaymentMethod = async () => {
const paymentSource = await this.paymentComponent.tokenize();
const request = new UpdatePaymentMethodRequest();
request.paymentSource = paymentSource;
request.taxInformation = {
country: this.taxInfoComponent.country,
postalCode: this.taxInfoComponent.postalCode,
taxId: this.taxInfoComponent.taxId,
line1: this.taxInfoComponent.line1,
line2: this.taxInfoComponent.line2,
city: this.taxInfoComponent.city,
state: this.taxInfoComponent.state,
};
await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
};
private updatePremiumUserPaymentMethod = async () => {
const { type, token } = await this.paymentComponent.tokenize();
const request = new PaymentRequest();
request.paymentMethodType = type;
request.paymentToken = token;
request.country = this.taxInfoComponent.country;
request.postalCode = this.taxInfoComponent.postalCode;
await this.apiService.postAccountPayment(request);
};
static open = (
dialogService: DialogService,
dialogConfig: DialogConfig<AdjustPaymentDialogV2Params>,
) =>
dialogService.open<AdjustPaymentDialogV2ResultType>(
AdjustPaymentDialogV2Component,
dialogConfig,
);
}

View File

@@ -10,8 +10,8 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "./payment.component";
import { TaxInfoComponent } from "./tax-info.component";
import { PaymentComponent } from "../payment/payment.component";
import { TaxInfoComponent } from "../tax-info.component";
export interface AdjustPaymentDialogData {
organizationId: string;

View File

@@ -12,7 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { PaymentComponent } from "./payment.component";
import { PaymentComponent } from "./payment/payment.component";
export interface AdjustStorageDialogData {
storageGbPrice: number;

View File

@@ -4,19 +4,29 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared";
import { AddCreditDialogComponent } from "./add-credit-dialog.component";
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component";
import { AdjustPaymentDialogV2Component } from "./adjust-payment-dialog/adjust-payment-dialog-v2.component";
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog/adjust-payment-dialog.component";
import { AdjustStorageComponent } from "./adjust-storage.component";
import { BillingHistoryComponent } from "./billing-history.component";
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
import { PaymentV2Component } from "./payment/payment-v2.component";
import { PaymentComponent } from "./payment/payment.component";
import { PaymentMethodComponent } from "./payment-method.component";
import { PaymentComponent } from "./payment.component";
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
import { TaxInfoComponent } from "./tax-info.component";
import { UpdateLicenseDialogComponent } from "./update-license-dialog.component";
import { UpdateLicenseComponent } from "./update-license.component";
import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-account.component";
@NgModule({
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
imports: [
SharedModule,
PaymentComponent,
TaxInfoComponent,
HeaderModule,
PaymentV2Component,
VerifyBankAccountComponent,
],
declarations: [
AddCreditDialogComponent,
AdjustPaymentDialogComponent,
@@ -27,6 +37,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
UpdateLicenseComponent,
UpdateLicenseDialogComponent,
OffboardingSurveyComponent,
AdjustPaymentDialogV2Component,
],
exports: [
SharedModule,
@@ -38,6 +49,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
UpdateLicenseComponent,
UpdateLicenseDialogComponent,
OffboardingSurveyComponent,
VerifyBankAccountComponent,
],
})
export class BillingSharedModule {}

View File

@@ -1,5 +1,5 @@
export * from "./billing-shared.module";
export * from "./payment-method.component";
export * from "./payment.component";
export * from "./payment/payment.component";
export * from "./sm-subscribe.component";
export * from "./tax-info.component";

View File

@@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService, ToastService } from "@bitwarden/components";

View File

@@ -19,7 +19,7 @@ import { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.
import {
AdjustPaymentDialogResult,
openAdjustPaymentDialog,
} from "./adjust-payment-dialog.component";
} from "./adjust-payment-dialog/adjust-payment-dialog.component";
import { TaxInfoComponent } from "./tax-info.component";
@Component({

View File

@@ -0,0 +1,158 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<div class="tw-mb-4 tw-text-lg">
<bit-radio-group formControlName="paymentMethod">
<bit-radio-button id="card-payment-method" [value]="PaymentMethodType.Card">
<bit-label>
<i class="bwi bwi-fw bwi-credit-card" aria-hidden="true"></i>
{{ "creditCard" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="bank-payment-method"
[value]="PaymentMethodType.BankAccount"
*ngIf="showBankAccount"
>
<bit-label>
<i class="bwi bwi-fw bwi-bank" aria-hidden="true"></i>
{{ "bankAccount" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="paypal-payment-method"
[value]="PaymentMethodType.PayPal"
*ngIf="showPayPal"
>
<bit-label>
<i class="bwi bwi-fw bwi-paypal" aria-hidden="true"></i>
{{ "payPal" | i18n }}
</bit-label>
</bit-radio-button>
<bit-radio-button
id="credit-payment-method"
[value]="PaymentMethodType.Credit"
*ngIf="showAccountCredit"
>
<bit-label>
<i class="bwi bwi-fw bwi-dollar" aria-hidden="true"></i>
{{ "accountCredit" | i18n }}
</bit-label>
</bit-radio-button>
</bit-radio-group>
</div>
<!-- Card -->
<ng-container *ngIf="usingCard">
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4">
<div class="tw-col-span-1">
<label for="stripe-card-number">
{{ "number" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<div id="stripe-card-number" class="form-control stripe-form-control"></div>
</div>
<div class="tw-col-span-1 tw-flex tw-items-end">
<img
src="../../../images/cards.png"
alt="Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay"
class="tw-max-w-full"
/>
</div>
<div class="tw-col-span-1">
<label for="stripe-card-expiry">
{{ "expiration" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<div id="stripe-card-expiry" class="form-control stripe-form-control"></div>
</div>
<div class="tw-col-span-1">
<div class="tw-flex">
<label for="stripe-card-cvc">
{{ "securityCode" | i18n }}
<span class="tw-text-xs tw-font-normal">({{ "required" | i18n }})</span>
</label>
<a
href="https://www.cvvnumber.com/cvv.html"
tabindex="-1"
target="_blank"
rel="noreferrer"
class="ml-auto"
appA11yTitle="{{ 'learnMore' | i18n }}"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</div>
<div id="stripe-card-cvc" class="form-control stripe-form-control"></div>
</div>
</div>
</ng-container>
<!-- Bank Account -->
<ng-container *ngIf="showBankAccount && usingBankAccount">
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
{{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}
</app-callout>
<div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4" formGroupName="bankInformation">
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "routingNumber" | i18n }}</bit-label>
<input
bitInput
id="routingNumber"
type="text"
formControlName="routingNumber"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "accountNumber" | i18n }}</bit-label>
<input
bitInput
id="accountNumber"
type="text"
formControlName="accountNumber"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "accountHolderName" | i18n }}</bit-label>
<input
id="accountHolderName"
bitInput
type="text"
formControlName="accountHolderName"
required
appInputVerbatim
/>
</bit-form-field>
<bit-form-field class="tw-col-span-1" disableMargin>
<bit-label>{{ "bankAccountType" | i18n }}</bit-label>
<bit-select id="accountHolderType" formControlName="accountHolderType" required>
<bit-option [value]="''" label="-- {{ 'select' | i18n }} --"></bit-option>
<bit-option
[value]="'company'"
label="{{ 'bankAccountTypeCompany' | i18n }}"
></bit-option>
<bit-option
[value]="'individual'"
label="{{ 'bankAccountTypeIndividual' | i18n }}"
></bit-option>
</bit-select>
</bit-form-field>
</div>
</ng-container>
<!-- PayPal -->
<ng-container *ngIf="showPayPal && usingPayPal">
<div class="tw-mb-3">
<div id="braintree-container" class="tw-mb-1 tw-content-center"></div>
<small class="tw-text-muted">{{ "paypalClickSubmit" | i18n }}</small>
</div>
</ng-container>
<!-- Account Credit -->
<ng-container *ngIf="showAccountCredit && usingAccountCredit">
<app-callout type="info">
{{ "makeSureEnoughCredit" | i18n }}
</app-callout>
</ng-container>
<button *ngIf="!!onSubmit" bitButton bitFormButton buttonType="primary" type="submit">
{{ "submit" | i18n }}
</button>
</form>

View File

@@ -0,0 +1,192 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import {
BillingApiServiceAbstraction,
BraintreeServiceAbstraction,
StripeServiceAbstraction,
} from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request";
import { SharedModule } from "../../../shared";
/**
* Render a form that allows the user to enter their payment method, tokenize it against one of our payment providers and,
* optionally, submit it using the {@link onSubmit} function if it is provided.
*
* This component is meant to replace the existing {@link PaymentComponent} which is using the deprecated Stripe Sources API.
*/
@Component({
selector: "app-payment-v2",
templateUrl: "./payment-v2.component.html",
standalone: true,
imports: [SharedModule],
})
export class PaymentV2Component implements OnInit, OnDestroy {
/** Show account credit as a payment option. */
@Input() showAccountCredit: boolean = true;
/** Show bank account as a payment option. */
@Input() showBankAccount: boolean = true;
/** Show PayPal as a payment option. */
@Input() showPayPal: boolean = true;
/** The payment method selected by default when the component renders. */
@Input() private initialPaymentMethod: PaymentMethodType = PaymentMethodType.Card;
/** If provided, will be invoked with the tokenized payment source during form submission. */
@Input() protected onSubmit?: (request: TokenizedPaymentSourceRequest) => Promise<void>;
@Output() submitted = new EventEmitter<PaymentMethodType>();
private destroy$ = new Subject<void>();
protected formGroup = new FormGroup({
paymentMethod: new FormControl<PaymentMethodType>(null),
bankInformation: new FormGroup({
routingNumber: new FormControl<string>("", [Validators.required]),
accountNumber: new FormControl<string>("", [Validators.required]),
accountHolderName: new FormControl<string>("", [Validators.required]),
accountHolderType: new FormControl<string>("", [Validators.required]),
}),
});
protected PaymentMethodType = PaymentMethodType;
constructor(
private billingApiService: BillingApiServiceAbstraction,
private braintreeService: BraintreeServiceAbstraction,
private stripeService: StripeServiceAbstraction,
) {}
ngOnInit(): void {
this.formGroup.controls.paymentMethod.patchValue(this.initialPaymentMethod);
this.stripeService.loadStripe(
{
cardNumber: "#stripe-card-number",
cardExpiry: "#stripe-card-expiry",
cardCvc: "#stripe-card-cvc",
},
this.initialPaymentMethod === PaymentMethodType.Card,
);
if (this.showPayPal) {
this.braintreeService.loadBraintree(
"#braintree-container",
this.initialPaymentMethod === PaymentMethodType.PayPal,
);
}
this.formGroup
.get("paymentMethod")
.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((type) => {
this.onPaymentMethodChange(type);
});
}
/** Programmatically select the provided payment method. */
select = (paymentMethod: PaymentMethodType) => {
this.formGroup.value.paymentMethod = paymentMethod;
};
protected submit = async () => {
const { type, token } = await this.tokenize();
await this.onSubmit({ type, token });
this.submitted.emit(type);
};
/**
* Tokenize the payment method information entered by the user against one of our payment providers.
*
* - {@link PaymentMethodType.Card} => [Stripe.confirmCardSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_card_setup}
* - {@link PaymentMethodType.BankAccount} => [Stripe.confirmUsBankAccountSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_us_bank_account_setup}
* - {@link PaymentMethodType.PayPal} => [Braintree.requestPaymentMethod]{@link https://braintree.github.io/braintree-web-drop-in/docs/current/Dropin.html#requestPaymentMethod}
* */
async tokenize(): Promise<{ type: PaymentMethodType; token: string }> {
const type = this.selected;
if (this.usingStripe) {
const clientSecret = await this.billingApiService.createSetupIntent(type);
if (this.usingBankAccount) {
const token = await this.stripeService.setupBankAccountPaymentMethod(clientSecret, {
accountHolderName: this.formGroup.value.bankInformation.accountHolderName,
routingNumber: this.formGroup.value.bankInformation.routingNumber,
accountNumber: this.formGroup.value.bankInformation.accountNumber,
accountHolderType: this.formGroup.value.bankInformation.accountHolderType,
});
return {
type,
token,
};
}
if (this.usingCard) {
const token = await this.stripeService.setupCardPaymentMethod(clientSecret);
return {
type,
token,
};
}
}
if (this.usingPayPal) {
const token = await this.braintreeService.requestPaymentMethod();
return {
type,
token,
};
}
return null;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
this.stripeService.unloadStripe();
if (this.showPayPal) {
this.braintreeService.unloadBraintree();
}
}
private onPaymentMethodChange(type: PaymentMethodType): void {
switch (type) {
case PaymentMethodType.Card: {
this.stripeService.mountElements();
break;
}
case PaymentMethodType.PayPal: {
this.braintreeService.createDropin();
break;
}
}
}
get selected(): PaymentMethodType {
return this.formGroup.value.paymentMethod;
}
protected get usingAccountCredit(): boolean {
return this.selected === PaymentMethodType.Credit;
}
protected get usingBankAccount(): boolean {
return this.selected === PaymentMethodType.BankAccount;
}
protected get usingCard(): boolean {
return this.selected === PaymentMethodType.Card;
}
protected get usingPayPal(): boolean {
return this.selected === PaymentMethodType.PayPal;
}
private get usingStripe(): boolean {
return this.usingBankAccount || this.usingCard;
}
}

View File

@@ -7,7 +7,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SharedModule } from "../../shared";
import { SharedModule } from "../../../shared";
@Component({
selector: "app-payment",

View File

@@ -0,0 +1,18 @@
<app-callout type="warning" title="{{ 'verifyBankAccount' | i18n }}">
<p>{{ "verifyBankAccountDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }}</p>
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-form-field class="tw-mr-2 tw-w-40">
<bit-label>{{ "amountX" | i18n: "1" }}</bit-label>
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount1" />
<span bitPrefix>$0.</span>
</bit-form-field>
<bit-form-field class="tw-mr-2 tw-w-40">
<bit-label>{{ "amountX" | i18n: "2" }}</bit-label>
<input bitInput type="number" step="1" placeholder="xx" formControlName="amount2" />
<span bitPrefix>$0.</span>
</bit-form-field>
<button *ngIf="onSubmit" type="submit" bitButton bitFormButton buttonType="primary">
{{ "submit" | i18n }}
</button>
</form>
</app-callout>

View File

@@ -0,0 +1,41 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
import { SharedModule } from "../../../shared";
@Component({
selector: "app-verify-bank-account",
templateUrl: "./verify-bank-account.component.html",
standalone: true,
imports: [SharedModule],
})
export class VerifyBankAccountComponent {
@Input() onSubmit?: (request: VerifyBankAccountRequest) => Promise<void>;
@Output() submitted = new EventEmitter();
protected formGroup = this.formBuilder.group({
amount1: new FormControl<number>(null, [
Validators.required,
Validators.min(0),
Validators.max(99),
]),
amount2: new FormControl<number>(null, [
Validators.required,
Validators.min(0),
Validators.max(99),
]),
});
constructor(private formBuilder: FormBuilder) {}
submit = async () => {
const request = new VerifyBankAccountRequest(
this.formGroup.value.amount1,
this.formGroup.value.amount2,
);
await this.onSubmit(request);
this.submitted.emit();
};
}

View File

@@ -40,6 +40,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
@@ -62,6 +63,7 @@ import { WebSetPasswordJitService, WebRegistrationFinishService, WebLoginService
import { AcceptOrganizationInviteService } from "../auth/organization-invite/accept-organization.service";
import { HtmlStorageService } from "../core/html-storage.service";
import { I18nService } from "../core/i18n.service";
import { WebBiometricsService } from "../platform/web-biometric.service";
import { WebEnvironmentService } from "../platform/web-environment.service";
import { WebMigrationRunner } from "../platform/web-migration-runner";
import { WebStorageServiceProvider } from "../platform/web-storage-service.provider";
@@ -164,6 +166,11 @@ const safeProviders: SafeProvider[] = [
useClass: WebEnvironmentService,
deps: [WINDOW, StateProvider, AccountService],
}),
safeProvider({
provide: BiometricsService,
useClass: WebBiometricsService,
deps: [],
}),
safeProvider({
provide: ThemeStateService,
useFactory: (globalStateProvider: GlobalStateProvider) =>

View File

@@ -186,20 +186,6 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
throw new Error("Cannot read from clipboard on web.");
}
supportsBiometric() {
return Promise.resolve(false);
}
authenticateBiometric() {
return Promise.resolve(false);
}
biometricsNeedsSetup: () => Promise<boolean>;
biometricsSupportsAutoSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
biometricsSetup: () => Promise<void>;
supportsSecureStorage() {
return false;
}

View File

@@ -69,6 +69,7 @@ import { PreferencesComponent } from "./settings/preferences.component";
import { GeneratorComponent } from "./tools/generator.component";
import { ReportsModule } from "./tools/reports";
import { AccessComponent } from "./tools/send/access.component";
import { SendAccessExplainerComponent } from "./tools/send/send-access-explainer.component";
import { SendComponent } from "./tools/send/send.component";
import { VaultModule } from "./vault/individual-vault/vault.module";
@@ -146,11 +147,6 @@ const routes: Routes = [
canActivate: [unauthGuardFn()],
data: { titleId: "deleteAccount" } satisfies DataProperties,
},
{
path: "send/:sendId/:key",
component: AccessComponent,
data: { titleId: "Bitwarden Send" } satisfies DataProperties,
},
{
path: "update-temp-password",
component: UpdateTempPasswordComponent,
@@ -211,6 +207,24 @@ const routes: Routes = [
},
],
},
{
path: "send/:sendId/:key",
data: {
pageTitle: "viewSend",
showReadonlyHostname: true,
} satisfies DataProperties & AnonLayoutWrapperData,
children: [
{
path: "",
component: AccessComponent,
},
{
path: "",
outlet: "secondary",
component: SendAccessExplainerComponent,
},
],
},
{
path: "set-password-jit",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],

View File

@@ -0,0 +1,27 @@
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
export class WebBiometricsService extends BiometricsService {
async supportsBiometric(): Promise<boolean> {
return false;
}
async isBiometricUnlockAvailable(): Promise<boolean> {
return false;
}
async authenticateBiometric(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsNeedsSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsSupportsAutoSetup(): Promise<boolean> {
throw new Error("Method not implemented.");
}
async biometricsSetup(): Promise<void> {
throw new Error("Method not implemented.");
}
}

View File

@@ -167,7 +167,10 @@ export class PreferencesComponent implements OnInit, OnDestroy {
);
return;
}
const values = this.form.value;
// must get raw value b/c the vault timeout action is disabled when a policy is applied
// which removes the timeout action property and value from the normal form.value.
const values = this.form.getRawValue();
const activeAcct = await firstValueFrom(this.accountService.activeAccount$);

View File

@@ -1,85 +1,52 @@
<form [formGroup]="formGroup" [bitSubmit]="load">
<div
class="tw-mx-auto tw-mt-5 tw-flex tw-max-w-xl tw-flex-col tw-items-center tw-justify-center tw-p-8"
>
<img class="logo logo-themed" alt="Bitwarden" />
<div class="tw-mt-5 tw-w-full">
<h2 bitTypography="h2" class="tw-mb-4 tw-text-center">View Send</h2>
</div>
<div class="tw-w-full tw-text-center" *ngIf="creatorIdentifier != null">
<p>{{ "sendAccessCreatorIdentifier" | i18n: creatorIdentifier }}</p>
</div>
<bit-callout *ngIf="hideEmail" type="warning" title="{{ 'warning' | i18n }}">
{{ "viewSendHiddenEmailWarning" | i18n }}
<a bitLink href="https://bitwarden.com/help/receive-send/" target="_blank" rel="noreferrer">{{
"learnMore" | i18n
}}</a
>.
</bit-callout>
<div
class="tw-mt-3 tw-w-10/12 tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-6"
>
<ng-container *ngIf="!loading; else spinner">
<app-send-access-password
(setPasswordEvent)="setPassword($event)"
*ngIf="passwordRequired && !error"
></app-send-access-password>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="unavailable">
<ng-container slot="description">{{ "sendAccessUnavailable" | i18n }}</ng-container>
</bit-no-items>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="error">
<ng-container slot="description">{{ "unexpectedErrorSend" | i18n }}</ng-container>
</bit-no-items>
<div *ngIf="!passwordRequired && send && !error && !unavailable">
<p class="tw-text-center">
<b>{{ send.name }}</b>
</p>
<hr />
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-send-access-text [send]="send"></app-send-access-text>
</ng-container>
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<app-send-access-file
[send]="send"
[decKey]="decKey"
[accessRequest]="accessRequest"
></app-send-access-file>
</ng-container>
<p *ngIf="expirationDate" class="tw-text-center tw-text-muted">
Expires: {{ expirationDate | date: "medium" }}
</p>
</div>
<bit-callout *ngIf="hideEmail" type="warning" title="{{ 'warning' | i18n }}">
{{ "viewSendHiddenEmailWarning" | i18n }}
<a bitLink href="https://bitwarden.com/help/receive-send/" target="_blank" rel="noreferrer">{{
"learnMore" | i18n
}}</a
>.
</bit-callout>
<ng-container *ngIf="!loading; else spinner">
<app-send-access-password
(setPasswordEvent)="setPassword($event)"
*ngIf="passwordRequired && !error"
></app-send-access-password>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="unavailable">
<ng-container slot="description">{{ "sendAccessUnavailable" | i18n }}</ng-container>
</bit-no-items>
<bit-no-items [icon]="expiredSendIcon" class="tw-text-main" *ngIf="error">
<ng-container slot="description">{{ "unexpectedErrorSend" | i18n }}</ng-container>
</bit-no-items>
<div *ngIf="!passwordRequired && send && !error && !unavailable">
<p class="tw-text-center">
<b>{{ send.name }}</b>
</p>
<hr />
<!-- Text -->
<ng-container *ngIf="send.type === sendType.Text">
<app-send-access-text [send]="send"></app-send-access-text>
</ng-container>
<ng-template #spinner>
<div class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
</ng-template>
</div>
<div class="tw-mt-5 tw-w-10/12 tw-text-center tw-text-muted">
<p bitTypography="body2" class="tw-mb-0">
{{ "sendAccessTaglineProductDesc" | i18n }}
{{ "sendAccessTaglineLearnMore" | i18n }}
<a
bitLink
href="https://www.bitwarden.com/products/send?source=web-vault"
target="_blank"
rel="noreferrer"
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a bitLink [routerLink]="registerRoute$ | async" target="_blank" rel="noreferrer">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}
<!-- File -->
<ng-container *ngIf="send.type === sendType.File">
<app-send-access-file
[send]="send"
[decKey]="decKey"
[accessRequest]="accessRequest"
></app-send-access-file>
</ng-container>
<p *ngIf="expirationDate" class="tw-text-center tw-text-muted">
Expires: {{ expirationDate | date: "medium" }}
</p>
</div>
</div>
</ng-container>
<ng-template #spinner>
<div class="tw-text-center">
<i
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</div>
</ng-template>
</form>

View File

@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { AnonLayoutWrapperDataService } from "@bitwarden/auth/angular";
import { RegisterRouteService } from "@bitwarden/auth/common";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -70,6 +71,7 @@ export class AccessComponent implements OnInit {
private i18nService: I18nService,
private configService: ConfigService,
private registerRouteService: RegisterRouteService,
private layoutWrapperDataService: AnonLayoutWrapperDataService,
protected formBuilder: FormBuilder,
) {}
@@ -151,6 +153,15 @@ export class AccessComponent implements OnInit {
!this.passwordRequired &&
!this.loading &&
!this.unavailable;
if (this.creatorIdentifier != null) {
this.layoutWrapperDataService.setAnonLayoutWrapperData({
pageSubtitle: {
subtitle: this.i18nService.t("sendAccessCreatorIdentifier", this.creatorIdentifier),
translate: false,
},
});
}
};
protected setPassword(password: string) {

View File

@@ -0,0 +1,18 @@
<div class="tw-text-center tw-text-muted">
<p bitTypography="body2" class="tw-mb-0">
{{ "sendAccessTaglineProductDesc" | i18n }}
{{ "sendAccessTaglineLearnMore" | i18n }}
<a
bitLink
href="https://www.bitwarden.com/products/send?source=web-vault"
target="_blank"
rel="noreferrer"
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a bitLink [routerLink]="registerRoute$ | async" target="_blank" rel="noreferrer">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}
</p>
</div>

View File

@@ -0,0 +1,17 @@
import { Component } from "@angular/core";
import { RegisterRouteService } from "@bitwarden/auth/common";
import { SharedModule } from "../../shared";
@Component({
selector: "app-send-access-explainer",
templateUrl: "send-access-explainer.component.html",
standalone: true,
imports: [SharedModule],
})
export class SendAccessExplainerComponent {
// TODO: remove when email verification flag is removed
registerRoute$ = this.registerRouteService.registerRoute$();
constructor(private registerRouteService: RegisterRouteService) {}
}

View File

@@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
@Component({
selector: "app-vault-collections",
@@ -29,6 +29,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
accountService: AccountService,
protected dialogRef: DialogRef,
@Inject(DIALOG_DATA) params: CollectionsDialogParams,
toastService: ToastService,
) {
super(
collectionService,
@@ -39,6 +40,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
logService,
configService,
accountService,
toastService,
);
this.cipherId = params?.cipherId;
}

View File

@@ -15,7 +15,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 { SyncService } from "@bitwarden/common/platform/sync";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import { OrganizationUserResetPasswordService } from "../../../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { EnrollMasterPasswordReset } from "../../../../admin-console/organizations/users/enroll-master-password-reset.component";
@@ -50,6 +50,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
private dialogService: DialogService,
private resetPasswordService: OrganizationUserResetPasswordService,
private userVerificationService: UserVerificationService,
private toastService: ToastService,
) {}
async ngOnInit() {
@@ -158,6 +159,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
this.syncService,
this.logService,
this.userVerificationService,
this.toastService,
);
} else {
// Remove reset password

View File

@@ -718,12 +718,12 @@ export class VaultComponent implements OnInit, OnDestroy {
const result: ViewCipherDialogCloseResult = await lastValueFrom(dialogRef.closed);
// If the dialog was closed by deleting the cipher, refresh the vault.
if (result.action === ViewCipherDialogResult.deleted) {
if (result?.action === ViewCipherDialogResult.deleted) {
this.refresh();
}
// If the dialog was closed by any other action (close button, escape key, etc), navigate back to the vault.
if (!result.action) {
if (!result?.action) {
this.go({ cipherId: null, itemId: null, action: null });
}
}

View File

@@ -15,7 +15,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherCollectionsRequest } from "@bitwarden/common/vault/models/request/cipher-collections.request";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";
import {
CollectionsComponent as BaseCollectionsComponent,
@@ -41,6 +41,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
accountService: AccountService,
protected dialogRef: DialogRef,
@Inject(DIALOG_DATA) params: OrgVaultCollectionsDialogParams,
toastService: ToastService,
) {
super(
collectionService,
@@ -53,6 +54,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
accountService,
dialogRef,
params,
toastService,
);
this.allowSelectNone = true;
this.collectionIds = params?.collectionIds;

View File

@@ -113,10 +113,32 @@
{{ "new" | i18n }}<i class="bwi bwi-angle-down tw-ml-2" aria-hidden="true"></i>
</button>
<bit-menu #addOptions aria-labelledby="newItemDropdown">
<button type="button" bitMenuItem (click)="addCipher()">
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
{{ "item" | i18n }}
</button>
<ng-container [ngSwitch]="extensionRefreshEnabled">
<ng-container *ngSwitchCase="true">
<button type="button" bitMenuItem (click)="addCipher(CipherType.Login)">
<i class="bwi bwi-globe" slot="start" aria-hidden="true"></i>
{{ "typeLogin" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Card)">
<i class="bwi bwi-credit-card" slot="start" aria-hidden="true"></i>
{{ "typeCard" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Identity)">
<i class="bwi bwi-id-card" slot="start" aria-hidden="true"></i>
{{ "typeIdentity" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.SecureNote)">
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
{{ "note" | i18n }}
</button>
</ng-container>
<ng-container *ngSwitchCase="false">
<button type="button" bitMenuItem (click)="addCipher()">
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
{{ "item" | i18n }}
</button>
</ng-container>
</ng-container>
<button type="button" bitMenuItem (click)="addCollection()">
<i class="bwi bwi-fw bwi-collection" aria-hidden="true"></i>
{{ "collection" | i18n }}

View File

@@ -10,6 +10,7 @@ import { ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import {
DialogService,
@@ -67,7 +68,7 @@ export class VaultHeaderComponent implements OnInit {
@Input() searchText: string;
/** Emits an event when the new item button is clicked in the header */
@Output() onAddCipher = new EventEmitter<void>();
@Output() onAddCipher = new EventEmitter<CipherType | undefined>();
/** Emits an event when the new collection button is clicked in the header */
@Output() onAddCollection = new EventEmitter<void>();
@@ -89,6 +90,14 @@ export class VaultHeaderComponent implements OnInit {
protected restrictProviderAccessFlag = false;
/**
* Whether the extension refresh feature flag is enabled.
*/
protected extensionRefreshEnabled = false;
/** The cipher type enum. */
protected CipherType = CipherType;
constructor(
private organizationService: OrganizationService,
private i18nService: I18nService,
@@ -102,6 +111,9 @@ export class VaultHeaderComponent implements OnInit {
this.restrictProviderAccessFlag = await this.configService.getFeatureFlag(
FeatureFlag.RestrictProviderAccess,
);
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
FeatureFlag.ExtensionRefresh,
);
}
get title() {
@@ -194,8 +206,8 @@ export class VaultHeaderComponent implements OnInit {
return this.collection.node.canEdit(this.organization);
}
addCipher() {
this.onAddCipher.emit();
addCipher(cipherType?: CipherType) {
this.onAddCipher.emit(cipherType);
}
async addCollection() {

View File

@@ -4,7 +4,7 @@
[organization]="organization"
[collection]="selectedCollection"
[searchText]="currentSearchText$ | async"
(onAddCipher)="addCipher()"
(onAddCipher)="addCipher($event)"
(onAddCollection)="addCollection()"
(onEditCollection)="editCollection(selectedCollection.node, $event.tab, $event.readonly)"
(onDeleteCollection)="deleteCollection(selectedCollection.node)"

View File

@@ -54,6 +54,7 @@ import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -786,14 +787,14 @@ export class VaultComponent implements OnInit, OnDestroy {
}
}
async addCipher() {
async addCipher(cipherType?: CipherType) {
let collections: CollectionView[] = [];
// Admins limited to only adding items to collections they have access to.
collections = await firstValueFrom(this.editableCollections$);
await this.editCipher(null, (comp) => {
comp.type = this.activeFilter.cipherType;
comp.type = cipherType || this.activeFilter.cipherType;
comp.collections = collections;
if (this.activeFilter.collectionId) {
comp.collectionIds = [this.activeFilter.collectionId];