mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 05:53:42 +00:00
Merge branch 'km/fix-bio' into km/fix-autoprompt
This commit is contained in:
@@ -257,7 +257,7 @@ export class NativeMessagingBackground {
|
||||
message.command == BiometricsCommands.Unlock ||
|
||||
message.command == BiometricsCommands.IsAvailable
|
||||
) {
|
||||
// TODO remove after 2025.01
|
||||
// TODO remove after 2025.3
|
||||
// wait until there is no other callbacks, or timeout
|
||||
const call = await firstValueFrom(
|
||||
race(
|
||||
|
||||
@@ -54,7 +54,15 @@ export class ExtensionLockComponentService implements LockComponentService {
|
||||
if (!(await firstValueFrom(this.biometricStateService.biometricUnlockEnabled$))) {
|
||||
return BiometricsStatus.NotEnabledLocally;
|
||||
} else {
|
||||
return await this.biometricsService.getBiometricsStatusForUser(userId);
|
||||
// TODO remove after 2025.3
|
||||
// remove after backward compatibility code for old biometrics ipc protocol is removed
|
||||
const result: BiometricsStatus = (await Promise.race([
|
||||
this.biometricsService.getBiometricsStatusForUser(userId),
|
||||
new Promise((resolve) =>
|
||||
setTimeout(() => resolve(BiometricsStatus.DesktopDisconnected), 1000),
|
||||
),
|
||||
])) as BiometricsStatus;
|
||||
return result;
|
||||
}
|
||||
}),
|
||||
this.userDecryptionOptionsService.userDecryptionOptionsById$(userId),
|
||||
|
||||
@@ -8,7 +8,6 @@ command: bitwarden.sh
|
||||
finish-args:
|
||||
- --share=ipc
|
||||
- --share=network
|
||||
- --socket=wayland
|
||||
- --socket=x11
|
||||
- --device=dri
|
||||
- --env=XDG_CURRENT_DESKTOP=Unity
|
||||
@@ -43,5 +42,4 @@ modules:
|
||||
commands:
|
||||
- ulimit -c 0
|
||||
- export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
|
||||
- exec zypak-wrapper /app/bin/bitwarden-app --ozone-platform-hint=auto
|
||||
--enable-features=WaylandWindowDecorations "$@"
|
||||
- exec zypak-wrapper /app/bin/bitwarden-app "$@"
|
||||
|
||||
@@ -330,6 +330,20 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "upgradeOrganization": {
|
||||
const upgradeConfirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "upgradeOrganization" },
|
||||
content: { key: "upgradeOrganizationDesc" },
|
||||
acceptButtonText: { key: "learnMore" },
|
||||
type: "info",
|
||||
});
|
||||
if (upgradeConfirmed) {
|
||||
this.platformUtilsService.launchUri(
|
||||
"https://bitwarden.com/help/upgrade-from-individual-to-org/",
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "emailVerificationRequired": {
|
||||
const emailVerificationConfirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "emailVerificationRequired" },
|
||||
|
||||
@@ -3474,5 +3474,14 @@
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
},
|
||||
"organizationUpgradeRequired": {
|
||||
"message": "Organization upgrade required"
|
||||
},
|
||||
"upgradeOrganization": {
|
||||
"message": "Upgrade organization"
|
||||
},
|
||||
"upgradeOrganizationDesc": {
|
||||
"message": "This feature is not available for free organizations. Switch to a paid plan to unlock more features."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ export class BiometricMessageHandlerService {
|
||||
appId,
|
||||
);
|
||||
}
|
||||
// TODO: legacy, remove after 2025.01
|
||||
// TODO: legacy, remove after 2025.3
|
||||
case BiometricsCommands.IsAvailable: {
|
||||
const available =
|
||||
(await this.biometricsService.getBiometricsStatus()) == BiometricsStatus.Available;
|
||||
@@ -200,7 +200,7 @@ export class BiometricMessageHandlerService {
|
||||
appId,
|
||||
);
|
||||
}
|
||||
// TODO: legacy, remove after 2025.01
|
||||
// TODO: legacy, remove after 2025.3
|
||||
case BiometricsCommands.Unlock: {
|
||||
const isTemporarilyDisabled =
|
||||
(await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) &&
|
||||
|
||||
@@ -186,6 +186,16 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex totp" *ngIf="showUpgradeRequiredTotp">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
|
||||
<span class="row-label">
|
||||
<a [routerLink]="" (click)="upgradeOrganization()"
|
||||
>{{ "organizationUpgradeRequired" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.card">
|
||||
|
||||
@@ -157,4 +157,10 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
|
||||
upgradeOrganization() {
|
||||
this.messagingService.send("upgradeOrganization", {
|
||||
organizationId: this.cipher.organizationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
protected messagingService: MessagingService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
protected policyService: PolicyService,
|
||||
organizationService: OrganizationService,
|
||||
protected organizationService: OrganizationService,
|
||||
logService: LogService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
dialogService: DialogService,
|
||||
@@ -307,7 +307,8 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
this.cipher.type === CipherType.Login &&
|
||||
this.cipher.login.totp &&
|
||||
this.organization?.productTierType != ProductTierType.Free &&
|
||||
(this.cipher.organizationUseTotp || this.canAccessPremium)
|
||||
((this.canAccessPremium && this.cipher.organizationId == null) ||
|
||||
this.cipher.organizationUseTotp)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ import { KeyService } from "@bitwarden/key-management";
|
||||
templateUrl: "setup.component.html",
|
||||
})
|
||||
export class SetupComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(ManageTaxInformationComponent)
|
||||
manageTaxInformationComponent: ManageTaxInformationComponent;
|
||||
@ViewChild(ManageTaxInformationComponent) taxInformationComponent: ManageTaxInformationComponent;
|
||||
|
||||
loading = true;
|
||||
providerId: string;
|
||||
@@ -111,7 +110,7 @@ export class SetupComponent implements OnInit, OnDestroy {
|
||||
try {
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (!this.manageTaxInformationComponent.validate() || !this.formGroup.valid) {
|
||||
if (!this.taxInformationComponent.validate() || !this.formGroup.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ export class SetupComponent implements OnInit, OnDestroy {
|
||||
request.key = key;
|
||||
|
||||
request.taxInfo = new ExpandedTaxInfoUpdateRequest();
|
||||
const taxInformation = this.manageTaxInformationComponent.getTaxInformation();
|
||||
const taxInformation = this.taxInformationComponent.getTaxInformation();
|
||||
|
||||
request.taxInfo.country = taxInformation.country;
|
||||
request.taxInfo.postalCode = taxInformation.postalCode;
|
||||
@@ -147,6 +146,7 @@ export class SetupComponent implements OnInit, OnDestroy {
|
||||
|
||||
await this.router.navigate(["/providers", provider.id]);
|
||||
} catch (e) {
|
||||
e.message = this.i18nService.translate(e.message) || e.message;
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { BannerModule } from "@bitwarden/components";
|
||||
import { OnboardingModule } from "@bitwarden/web-vault/app/shared/components/onboarding/onboarding.module";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { OnboardingModule } from "../../../../../../apps/web/src/app/shared/components/onboarding/onboarding.module";
|
||||
import { SecretsManagerSharedModule } from "../shared/sm-shared.module";
|
||||
|
||||
import { OverviewRoutingModule } from "./overview-routing.module";
|
||||
|
||||
@@ -8,10 +8,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { RouterService } from "@bitwarden/web-vault/app/core";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { RouterService } from "../../../../../../../apps/web/src/app/core/router.service";
|
||||
import { ProjectView } from "../../models/view/project.view";
|
||||
import { ProjectService } from "../project.service";
|
||||
|
||||
|
||||
@@ -8,10 +8,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { RouterService } from "@bitwarden/web-vault/app/core";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { RouterService } from "../../../../../../../../clients/apps/web/src/app/core/router.service";
|
||||
import { ServiceAccountView } from "../../models/view/service-account.view";
|
||||
import { ServiceAccountService } from "../service-account.service";
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
protected policyService: PolicyService,
|
||||
protected logService: LogService,
|
||||
protected passwordRepromptService: PasswordRepromptService,
|
||||
private organizationService: OrganizationService,
|
||||
protected organizationService: OrganizationService,
|
||||
protected dialogService: DialogService,
|
||||
protected win: Window,
|
||||
protected datePipe: DatePipe,
|
||||
|
||||
@@ -65,6 +65,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
showPrivateKey: boolean;
|
||||
canAccessPremium: boolean;
|
||||
showPremiumRequiredTotp: boolean;
|
||||
showUpgradeRequiredTotp: boolean;
|
||||
totpCode: string;
|
||||
totpCodeFormatted: string;
|
||||
totpDash: number;
|
||||
@@ -151,22 +152,25 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$(activeUserId),
|
||||
);
|
||||
this.showPremiumRequiredTotp =
|
||||
this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationUseTotp;
|
||||
this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationId;
|
||||
this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [
|
||||
this.collectionId as CollectionId,
|
||||
]);
|
||||
|
||||
this.showUpgradeRequiredTotp =
|
||||
this.cipher.login.totp && this.cipher.organizationId && !this.cipher.organizationUseTotp;
|
||||
|
||||
if (this.cipher.folderId) {
|
||||
this.folder = await (
|
||||
await firstValueFrom(this.folderService.folderViews$(activeUserId))
|
||||
).find((f) => f.id == this.cipher.folderId);
|
||||
}
|
||||
|
||||
if (
|
||||
this.cipher.type === CipherType.Login &&
|
||||
this.cipher.login.totp &&
|
||||
(cipher.organizationUseTotp || this.canAccessPremium)
|
||||
) {
|
||||
const canGenerateTotp = this.cipher.organizationId
|
||||
? this.cipher.organizationUseTotp
|
||||
: this.canAccessPremium;
|
||||
|
||||
if (this.cipher.type === CipherType.Login && this.cipher.login.totp && canGenerateTotp) {
|
||||
await this.totpUpdateCode();
|
||||
const interval = this.totpService.getTimeInterval(this.cipher.login.totp);
|
||||
await this.totpTick(interval);
|
||||
|
||||
@@ -395,6 +395,11 @@
|
||||
Open the FullClient, go to the Main Menu and select Export. Start the export passwords
|
||||
wizard and follow the instructions to export a CSV file.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="format === 'nordpasscsv'">
|
||||
Log in to NordPass and open Settings → Scroll down to the Import and Export section
|
||||
and select Export items → Enter your Master Password and select Continue. → Save
|
||||
the CSV file on your device.
|
||||
</ng-container>
|
||||
</bit-callout>
|
||||
<import-lastpass
|
||||
*ngIf="showLastPassOptions"
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
<bit-label [appTextDrag]="totpCodeCopyObj?.totpCode"
|
||||
>{{ "verificationCodeTotp" | i18n }}
|
||||
<span
|
||||
*ngIf="!(isPremium$ | async)"
|
||||
*ngIf="!(allowTotpGeneration$ | async)"
|
||||
bitBadge
|
||||
variant="success"
|
||||
class="tw-ml-2 tw-cursor-pointer"
|
||||
@@ -130,14 +130,14 @@
|
||||
id="totp"
|
||||
readonly
|
||||
bitInput
|
||||
[type]="!(isPremium$ | async) ? 'password' : 'text'"
|
||||
[type]="!(allowTotpGeneration$ | async) ? 'password' : 'text'"
|
||||
[value]="totpCodeCopyObj?.totpCodeFormatted || '*** ***'"
|
||||
aria-readonly="true"
|
||||
data-testid="login-totp"
|
||||
class="tw-font-mono"
|
||||
/>
|
||||
<div
|
||||
*ngIf="isPremium$ | async"
|
||||
*ngIf="allowTotpGeneration$ | async"
|
||||
bitTotpCountdown
|
||||
[cipher]="cipher"
|
||||
bitSuffix
|
||||
@@ -152,7 +152,7 @@
|
||||
showToast
|
||||
[appA11yTitle]="'copyVerificationCode' | i18n"
|
||||
data-testid="copy-totp"
|
||||
[disabled]="!(isPremium$ | async)"
|
||||
[disabled]="!(allowTotpGeneration$ | async)"
|
||||
class="disabled:tw-cursor-default"
|
||||
></button>
|
||||
</bit-form-field>
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
// @ts-strict-ignore
|
||||
import { CommonModule, DatePipe } from "@angular/common";
|
||||
import { Component, inject, Input } from "@angular/core";
|
||||
import { Observable, switchMap } from "rxjs";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
filter,
|
||||
map,
|
||||
Observable,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
} from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
@@ -12,13 +20,13 @@ import { EventType } from "@bitwarden/common/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import {
|
||||
BadgeModule,
|
||||
ColorPasswordModule,
|
||||
FormFieldModule,
|
||||
IconButtonModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
TypographyModule,
|
||||
IconButtonModule,
|
||||
BadgeModule,
|
||||
ColorPasswordModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
@@ -51,13 +59,31 @@ type TotpCodeValues = {
|
||||
],
|
||||
})
|
||||
export class LoginCredentialsViewComponent {
|
||||
@Input() cipher: CipherView;
|
||||
@Input()
|
||||
get cipher(): CipherView {
|
||||
return this._cipher$.value;
|
||||
}
|
||||
set cipher(value: CipherView) {
|
||||
this._cipher$.next(value);
|
||||
}
|
||||
private _cipher$ = new BehaviorSubject<CipherView>(null);
|
||||
|
||||
isPremium$: Observable<boolean> = this.accountService.activeAccount$.pipe(
|
||||
private _userHasPremium$: Observable<boolean> = this.accountService.activeAccount$.pipe(
|
||||
switchMap((account) =>
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id),
|
||||
),
|
||||
);
|
||||
|
||||
allowTotpGeneration$: Observable<boolean> = combineLatest([
|
||||
this._userHasPremium$,
|
||||
this._cipher$.pipe(filter((c) => c != null)),
|
||||
]).pipe(
|
||||
map(([userHasPremium, cipher]) => {
|
||||
// User premium status only applies to personal ciphers, organizationUseTotp applies to organization ciphers
|
||||
return (userHasPremium && cipher.organizationId == null) || cipher.organizationUseTotp;
|
||||
}),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
showPasswordCount: boolean = false;
|
||||
passwordRevealed: boolean = false;
|
||||
totpCodeCopyObj: TotpCodeValues;
|
||||
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -83,7 +83,6 @@
|
||||
"@angular-eslint/template-parser": "18.4.3",
|
||||
"@angular/cli": "18.2.12",
|
||||
"@angular/compiler-cli": "18.2.13",
|
||||
"@angular/elements": "18.2.13",
|
||||
"@babel/core": "7.24.9",
|
||||
"@babel/preset-env": "7.24.8",
|
||||
"@compodoc/compodoc": "1.1.26",
|
||||
@@ -2287,22 +2286,6 @@
|
||||
"zone.js": "~0.14.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/elements": {
|
||||
"version": "18.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@angular/elements/-/elements-18.2.13.tgz",
|
||||
"integrity": "sha512-yahRkXWgFolpWMeVsTIlWYwoq4Bsz6wrfS4b+TKHIZbTCyRUlJ5zBFecpYMwgmVuQDJZp+WkUWZV2Qg7kLJR5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "18.2.13",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/forms": {
|
||||
"version": "18.2.13",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.13.tgz",
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
"@angular-eslint/template-parser": "18.4.3",
|
||||
"@angular/cli": "18.2.12",
|
||||
"@angular/compiler-cli": "18.2.13",
|
||||
"@angular/elements": "18.2.13",
|
||||
"@babel/core": "7.24.9",
|
||||
"@babel/preset-env": "7.24.8",
|
||||
"@compodoc/compodoc": "1.1.26",
|
||||
|
||||
Reference in New Issue
Block a user