mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-25 12:15:18 +01:00
Merge branch 'main' into vault/pm-5273
# Conflicts: # libs/common/src/platform/abstractions/state.service.ts # libs/common/src/platform/services/state.service.ts # libs/common/src/state-migrations/migrate.ts
This commit is contained in:
commit
8a1df6671a
72
.github/workflows/scan.yml
vendored
Normal file
72
.github/workflows/scan.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
name: Scan
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "rc"
|
||||
- "hotfix-rc"
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
check-run:
|
||||
name: Check PR run
|
||||
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||
|
||||
sast:
|
||||
name: SAST scan
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-run
|
||||
permissions:
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Scan with Checkmarx
|
||||
uses: checkmarx/ast-github-action@749fec53e0db0f6404a97e2e0807c3e80e3583a7 #2.0.23
|
||||
env:
|
||||
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
|
||||
with:
|
||||
project_name: ${{ github.repository }}
|
||||
cx_tenant: ${{ secrets.CHECKMARX_TENANT }}
|
||||
base_uri: https://ast.checkmarx.net/
|
||||
cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }}
|
||||
cx_client_secret: ${{ secrets.CHECKMARX_SECRET }}
|
||||
additional_params: --report-format sarif --output-path . ${{ env.INCREMENTAL }}
|
||||
|
||||
- name: Upload Checkmarx results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
|
||||
with:
|
||||
sarif_file: cx_result.sarif
|
||||
|
||||
quality:
|
||||
name: Quality scan
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-run
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Scan with SonarCloud
|
||||
uses: sonarsource/sonarcloud-github-action@49e6cd3b187936a73b8280d59ffd9da69df63ec9 # v2.1.1
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: >
|
||||
-Dsonar.organization=${{ github.repository_owner }}
|
||||
-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}
|
||||
-Dsonar.test.inclusions=**/*.spec.ts
|
||||
-Dsonar.tests=.
|
@ -119,7 +119,7 @@
|
||||
"message": "Дадаць лагін"
|
||||
},
|
||||
"addCardMenu": {
|
||||
"message": "Add card"
|
||||
"message": "Дадаць картку"
|
||||
},
|
||||
"addIdentityMenu": {
|
||||
"message": "Add identity"
|
||||
@ -269,7 +269,7 @@
|
||||
"message": "Даўжыня"
|
||||
},
|
||||
"passwordMinLength": {
|
||||
"message": "Minimum password length"
|
||||
"message": "Мінімальная даўжыня пароля"
|
||||
},
|
||||
"uppercase": {
|
||||
"message": "Вялікія літары (A-Z)"
|
||||
@ -369,7 +369,7 @@
|
||||
"message": "Iншае"
|
||||
},
|
||||
"unlockMethodNeededToChangeTimeoutActionDesc": {
|
||||
"message": "Set up an unlock method to change your vault timeout action."
|
||||
"message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча."
|
||||
},
|
||||
"unlockMethodNeeded": {
|
||||
"message": "Set up an unlock method in Settings"
|
||||
@ -415,7 +415,7 @@
|
||||
"message": "Заблакіраваць зараз"
|
||||
},
|
||||
"lockAll": {
|
||||
"message": "Lock all"
|
||||
"message": "Заблакаваць усе"
|
||||
},
|
||||
"immediately": {
|
||||
"message": "Адразу"
|
||||
@ -494,7 +494,7 @@
|
||||
"message": "Ваш уліковы запіс створаны! Цяпер вы можаце ўвайсці ў яго."
|
||||
},
|
||||
"youSuccessfullyLoggedIn": {
|
||||
"message": "You successfully logged in"
|
||||
"message": "Вы паспяхова аўтарызаваны"
|
||||
},
|
||||
"youMayCloseThisWindow": {
|
||||
"message": "You may close this window"
|
||||
@ -2005,7 +2005,7 @@
|
||||
"message": "Выбраць папку..."
|
||||
},
|
||||
"noFoldersFound": {
|
||||
"message": "No folders found",
|
||||
"message": "Папкі не знойдзены",
|
||||
"description": "Used as a message within the notification bar when no folders are found"
|
||||
},
|
||||
"orgPermissionsUpdatedMustSetPassword": {
|
||||
@ -2215,7 +2215,7 @@
|
||||
"message": "Версія сервера"
|
||||
},
|
||||
"selfHostedServer": {
|
||||
"message": "self-hosted"
|
||||
"message": "уласнае размяшчэнне"
|
||||
},
|
||||
"thirdParty": {
|
||||
"message": "Іншы пастаўшчык"
|
||||
@ -2356,25 +2356,25 @@
|
||||
}
|
||||
},
|
||||
"loggingInOn": {
|
||||
"message": "Logging in on"
|
||||
"message": "Увайсці на"
|
||||
},
|
||||
"opensInANewWindow": {
|
||||
"message": "Адкрываць у новым акне"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Device approval required. Select an approval option below:"
|
||||
"message": "Патрабуецца ўхваленне прылады. Выберыце параметры ўхвалення ніжэй:"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Remember this device"
|
||||
"message": "Запомніць гэту прыладу"
|
||||
},
|
||||
"uncheckIfPublicDevice": {
|
||||
"message": "Uncheck if using a public device"
|
||||
"message": "Здыміце пазнаку, калі выкарыстоўваеце агульнадаступную прыладу"
|
||||
},
|
||||
"approveFromYourOtherDevice": {
|
||||
"message": "Approve from your other device"
|
||||
"message": "Ухваліць з іншай вашай прылады"
|
||||
},
|
||||
"requestAdminApproval": {
|
||||
"message": "Request admin approval"
|
||||
"message": "Запытаць ухваленне адміністратара"
|
||||
},
|
||||
"approveWithMasterPassword": {
|
||||
"message": "Approve with master password"
|
||||
@ -2402,7 +2402,7 @@
|
||||
"message": "Адлюстраванне"
|
||||
},
|
||||
"accountSuccessfullyCreated": {
|
||||
"message": "Account successfully created!"
|
||||
"message": "Уліковы запіс паспяхова створаны!"
|
||||
},
|
||||
"adminApprovalRequested": {
|
||||
"message": "Admin approval requested"
|
||||
|
@ -688,7 +688,7 @@
|
||||
"message": "A bejelentkezési jelszó frissítésének kérése, ha változást lett érzékelve egy webhelyen. Minden bejelentkezett fiókra vonatkozik."
|
||||
},
|
||||
"enableUsePasskeys": {
|
||||
"message": "Kérés a jhozzáférési kulcs mentésére és használatára"
|
||||
"message": "Kérés a hozzáférési kulcs mentésére és használatára"
|
||||
},
|
||||
"usePasskeysDesc": {
|
||||
"message": "Kérés az új hozzáféréi kulcsok mentésére vagy bejelentkezés a széfben tárolt hozzáférési kulcsokkal. Minden bejelentkezett fiókra vonatkozik."
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
||||
|
||||
import {
|
||||
FactoryOptions,
|
||||
@ -6,11 +7,7 @@ import {
|
||||
factory,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import { stateProviderFactory } from "../../../platform/background/service-factories/state-provider.factory";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
import { BrowserOrganizationService } from "../../services/browser-organization.service";
|
||||
import { StateServiceInitOptions } from "../../../platform/background/service-factories/state-service.factory";
|
||||
|
||||
type OrganizationServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
@ -25,10 +22,6 @@ export function organizationServiceFactory(
|
||||
cache,
|
||||
"organizationService",
|
||||
opts,
|
||||
async () =>
|
||||
new BrowserOrganizationService(
|
||||
await stateServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
),
|
||||
async () => new OrganizationService(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
||||
|
||||
import { browserSession, sessionSync } from "../../platform/decorators/session-sync-observable";
|
||||
|
||||
@browserSession
|
||||
export class BrowserOrganizationService extends OrganizationService {
|
||||
@sessionSync({ initializer: Organization.fromJSON, initializeAs: "array" })
|
||||
protected _organizations: BehaviorSubject<Organization[]>;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
|
||||
import {
|
||||
ApiServiceInitOptions,
|
||||
apiServiceFactory,
|
||||
} from "../../../platform/background/service-factories/api-service.factory";
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
FactoryOptions,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type AvatarServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type AvatarServiceInitOptions = AvatarServiceFactoryOptions &
|
||||
ApiServiceInitOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function avatarServiceFactory(
|
||||
cache: { avatarService?: AvatarServiceAbstraction } & CachedServices,
|
||||
opts: AvatarServiceInitOptions,
|
||||
): Promise<AvatarServiceAbstraction> {
|
||||
return factory(
|
||||
cache,
|
||||
"avatarService",
|
||||
opts,
|
||||
async () =>
|
||||
new AvatarService(
|
||||
await apiServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
@ -9,6 +9,7 @@ import {
|
||||
ApiServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/api-service.factory";
|
||||
import { appIdServiceFactory } from "../../../platform/background/service-factories/app-id-service.factory";
|
||||
import { billingAccountProfileStateServiceFactory } from "../../../platform/background/service-factories/billing-account-profile-state-service.factory";
|
||||
import {
|
||||
CryptoServiceInitOptions,
|
||||
cryptoServiceFactory,
|
||||
@ -119,6 +120,7 @@ export function loginStrategyServiceFactory(
|
||||
await deviceTrustCryptoServiceFactory(cache, opts),
|
||||
await authRequestServiceFactory(cache, opts),
|
||||
await globalStateProviderFactory(cache, opts),
|
||||
await billingAccountProfileStateServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -7,13 +7,29 @@ import {
|
||||
factory,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
GlobalStateProviderInitOptions,
|
||||
globalStateProviderFactory,
|
||||
} from "../../../platform/background/service-factories/global-state-provider.factory";
|
||||
import {
|
||||
PlatformUtilsServiceInitOptions,
|
||||
platformUtilsServiceFactory,
|
||||
} from "../../../platform/background/service-factories/platform-utils-service.factory";
|
||||
import {
|
||||
SingleUserStateProviderInitOptions,
|
||||
singleUserStateProviderFactory,
|
||||
} from "../../../platform/background/service-factories/single-user-state-provider.factory";
|
||||
import {
|
||||
SecureStorageServiceInitOptions,
|
||||
secureStorageServiceFactory,
|
||||
} from "../../../platform/background/service-factories/storage-service.factory";
|
||||
|
||||
type TokenServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type TokenServiceInitOptions = TokenServiceFactoryOptions & StateServiceInitOptions;
|
||||
export type TokenServiceInitOptions = TokenServiceFactoryOptions &
|
||||
SingleUserStateProviderInitOptions &
|
||||
GlobalStateProviderInitOptions &
|
||||
PlatformUtilsServiceInitOptions &
|
||||
SecureStorageServiceInitOptions;
|
||||
|
||||
export function tokenServiceFactory(
|
||||
cache: { tokenService?: AbstractTokenService } & CachedServices,
|
||||
@ -23,6 +39,12 @@ export function tokenServiceFactory(
|
||||
cache,
|
||||
"tokenService",
|
||||
opts,
|
||||
async () => new TokenService(await stateServiceFactory(cache, opts)),
|
||||
async () =>
|
||||
new TokenService(
|
||||
await singleUserStateProviderFactory(cache, opts),
|
||||
await globalStateProviderFactory(cache, opts),
|
||||
(await platformUtilsServiceFactory(cache, opts)).supportsSecureStorage(),
|
||||
await secureStorageServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,32 +1,61 @@
|
||||
import { Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Observable, combineLatest, switchMap } from "rxjs";
|
||||
|
||||
import { CurrentAccountService } from "./services/current-account.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export type CurrentAccount = {
|
||||
id: UserId;
|
||||
name: string | undefined;
|
||||
email: string;
|
||||
status: AuthenticationStatus;
|
||||
avatarColor: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-current-account",
|
||||
templateUrl: "current-account.component.html",
|
||||
})
|
||||
export class CurrentAccountComponent {
|
||||
currentAccount$: Observable<CurrentAccount>;
|
||||
|
||||
constructor(
|
||||
private currentAccountService: CurrentAccountService,
|
||||
private accountService: AccountService,
|
||||
private avatarService: AvatarService,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
private route: ActivatedRoute,
|
||||
) {}
|
||||
) {
|
||||
this.currentAccount$ = combineLatest([
|
||||
this.accountService.activeAccount$,
|
||||
this.avatarService.avatarColor$,
|
||||
]).pipe(
|
||||
switchMap(async ([account, avatarColor]) => {
|
||||
if (account == null) {
|
||||
return null;
|
||||
}
|
||||
const currentAccount: CurrentAccount = {
|
||||
id: account.id,
|
||||
name: account.name || account.email,
|
||||
email: account.email,
|
||||
status: account.status,
|
||||
avatarColor,
|
||||
};
|
||||
|
||||
get currentAccount$() {
|
||||
return this.currentAccountService.currentAccount$;
|
||||
return currentAccount;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async currentAccountClicked() {
|
||||
if (this.route.snapshot.data.state.includes("account-switcher")) {
|
||||
this.location.back();
|
||||
} else {
|
||||
// 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(["/account-switcher"]);
|
||||
await this.router.navigate(["/account-switcher"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { matches, mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom, timeout } from "rxjs";
|
||||
import { BehaviorSubject, firstValueFrom, of, timeout } from "rxjs";
|
||||
|
||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { AccountSwitcherService } from "./account-switcher.service";
|
||||
@ -16,7 +16,7 @@ describe("AccountSwitcherService", () => {
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>(null);
|
||||
|
||||
const accountService = mock<AccountService>();
|
||||
const stateService = mock<StateService>();
|
||||
const avatarService = mock<AvatarService>();
|
||||
const messagingService = mock<MessagingService>();
|
||||
const environmentService = mock<EnvironmentService>();
|
||||
const logService = mock<LogService>();
|
||||
@ -25,11 +25,13 @@ describe("AccountSwitcherService", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
accountService.accounts$ = accountsSubject;
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
|
||||
accountSwitcherService = new AccountSwitcherService(
|
||||
accountService,
|
||||
stateService,
|
||||
avatarService,
|
||||
messagingService,
|
||||
environmentService,
|
||||
logService,
|
||||
@ -44,6 +46,7 @@ describe("AccountSwitcherService", () => {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
};
|
||||
|
||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||
accountsSubject.next({
|
||||
"1": user1AccountInfo,
|
||||
} as Record<UserId, AccountInfo>);
|
||||
@ -72,6 +75,7 @@ describe("AccountSwitcherService", () => {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
};
|
||||
}
|
||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||
accountsSubject.next(seedAccounts);
|
||||
activeAccountSubject.next(
|
||||
Object.assign(seedAccounts["1" as UserId], { id: "1" as UserId }),
|
||||
|
@ -11,11 +11,11 @@ import {
|
||||
} from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { fromChromeEvent } from "../../../../platform/browser/from-chrome-event";
|
||||
@ -44,7 +44,7 @@ export class AccountSwitcherService {
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private stateService: StateService,
|
||||
private avatarService: AvatarService,
|
||||
private messagingService: MessagingService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logService: LogService,
|
||||
@ -68,7 +68,9 @@ export class AccountSwitcherService {
|
||||
server: await this.environmentService.getHost(id),
|
||||
status: account.status,
|
||||
isActive: id === activeAccount?.id,
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: id }),
|
||||
avatarColor: await firstValueFrom(
|
||||
this.avatarService.getUserAvatarColor$(id as UserId),
|
||||
),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable, switchMap } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export type CurrentAccount = {
|
||||
id: UserId;
|
||||
name: string | undefined;
|
||||
email: string;
|
||||
status: AuthenticationStatus;
|
||||
avatarColor: string;
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class CurrentAccountService {
|
||||
currentAccount$: Observable<CurrentAccount>;
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.currentAccount$ = this.accountService.activeAccount$.pipe(
|
||||
switchMap(async (account) => {
|
||||
if (account == null) {
|
||||
return null;
|
||||
}
|
||||
const currentAccount: CurrentAccount = {
|
||||
id: account.id,
|
||||
name: account.name || account.email,
|
||||
email: account.email,
|
||||
status: account.status,
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: account.id }),
|
||||
};
|
||||
|
||||
return currentAccount;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -91,6 +91,8 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
}
|
||||
|
||||
async launchSsoBrowser() {
|
||||
// Save off email for SSO
|
||||
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
|
||||
await this.loginService.saveEmailSettings();
|
||||
// Generate necessary sso params
|
||||
const passwordOptions: any = {
|
||||
|
@ -3,6 +3,7 @@ import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-options.component";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
@ -16,9 +17,10 @@ export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
) {
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window);
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window, environmentService);
|
||||
}
|
||||
|
||||
close() {
|
||||
|
@ -8,11 +8,21 @@ import {
|
||||
AutofillOverlayVisibility,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import {
|
||||
DefaultDomainSettingsService,
|
||||
DomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/services/i18n.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import {
|
||||
FakeStateProvider,
|
||||
FakeAccountService,
|
||||
mockAccountServiceWith,
|
||||
} from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -41,6 +51,10 @@ import OverlayBackground from "./overlay.background";
|
||||
const iconServerUrl = "https://icons.bitwarden.com/";
|
||||
|
||||
describe("OverlayBackground", () => {
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService);
|
||||
let domainSettingsService: DomainSettingsService;
|
||||
let buttonPortSpy: chrome.runtime.Port;
|
||||
let listPortSpy: chrome.runtime.Port;
|
||||
let overlayBackground: OverlayBackground;
|
||||
@ -50,7 +64,6 @@ describe("OverlayBackground", () => {
|
||||
const environmentService = mock<EnvironmentService>({
|
||||
getIconsUrl: () => iconServerUrl,
|
||||
});
|
||||
const settingsService = mock<SettingsService>();
|
||||
const stateService = mock<BrowserStateService>();
|
||||
const autofillSettingsService = mock<AutofillSettingsService>();
|
||||
const i18nService = mock<I18nService>();
|
||||
@ -72,12 +85,13 @@ describe("OverlayBackground", () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
|
||||
overlayBackground = new OverlayBackground(
|
||||
cipherService,
|
||||
autofillService,
|
||||
authService,
|
||||
environmentService,
|
||||
settingsService,
|
||||
domainSettingsService,
|
||||
stateService,
|
||||
autofillSettingsService,
|
||||
i18nService,
|
||||
@ -90,6 +104,7 @@ describe("OverlayBackground", () => {
|
||||
.mockResolvedValue(AutofillOverlayVisibility.OnFieldFocus);
|
||||
|
||||
themeStateService.selectedTheme$ = of(ThemeType.Light);
|
||||
domainSettingsService.showFavicons$ = of(true);
|
||||
|
||||
void overlayBackground.init();
|
||||
});
|
||||
@ -274,7 +289,7 @@ describe("OverlayBackground", () => {
|
||||
card: { subTitle: "Mastercard, *1234" },
|
||||
});
|
||||
|
||||
it("formats and returns the cipher data", () => {
|
||||
it("formats and returns the cipher data", async () => {
|
||||
overlayBackground["overlayLoginCiphers"] = new Map([
|
||||
["overlay-cipher-0", cipher2],
|
||||
["overlay-cipher-1", cipher1],
|
||||
@ -282,7 +297,7 @@ describe("OverlayBackground", () => {
|
||||
["overlay-cipher-3", cipher4],
|
||||
]);
|
||||
|
||||
const overlayCipherData = overlayBackground["getOverlayCipherData"]();
|
||||
const overlayCipherData = await overlayBackground["getOverlayCipherData"]();
|
||||
|
||||
expect(overlayCipherData).toStrictEqual([
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -92,7 +92,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
private autofillService: AutofillService,
|
||||
private authService: AuthService,
|
||||
private environmentService: EnvironmentService,
|
||||
private settingsService: SettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private stateService: StateService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private i18nService: I18nService,
|
||||
@ -145,7 +145,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
this.overlayLoginCiphers.set(`overlay-cipher-${cipherIndex}`, ciphersViews[cipherIndex]);
|
||||
}
|
||||
|
||||
const ciphers = this.getOverlayCipherData();
|
||||
const ciphers = await this.getOverlayCipherData();
|
||||
this.overlayListPort?.postMessage({ command: "updateOverlayListCiphers", ciphers });
|
||||
await BrowserApi.tabSendMessageData(currentTab, "updateIsOverlayCiphersPopulated", {
|
||||
isOverlayCiphersPopulated: Boolean(ciphers.length),
|
||||
@ -156,8 +156,8 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
* Strips out unnecessary data from the ciphers and returns an array of
|
||||
* objects that contain the cipher data needed for the overlay list.
|
||||
*/
|
||||
private getOverlayCipherData(): OverlayCipherData[] {
|
||||
const isFaviconDisabled = this.settingsService.getDisableFavicon();
|
||||
private async getOverlayCipherData(): Promise<OverlayCipherData[]> {
|
||||
const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$);
|
||||
const overlayCiphersArray = Array.from(this.overlayLoginCiphers);
|
||||
const overlayCipherData = [];
|
||||
let loginCipherIcon: WebsiteIconData;
|
||||
@ -165,7 +165,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
for (let cipherIndex = 0; cipherIndex < overlayCiphersArray.length; cipherIndex++) {
|
||||
const [overlayCipherId, cipher] = overlayCiphersArray[cipherIndex];
|
||||
if (!loginCipherIcon && cipher.type === CipherType.Login) {
|
||||
loginCipherIcon = buildCipherIcon(this.iconsServerUrl, cipher, isFaviconDisabled);
|
||||
loginCipherIcon = buildCipherIcon(this.iconsServerUrl, cipher, showFavicons);
|
||||
}
|
||||
|
||||
overlayCipherData.push({
|
||||
@ -177,7 +177,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
icon:
|
||||
cipher.type === CipherType.Login
|
||||
? loginCipherIcon
|
||||
: buildCipherIcon(this.iconsServerUrl, cipher, isFaviconDisabled),
|
||||
: buildCipherIcon(this.iconsServerUrl, cipher, showFavicons),
|
||||
login: cipher.type === CipherType.Login ? { username: cipher.login.username } : null,
|
||||
card: cipher.type === CipherType.Card ? cipher.card.subTitle : null,
|
||||
});
|
||||
@ -699,7 +699,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
styleSheetUrl: chrome.runtime.getURL(`overlay/${isOverlayListPort ? "list" : "button"}.css`),
|
||||
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
|
||||
translations: this.getTranslations(),
|
||||
ciphers: isOverlayListPort ? this.getOverlayCipherData() : null,
|
||||
ciphers: isOverlayListPort ? await this.getOverlayCipherData() : null,
|
||||
});
|
||||
this.updateOverlayPosition({
|
||||
overlayElement: isOverlayListPort
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
EventCollectionServiceInitOptions,
|
||||
eventCollectionServiceFactory,
|
||||
} from "../../../background/service-factories/event-collection-service.factory";
|
||||
import { billingAccountProfileStateServiceFactory } from "../../../platform/background/service-factories/billing-account-profile-state-service.factory";
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
@ -69,6 +70,7 @@ export function autofillServiceFactory(
|
||||
await logServiceFactory(cache, opts),
|
||||
await domainSettingsServiceFactory(cache, opts),
|
||||
await userVerificationServiceFactory(cache, opts),
|
||||
await billingAccountProfileStateServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { of } from "rxjs";
|
||||
|
||||
import { NOOP_COMMAND_SUFFIX } from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
@ -18,6 +19,7 @@ describe("context-menu", () => {
|
||||
let autofillSettingsService: MockProxy<AutofillSettingsServiceAbstraction>;
|
||||
let i18nService: MockProxy<I18nService>;
|
||||
let logService: MockProxy<LogService>;
|
||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||
|
||||
let removeAllSpy: jest.SpyInstance<void, [callback?: () => void]>;
|
||||
let createSpy: jest.SpyInstance<
|
||||
@ -32,6 +34,7 @@ describe("context-menu", () => {
|
||||
autofillSettingsService = mock();
|
||||
i18nService = mock();
|
||||
logService = mock();
|
||||
billingAccountProfileStateService = mock();
|
||||
|
||||
removeAllSpy = jest
|
||||
.spyOn(chrome.contextMenus, "removeAll")
|
||||
@ -50,6 +53,7 @@ describe("context-menu", () => {
|
||||
autofillSettingsService,
|
||||
i18nService,
|
||||
logService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
autofillSettingsService.enableContextMenu$ = of(true);
|
||||
});
|
||||
@ -66,7 +70,7 @@ describe("context-menu", () => {
|
||||
});
|
||||
|
||||
it("has menu enabled, but does not have premium", async () => {
|
||||
stateService.getCanAccessPremium.mockResolvedValue(false);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
|
||||
|
||||
const createdMenu = await sut.init();
|
||||
expect(createdMenu).toBeTruthy();
|
||||
@ -74,7 +78,7 @@ describe("context-menu", () => {
|
||||
});
|
||||
|
||||
it("has menu enabled and has premium", async () => {
|
||||
stateService.getCanAccessPremium.mockResolvedValue(true);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
|
||||
const createdMenu = await sut.init();
|
||||
expect(createdMenu).toBeTruthy();
|
||||
@ -128,7 +132,7 @@ describe("context-menu", () => {
|
||||
});
|
||||
|
||||
it("create entry for each cipher piece", async () => {
|
||||
stateService.getCanAccessPremium.mockResolvedValue(true);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
|
||||
await sut.loadOptions("TEST_TITLE", "1", createCipher());
|
||||
|
||||
@ -137,7 +141,7 @@ describe("context-menu", () => {
|
||||
});
|
||||
|
||||
it("creates a login/unlock item for each context menu action option when user is not authenticated", async () => {
|
||||
stateService.getCanAccessPremium.mockResolvedValue(true);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
|
||||
await sut.loadOptions("TEST_TITLE", "NOOP");
|
||||
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
SEPARATOR_ID,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
@ -27,6 +28,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { autofillSettingsServiceFactory } from "../../autofill/background/service_factories/autofill-settings-service.factory";
|
||||
import { Account } from "../../models/account";
|
||||
import { billingAccountProfileStateServiceFactory } from "../../platform/background/service-factories/billing-account-profile-state-service.factory";
|
||||
import { CachedServices } from "../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
i18nServiceFactory,
|
||||
@ -163,6 +165,7 @@ export class MainContextMenuHandler {
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private i18nService: I18nService,
|
||||
private logService: LogService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
static async mv3Create(cachedServices: CachedServices) {
|
||||
@ -184,6 +187,11 @@ export class MainContextMenuHandler {
|
||||
stateServiceOptions: {
|
||||
stateFactory: stateFactory,
|
||||
},
|
||||
platformUtilsServiceOptions: {
|
||||
clipboardWriteCallback: () => Promise.resolve(),
|
||||
biometricCallback: () => Promise.resolve(false),
|
||||
win: self,
|
||||
},
|
||||
};
|
||||
|
||||
return new MainContextMenuHandler(
|
||||
@ -191,6 +199,7 @@ export class MainContextMenuHandler {
|
||||
await autofillSettingsServiceFactory(cachedServices, serviceOptions),
|
||||
await i18nServiceFactory(cachedServices, serviceOptions),
|
||||
await logServiceFactory(cachedServices, serviceOptions),
|
||||
await billingAccountProfileStateServiceFactory(cachedServices, serviceOptions),
|
||||
);
|
||||
}
|
||||
|
||||
@ -212,7 +221,10 @@ export class MainContextMenuHandler {
|
||||
|
||||
try {
|
||||
for (const options of this.initContextMenuItems) {
|
||||
if (options.checkPremiumAccess && !(await this.stateService.getCanAccessPremium())) {
|
||||
if (
|
||||
options.checkPremiumAccess &&
|
||||
!(await firstValueFrom(this.billingAccountProfileStateService.hasPremiumFromAnySource$))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -307,7 +319,9 @@ export class MainContextMenuHandler {
|
||||
await createChildItem(COPY_USERNAME_ID);
|
||||
}
|
||||
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$,
|
||||
);
|
||||
if (canAccessPremium && (!cipher || !Utils.isNullOrEmpty(cipher.login?.totp))) {
|
||||
await createChildItem(COPY_VERIFICATION_CODE_ID);
|
||||
}
|
||||
|
@ -1,8 +1,15 @@
|
||||
import AutofillOverlayButtonIframe from "./autofill-overlay-button-iframe";
|
||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||
|
||||
describe("AutofillOverlayButtonIframe", () => {
|
||||
window.customElements.define("autofill-overlay-button-iframe", AutofillOverlayButtonIframe);
|
||||
window.customElements.define(
|
||||
"autofill-overlay-button-iframe",
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillOverlayButtonIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
@ -13,7 +20,7 @@ describe("AutofillOverlayButtonIframe", () => {
|
||||
|
||||
const iframe = document.querySelector("autofill-overlay-button-iframe");
|
||||
|
||||
expect(iframe).toBeInstanceOf(AutofillOverlayButtonIframe);
|
||||
expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement);
|
||||
expect(iframe).toBeInstanceOf(HTMLElement);
|
||||
expect(iframe.shadowRoot).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum";
|
||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||
|
||||
class AutofillOverlayButtonIframe extends AutofillOverlayIframeElement {
|
||||
constructor() {
|
||||
constructor(element: HTMLElement) {
|
||||
super(
|
||||
element,
|
||||
"overlay/button.html",
|
||||
AutofillOverlayPort.Button,
|
||||
{
|
||||
|
@ -4,7 +4,21 @@ import AutofillOverlayIframeService from "./autofill-overlay-iframe.service";
|
||||
jest.mock("./autofill-overlay-iframe.service");
|
||||
|
||||
describe("AutofillOverlayIframeElement", () => {
|
||||
window.customElements.define("autofill-overlay-iframe", AutofillOverlayIframeElement);
|
||||
window.customElements.define(
|
||||
"autofill-overlay-iframe",
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillOverlayIframeElement(
|
||||
this,
|
||||
"overlay/button.html",
|
||||
"overlay/button",
|
||||
{ background: "transparent", border: "none" },
|
||||
"bitwardenOverlayButton",
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -1,16 +1,15 @@
|
||||
import AutofillOverlayIframeService from "./autofill-overlay-iframe.service";
|
||||
|
||||
class AutofillOverlayIframeElement extends HTMLElement {
|
||||
class AutofillOverlayIframeElement {
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
iframePath: string,
|
||||
portName: string,
|
||||
initStyles: Partial<CSSStyleDeclaration>,
|
||||
iframeTitle: string,
|
||||
ariaAlert?: string,
|
||||
) {
|
||||
super();
|
||||
|
||||
const shadow: ShadowRoot = this.attachShadow({ mode: "closed" });
|
||||
const shadow: ShadowRoot = element.attachShadow({ mode: "closed" });
|
||||
const autofillOverlayIframeService = new AutofillOverlayIframeService(
|
||||
iframePath,
|
||||
portName,
|
||||
|
@ -1,8 +1,15 @@
|
||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||
import AutofillOverlayListIframe from "./autofill-overlay-list-iframe";
|
||||
|
||||
describe("AutofillOverlayListIframe", () => {
|
||||
window.customElements.define("autofill-overlay-list-iframe", AutofillOverlayListIframe);
|
||||
window.customElements.define(
|
||||
"autofill-overlay-list-iframe",
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillOverlayListIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
@ -13,7 +20,7 @@ describe("AutofillOverlayListIframe", () => {
|
||||
|
||||
const iframe = document.querySelector("autofill-overlay-list-iframe");
|
||||
|
||||
expect(iframe).toBeInstanceOf(AutofillOverlayListIframe);
|
||||
expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement);
|
||||
expect(iframe).toBeInstanceOf(HTMLElement);
|
||||
expect(iframe.shadowRoot).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum";
|
||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||
|
||||
class AutofillOverlayListIframe extends AutofillOverlayIframeElement {
|
||||
constructor() {
|
||||
constructor(element: HTMLElement) {
|
||||
super(
|
||||
element,
|
||||
"overlay/list.html",
|
||||
AutofillOverlayPort.List,
|
||||
{
|
||||
|
@ -877,6 +877,44 @@ describe("AutofillOverlayContentService", () => {
|
||||
sender: "autofillOverlayContentService",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds the overlay elements as custom web components if the user's browser is not Firefox", () => {
|
||||
let namesIndex = 0;
|
||||
const customNames = ["op-autofill-overlay-button", "op-autofill-overlay-list"];
|
||||
|
||||
jest
|
||||
.spyOn(autofillOverlayContentService as any, "generateRandomCustomElementName")
|
||||
.mockImplementation(() => {
|
||||
if (namesIndex > 1) {
|
||||
return "";
|
||||
}
|
||||
const customName = customNames[namesIndex];
|
||||
namesIndex++;
|
||||
|
||||
return customName;
|
||||
});
|
||||
autofillOverlayContentService["isFirefoxBrowser"] = false;
|
||||
|
||||
autofillOverlayContentService.openAutofillOverlay();
|
||||
|
||||
expect(autofillOverlayContentService["overlayButtonElement"]).toBeInstanceOf(HTMLElement);
|
||||
expect(autofillOverlayContentService["overlayButtonElement"].tagName).toEqual(
|
||||
customNames[0].toUpperCase(),
|
||||
);
|
||||
expect(autofillOverlayContentService["overlayListElement"]).toBeInstanceOf(HTMLElement);
|
||||
expect(autofillOverlayContentService["overlayListElement"].tagName).toEqual(
|
||||
customNames[1].toUpperCase(),
|
||||
);
|
||||
});
|
||||
|
||||
it("builds the overlay elements as `div` elements if the user's browser is Firefox", () => {
|
||||
autofillOverlayContentService["isFirefoxBrowser"] = true;
|
||||
|
||||
autofillOverlayContentService.openAutofillOverlay();
|
||||
|
||||
expect(autofillOverlayContentService["overlayButtonElement"]).toBeInstanceOf(HTMLDivElement);
|
||||
expect(autofillOverlayContentService["overlayListElement"]).toBeInstanceOf(HTMLDivElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe("focusMostRecentOverlayField", () => {
|
||||
|
@ -30,6 +30,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
isOverlayCiphersPopulated = false;
|
||||
pageDetailsUpdateRequired = false;
|
||||
autofillOverlayVisibility: number;
|
||||
private isFirefoxBrowser =
|
||||
globalThis.navigator.userAgent.indexOf(" Firefox/") !== -1 ||
|
||||
globalThis.navigator.userAgent.indexOf(" Gecko/") !== -1;
|
||||
private readonly generateRandomCustomElementName = generateRandomCustomElementName;
|
||||
private readonly findTabs = tabbable;
|
||||
private readonly sendExtensionMessage = sendExtensionMessage;
|
||||
private formFieldElements: Set<ElementWithOpId<FormFieldElement>> = new Set([]);
|
||||
@ -593,6 +597,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
private updateOverlayButtonPosition() {
|
||||
if (!this.overlayButtonElement) {
|
||||
this.createAutofillOverlayButton();
|
||||
this.updateCustomElementDefaultStyles(this.overlayButtonElement);
|
||||
}
|
||||
|
||||
if (!this.isOverlayButtonVisible) {
|
||||
@ -613,6 +618,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
private updateOverlayListPosition() {
|
||||
if (!this.overlayListElement) {
|
||||
this.createAutofillOverlayList();
|
||||
this.updateCustomElementDefaultStyles(this.overlayListElement);
|
||||
}
|
||||
|
||||
if (!this.isOverlayListVisible) {
|
||||
@ -765,11 +771,24 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
return;
|
||||
}
|
||||
|
||||
const customElementName = generateRandomCustomElementName();
|
||||
globalThis.customElements?.define(customElementName, AutofillOverlayButtonIframe);
|
||||
this.overlayButtonElement = globalThis.document.createElement(customElementName);
|
||||
if (this.isFirefoxBrowser) {
|
||||
this.overlayButtonElement = globalThis.document.createElement("div");
|
||||
new AutofillOverlayButtonIframe(this.overlayButtonElement);
|
||||
|
||||
this.updateCustomElementDefaultStyles(this.overlayButtonElement);
|
||||
return;
|
||||
}
|
||||
|
||||
const customElementName = this.generateRandomCustomElementName();
|
||||
globalThis.customElements?.define(
|
||||
customElementName,
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillOverlayButtonIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
this.overlayButtonElement = globalThis.document.createElement(customElementName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -781,11 +800,24 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
return;
|
||||
}
|
||||
|
||||
const customElementName = generateRandomCustomElementName();
|
||||
globalThis.customElements?.define(customElementName, AutofillOverlayListIframe);
|
||||
this.overlayListElement = globalThis.document.createElement(customElementName);
|
||||
if (this.isFirefoxBrowser) {
|
||||
this.overlayListElement = globalThis.document.createElement("div");
|
||||
new AutofillOverlayListIframe(this.overlayListElement);
|
||||
|
||||
this.updateCustomElementDefaultStyles(this.overlayListElement);
|
||||
return;
|
||||
}
|
||||
|
||||
const customElementName = this.generateRandomCustomElementName();
|
||||
globalThis.customElements?.define(
|
||||
customElementName,
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
new AutofillOverlayListIframe(this);
|
||||
}
|
||||
},
|
||||
);
|
||||
this.overlayListElement = globalThis.document.createElement(customElementName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
DefaultDomainSettingsService,
|
||||
DomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -72,6 +73,7 @@ describe("AutofillService", () => {
|
||||
const eventCollectionService = mock<EventCollectionService>();
|
||||
const logService = mock<LogService>();
|
||||
const userVerificationService = mock<UserVerificationService>();
|
||||
const billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||
|
||||
beforeEach(() => {
|
||||
autofillService = new AutofillService(
|
||||
@ -83,6 +85,7 @@ describe("AutofillService", () => {
|
||||
logService,
|
||||
domainSettingsService,
|
||||
userVerificationService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
|
||||
@ -476,6 +479,7 @@ describe("AutofillService", () => {
|
||||
|
||||
it("throws an error if an autofill did not occur for any of the passed pages", async () => {
|
||||
autofillOptions.tab.url = "https://a-different-url.com";
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
|
||||
try {
|
||||
await autofillService.doAutoFill(autofillOptions);
|
||||
@ -487,7 +491,6 @@ describe("AutofillService", () => {
|
||||
});
|
||||
|
||||
it("will autofill login data for a page", async () => {
|
||||
jest.spyOn(stateService, "getCanAccessPremium");
|
||||
jest.spyOn(autofillService as any, "generateFillScript");
|
||||
jest.spyOn(autofillService as any, "generateLoginFillScript");
|
||||
jest.spyOn(logService, "info");
|
||||
@ -497,8 +500,6 @@ describe("AutofillService", () => {
|
||||
const autofillResult = await autofillService.doAutoFill(autofillOptions);
|
||||
|
||||
const currentAutofillPageDetails = autofillOptions.pageDetails[0];
|
||||
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
|
||||
expect(autofillService["getDefaultUriMatchStrategy"]).toHaveBeenCalled();
|
||||
expect(autofillService["generateFillScript"]).toHaveBeenCalledWith(
|
||||
currentAutofillPageDetails.details,
|
||||
{
|
||||
@ -660,7 +661,7 @@ describe("AutofillService", () => {
|
||||
it("returns a TOTP value", async () => {
|
||||
const totpCode = "123456";
|
||||
autofillOptions.cipher.login.totp = "totp";
|
||||
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(true);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(true);
|
||||
jest.spyOn(totpService, "getCode").mockResolvedValue(totpCode);
|
||||
|
||||
@ -673,7 +674,7 @@ describe("AutofillService", () => {
|
||||
|
||||
it("does not return a TOTP value if the user does not have premium features", async () => {
|
||||
autofillOptions.cipher.login.totp = "totp";
|
||||
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(false);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
|
||||
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(true);
|
||||
|
||||
const autofillResult = await autofillService.doAutoFill(autofillOptions);
|
||||
@ -707,7 +708,7 @@ describe("AutofillService", () => {
|
||||
it("returns a null value if the user cannot access premium and the organization does not use TOTP", async () => {
|
||||
autofillOptions.cipher.login.totp = "totp";
|
||||
autofillOptions.cipher.organizationUseTotp = false;
|
||||
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValueOnce(false);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
|
||||
|
||||
const autofillResult = await autofillService.doAutoFill(autofillOptions);
|
||||
|
||||
@ -717,13 +718,12 @@ describe("AutofillService", () => {
|
||||
it("returns a null value if the user has disabled `auto TOTP copy`", async () => {
|
||||
autofillOptions.cipher.login.totp = "totp";
|
||||
autofillOptions.cipher.organizationUseTotp = true;
|
||||
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(true);
|
||||
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
|
||||
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(false);
|
||||
jest.spyOn(totpService, "getCode");
|
||||
|
||||
const autofillResult = await autofillService.doAutoFill(autofillOptions);
|
||||
|
||||
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
|
||||
expect(autofillService.getShouldAutoCopyTotp).toHaveBeenCalled();
|
||||
expect(totpService.getCode).not.toHaveBeenCalled();
|
||||
expect(autofillResult).toBeNull();
|
||||
@ -3380,6 +3380,34 @@ describe("AutofillService", () => {
|
||||
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
|
||||
it("validates attribute identifiers with mixed camel case and non-alpha characters", () => {
|
||||
const attributes: Record<string, boolean> = {
|
||||
_$1_go_look: true,
|
||||
go_look: true,
|
||||
goLook: true,
|
||||
go1look: true,
|
||||
"go look": true,
|
||||
look_go: true,
|
||||
findPerson: true,
|
||||
query$1: true,
|
||||
look_goo: false,
|
||||
golook: false,
|
||||
lookgo: false,
|
||||
logonField: false,
|
||||
ego_input: false,
|
||||
"Gold Password": false,
|
||||
searching_for: false,
|
||||
person_finder: false,
|
||||
};
|
||||
const autofillFieldMocks = Object.keys(attributes).map((key) =>
|
||||
createAutofillFieldMock({ htmlID: key }),
|
||||
);
|
||||
autofillFieldMocks.forEach((field) => {
|
||||
const value = AutofillService["isSearchField"](field);
|
||||
expect(value).toBe(attributes[field.htmlID]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isFieldMatch", () => {
|
||||
|
@ -5,6 +5,7 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import {
|
||||
UriMatchStrategySetting,
|
||||
@ -44,6 +45,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
private openPasswordRepromptPopoutDebounce: NodeJS.Timeout;
|
||||
private currentlyOpeningPasswordRepromptPopout = false;
|
||||
private autofillScriptPortsSet = new Set<chrome.runtime.Port>();
|
||||
static searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
@ -54,6 +56,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
private logService: LogService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -239,7 +242,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
let totp: string | null = null;
|
||||
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$,
|
||||
);
|
||||
const defaultUriMatch = await this.getDefaultUriMatchStrategy();
|
||||
|
||||
if (!canAccessPremium) {
|
||||
@ -1380,11 +1385,33 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return excludedTypes.indexOf(type) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if a passed field contains text artifacts that identify it as a search field.
|
||||
*
|
||||
* @param field - The autofill field that we are validating as a search field
|
||||
*/
|
||||
private static isSearchField(field: AutofillField) {
|
||||
const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
|
||||
const matchPattern = new RegExp(AutoFillConstants.SearchFieldNames.join("|"), "gi");
|
||||
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
|
||||
if (!matchFieldAttributeValues[attrIndex]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Boolean(matchFieldAttributeValues.join(" ").match(matchPattern));
|
||||
// Separate camel case words and case them to lower case values
|
||||
const camelCaseSeparatedFieldAttribute = matchFieldAttributeValues[attrIndex]
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.toLowerCase();
|
||||
// Split the attribute by non-alphabetical characters to get the keywords
|
||||
const attributeKeywords = camelCaseSeparatedFieldAttribute.split(/[^a-z]/gi);
|
||||
|
||||
for (let keywordIndex = 0; keywordIndex < attributeKeywords.length; keywordIndex++) {
|
||||
if (AutofillService.searchFieldNamesSet.has(attributeKeywords[keywordIndex])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static isExcludedFieldType(field: AutofillField, excludedTypes: string[]) {
|
||||
@ -1397,11 +1424,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
// Check if the input is an untyped/mistyped search input
|
||||
if (this.isSearchField(field)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return this.isSearchField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1525,11 +1548,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1);
|
||||
}
|
||||
|
||||
static fieldHasDisqualifyingAttributeValue(field: AutofillField) {
|
||||
@ -1572,7 +1591,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
const arr: AutofillField[] = [];
|
||||
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)) {
|
||||
const isPassword = f.type === "password";
|
||||
if (
|
||||
!isPassword &&
|
||||
AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1581,23 +1604,16 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const isPassword = f.type === "password";
|
||||
|
||||
const isLikePassword = () => {
|
||||
if (f.type !== "text") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.htmlID)) {
|
||||
const testedValues = [f.htmlID, f.htmlName, f.placeholder];
|
||||
for (let i = 0; i < testedValues.length; i++) {
|
||||
if (AutofillService.valueIsLikePassword(testedValues[i])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.htmlName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.placeholder)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -755,6 +755,9 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
|
||||
// Prioritize capturing text content from elements rather than nodes.
|
||||
currentElement = currentElement.parentElement || currentElement.parentNode;
|
||||
if (!currentElement) {
|
||||
return textContentItems;
|
||||
}
|
||||
|
||||
let siblingElement = nodeIsElement(currentElement)
|
||||
? currentElement.previousElementSibling
|
||||
|
@ -553,17 +553,30 @@ describe("InsertAutofillContentService", () => {
|
||||
insertAutofillContentService as any,
|
||||
"simulateUserMouseClickAndFocusEventInteractions",
|
||||
);
|
||||
jest.spyOn(targetInput, "blur");
|
||||
|
||||
insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0");
|
||||
|
||||
expect(
|
||||
insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid,
|
||||
).toBeCalledWith("__0");
|
||||
expect(targetInput.blur).not.toHaveBeenCalled();
|
||||
expect(
|
||||
insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"],
|
||||
).toHaveBeenCalledWith(targetInput, true);
|
||||
expect(elementEventCount).toEqual(expectedElementEventCount);
|
||||
});
|
||||
|
||||
it("blurs the element if it is currently the active element before simulating click and focus events", () => {
|
||||
const targetInput = document.querySelector('input[type="text"]') as FormElementWithAttribute;
|
||||
targetInput.opid = "__0";
|
||||
targetInput.focus();
|
||||
jest.spyOn(targetInput, "blur");
|
||||
|
||||
insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0");
|
||||
|
||||
expect(targetInput.blur).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("insertValueIntoField", () => {
|
||||
@ -710,7 +723,7 @@ describe("InsertAutofillContentService", () => {
|
||||
});
|
||||
|
||||
describe("triggerPostInsertEventsOnElement", () => {
|
||||
it("triggers simulated event interactions and blurs the element after", () => {
|
||||
it("triggers simulated event interactions", () => {
|
||||
const elementValue = "test";
|
||||
document.body.innerHTML = `<input type="text" id="username" value="${elementValue}"/>`;
|
||||
const element = document.getElementById("username") as FillableFormFieldElement;
|
||||
@ -726,7 +739,6 @@ describe("InsertAutofillContentService", () => {
|
||||
expect(insertAutofillContentService["simulateInputElementChangedEvent"]).toHaveBeenCalledWith(
|
||||
element,
|
||||
);
|
||||
expect(element.blur).toHaveBeenCalled();
|
||||
expect(element.value).toBe(elementValue);
|
||||
});
|
||||
});
|
||||
|
@ -185,11 +185,18 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
|
||||
/**
|
||||
* Handles finding an element by opid and triggering click and focus events on the element.
|
||||
* @param {string} opid
|
||||
* @private
|
||||
* To ensure that we trigger a blur event correctly on a filled field, we first check if the
|
||||
* element is already focused. If it is, we blur the element before focusing on it again.
|
||||
*
|
||||
* @param {string} opid - The opid of the element to focus on.
|
||||
*/
|
||||
private handleFocusOnFieldByOpidAction(opid: string) {
|
||||
const element = this.collectAutofillContentService.getAutofillFieldElementByOpid(opid);
|
||||
|
||||
if (document.activeElement === element) {
|
||||
element.blur();
|
||||
}
|
||||
|
||||
this.simulateUserMouseClickAndFocusEventInteractions(element, true);
|
||||
}
|
||||
|
||||
@ -282,7 +289,6 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
}
|
||||
|
||||
this.simulateInputElementChangedEvent(element);
|
||||
element.blur();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -379,10 +385,6 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
element.dispatchEvent(new Event(simulatedInputEvents[index], { bubbles: true }));
|
||||
}
|
||||
}
|
||||
|
||||
private nodeIsElement(node: Node): node is HTMLElement {
|
||||
return node.nodeType === Node.ELEMENT_NODE;
|
||||
}
|
||||
}
|
||||
|
||||
export default InsertAutofillContentService;
|
||||
|
@ -8,19 +8,18 @@ import {
|
||||
AuthRequestServiceAbstraction,
|
||||
AuthRequestService,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
|
||||
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||
@ -39,6 +38,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
|
||||
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
@ -64,6 +64,8 @@ import {
|
||||
UserNotificationSettingsService,
|
||||
UserNotificationSettingsServiceAbstraction,
|
||||
} from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
@ -117,7 +119,6 @@ import { DefaultStateProvider } from "@bitwarden/common/platform/state/implement
|
||||
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
|
||||
/* eslint-enable import/no-restricted-paths */
|
||||
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
@ -125,6 +126,7 @@ import { EventUploadService } from "@bitwarden/common/services/event/event-uploa
|
||||
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/avatar.service";
|
||||
import {
|
||||
PasswordGenerationService,
|
||||
PasswordGenerationServiceAbstraction,
|
||||
@ -180,7 +182,6 @@ import {
|
||||
VaultExportServiceAbstraction,
|
||||
} from "@bitwarden/vault-export-core";
|
||||
|
||||
import { BrowserOrganizationService } from "../admin-console/services/browser-organization.service";
|
||||
import ContextMenusBackground from "../autofill/background/context-menus.background";
|
||||
import NotificationBackground from "../autofill/background/notification.background";
|
||||
import OverlayBackground from "../autofill/background/overlay.background";
|
||||
@ -211,7 +212,6 @@ import { BrowserPlatformUtilsService } from "../platform/services/platform-utils
|
||||
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
|
||||
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
|
||||
import { BrowserSendService } from "../services/browser-send.service";
|
||||
import { BrowserSettingsService } from "../services/browser-settings.service";
|
||||
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
|
||||
import FilelessImporterBackground from "../tools/background/fileless-importer.background";
|
||||
import { BrowserFido2UserInterfaceService } from "../vault/fido2/browser-fido2-user-interface.service";
|
||||
@ -240,7 +240,6 @@ export default class MainBackground {
|
||||
appIdService: AppIdServiceAbstraction;
|
||||
apiService: ApiServiceAbstraction;
|
||||
environmentService: BrowserEnvironmentService;
|
||||
settingsService: SettingsServiceAbstraction;
|
||||
cipherService: CipherServiceAbstraction;
|
||||
folderService: InternalFolderServiceAbstraction;
|
||||
collectionService: CollectionServiceAbstraction;
|
||||
@ -288,7 +287,7 @@ export default class MainBackground {
|
||||
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction;
|
||||
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction;
|
||||
fido2ClientService: Fido2ClientServiceAbstraction;
|
||||
avatarUpdateService: AvatarUpdateServiceAbstraction;
|
||||
avatarService: AvatarServiceAbstraction;
|
||||
mainContextMenuHandler: MainContextMenuHandler;
|
||||
cipherContextMenuHandler: CipherContextMenuHandler;
|
||||
configService: BrowserConfigService;
|
||||
@ -311,6 +310,7 @@ export default class MainBackground {
|
||||
biometricStateService: BiometricStateService;
|
||||
stateEventRunnerService: StateEventRunnerService;
|
||||
ssoLoginService: SsoLoginServiceAbstraction;
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||
|
||||
onUpdatedRan: boolean;
|
||||
onReplacedRan: boolean;
|
||||
@ -409,8 +409,7 @@ export default class MainBackground {
|
||||
);
|
||||
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
|
||||
this.accountService,
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
this.singleUserStateProvider,
|
||||
);
|
||||
this.derivedStateProvider = new BackgroundDerivedStateProvider(
|
||||
this.memoryStorageForStateProviders,
|
||||
@ -428,6 +427,21 @@ export default class MainBackground {
|
||||
);
|
||||
this.biometricStateService = new DefaultBiometricStateService(this.stateProvider);
|
||||
|
||||
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
|
||||
this.platformUtilsService = new BackgroundPlatformUtilsService(
|
||||
this.messagingService,
|
||||
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
|
||||
async () => this.biometricUnlock(),
|
||||
self,
|
||||
);
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
this.singleUserStateProvider,
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService.supportsSecureStorage(),
|
||||
this.secureStorageService,
|
||||
);
|
||||
|
||||
const migrationRunner = new MigrationRunner(
|
||||
this.storageService,
|
||||
this.logService,
|
||||
@ -442,15 +456,9 @@ export default class MainBackground {
|
||||
new StateFactory(GlobalState, Account),
|
||||
this.accountService,
|
||||
this.environmentService,
|
||||
this.tokenService,
|
||||
migrationRunner,
|
||||
);
|
||||
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
|
||||
this.platformUtilsService = new BackgroundPlatformUtilsService(
|
||||
this.messagingService,
|
||||
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
|
||||
async () => this.biometricUnlock(),
|
||||
self,
|
||||
);
|
||||
|
||||
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
|
||||
|
||||
@ -466,17 +474,17 @@ export default class MainBackground {
|
||||
this.stateProvider,
|
||||
this.biometricStateService,
|
||||
);
|
||||
this.tokenService = new TokenService(this.stateService);
|
||||
|
||||
this.appIdService = new AppIdService(this.globalStateProvider);
|
||||
this.apiService = new ApiService(
|
||||
this.tokenService,
|
||||
this.platformUtilsService,
|
||||
this.environmentService,
|
||||
this.appIdService,
|
||||
this.stateService,
|
||||
(expired: boolean) => this.logout(expired),
|
||||
);
|
||||
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
|
||||
this.settingsService = new BrowserSettingsService(this.stateService);
|
||||
this.fileUploadService = new FileUploadService(this.logService);
|
||||
this.cipherFileUploadService = new CipherFileUploadService(
|
||||
this.apiService,
|
||||
@ -490,10 +498,7 @@ export default class MainBackground {
|
||||
this.stateProvider,
|
||||
);
|
||||
this.syncNotifierService = new SyncNotifierService();
|
||||
this.organizationService = new BrowserOrganizationService(
|
||||
this.stateService,
|
||||
this.stateProvider,
|
||||
);
|
||||
this.organizationService = new OrganizationService(this.stateProvider);
|
||||
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
|
||||
this.autofillSettingsService = new AutofillSettingsService(
|
||||
this.stateProvider,
|
||||
@ -563,6 +568,10 @@ export default class MainBackground {
|
||||
this.stateService,
|
||||
);
|
||||
|
||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||
this.activeUserStateProvider,
|
||||
);
|
||||
|
||||
this.loginStrategyService = new LoginStrategyService(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
@ -582,6 +591,7 @@ export default class MainBackground {
|
||||
this.deviceTrustCryptoService,
|
||||
this.authRequestService,
|
||||
this.globalStateProvider,
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
this.ssoLoginService = new SsoLoginService(this.stateProvider);
|
||||
@ -685,7 +695,11 @@ export default class MainBackground {
|
||||
this.fileUploadService,
|
||||
this.sendService,
|
||||
);
|
||||
|
||||
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
||||
|
||||
this.providerService = new ProviderService(this.stateProvider);
|
||||
|
||||
this.syncService = new SyncService(
|
||||
this.apiService,
|
||||
this.domainSettingsService,
|
||||
@ -703,18 +717,22 @@ export default class MainBackground {
|
||||
this.folderApiService,
|
||||
this.organizationService,
|
||||
this.sendApiService,
|
||||
this.avatarService,
|
||||
logoutCallback,
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
this.eventUploadService = new EventUploadService(
|
||||
this.apiService,
|
||||
this.stateService,
|
||||
this.stateProvider,
|
||||
this.logService,
|
||||
this.accountService,
|
||||
);
|
||||
this.eventCollectionService = new EventCollectionService(
|
||||
this.cipherService,
|
||||
this.stateService,
|
||||
this.stateProvider,
|
||||
this.organizationService,
|
||||
this.eventUploadService,
|
||||
this.accountService,
|
||||
);
|
||||
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
|
||||
|
||||
@ -727,6 +745,7 @@ export default class MainBackground {
|
||||
this.logService,
|
||||
this.domainSettingsService,
|
||||
this.userVerificationService,
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
|
||||
|
||||
@ -787,7 +806,6 @@ export default class MainBackground {
|
||||
this.fido2AuthenticatorService,
|
||||
this.configService,
|
||||
this.authService,
|
||||
this.stateService,
|
||||
this.vaultSettingsService,
|
||||
this.domainSettingsService,
|
||||
this.logService,
|
||||
@ -835,7 +853,6 @@ export default class MainBackground {
|
||||
this.cryptoService,
|
||||
this.cryptoFunctionService,
|
||||
this.runtimeBackground,
|
||||
this.i18nService,
|
||||
this.messagingService,
|
||||
this.appIdService,
|
||||
this.platformUtilsService,
|
||||
@ -869,7 +886,7 @@ export default class MainBackground {
|
||||
this.autofillService,
|
||||
this.authService,
|
||||
this.environmentService,
|
||||
this.settingsService,
|
||||
this.domainSettingsService,
|
||||
this.stateService,
|
||||
this.autofillSettingsService,
|
||||
this.i18nService,
|
||||
@ -943,14 +960,13 @@ export default class MainBackground {
|
||||
this.apiService,
|
||||
);
|
||||
|
||||
this.avatarUpdateService = new AvatarUpdateService(this.apiService, this.stateService);
|
||||
|
||||
if (!this.popupOnlyContext) {
|
||||
this.mainContextMenuHandler = new MainContextMenuHandler(
|
||||
this.stateService,
|
||||
this.autofillSettingsService,
|
||||
this.i18nService,
|
||||
this.logService,
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
this.cipherContextMenuHandler = new CipherContextMenuHandler(
|
||||
@ -1090,7 +1106,7 @@ export default class MainBackground {
|
||||
async logout(expired: boolean, userId?: UserId) {
|
||||
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
|
||||
await this.eventUploadService.uploadEvents(userId);
|
||||
await this.eventUploadService.uploadEvents(userId as UserId);
|
||||
|
||||
await Promise.all([
|
||||
this.syncService.setLastSync(new Date(0), userId),
|
||||
|
@ -5,7 +5,6 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -75,7 +74,6 @@ export class NativeMessagingBackground {
|
||||
private cryptoService: CryptoService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private runtimeBackground: RuntimeBackground,
|
||||
private i18nService: I18nService,
|
||||
private messagingService: MessagingService,
|
||||
private appIdService: AppIdService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
|
@ -97,6 +97,10 @@ export default class RuntimeBackground {
|
||||
case "unlocked": {
|
||||
let item: LockedVaultPendingNotificationsData;
|
||||
|
||||
if (msg.command === "loggedIn") {
|
||||
await this.sendBwInstalledMessageToVault();
|
||||
}
|
||||
|
||||
if (this.lockedVaultPendingNotifications?.length > 0) {
|
||||
item = this.lockedVaultPendingNotifications.pop();
|
||||
await closeUnlockPopout();
|
||||
@ -130,9 +134,6 @@ export default class RuntimeBackground {
|
||||
await this.main.refreshBadge();
|
||||
await this.main.refreshMenu();
|
||||
}, 2000);
|
||||
// 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.main.avatarUpdateService.loadColorFromState();
|
||||
this.configService.triggerServerConfigFetch();
|
||||
}
|
||||
break;
|
||||
@ -354,8 +355,6 @@ export default class RuntimeBackground {
|
||||
if (await this.environmentService.hasManagedEnvironment()) {
|
||||
await this.environmentService.setUrlsToManagedEnvironment();
|
||||
}
|
||||
|
||||
await this.sendBwInstalledMessageToVault();
|
||||
}
|
||||
|
||||
this.onInstalledReason = null;
|
||||
|
@ -5,15 +5,14 @@ import {
|
||||
organizationServiceFactory,
|
||||
OrganizationServiceInitOptions,
|
||||
} from "../../admin-console/background/service-factories/organization-service.factory";
|
||||
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
|
||||
import {
|
||||
FactoryOptions,
|
||||
CachedServices,
|
||||
factory,
|
||||
} from "../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/state-service.factory";
|
||||
import { stateProviderFactory } from "../../platform/background/service-factories/state-provider.factory";
|
||||
import { StateServiceInitOptions } from "../../platform/background/service-factories/state-service.factory";
|
||||
import {
|
||||
cipherServiceFactory,
|
||||
CipherServiceInitOptions,
|
||||
@ -43,9 +42,10 @@ export function eventCollectionServiceFactory(
|
||||
async () =>
|
||||
new EventCollectionService(
|
||||
await cipherServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
await organizationServiceFactory(cache, opts),
|
||||
await eventUploadServiceFactory(cache, opts),
|
||||
await accountServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { EventUploadService as AbstractEventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
|
||||
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
|
||||
import {
|
||||
ApiServiceInitOptions,
|
||||
apiServiceFactory,
|
||||
@ -14,10 +15,8 @@ import {
|
||||
logServiceFactory,
|
||||
LogServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/log-service.factory";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/state-service.factory";
|
||||
import { stateProviderFactory } from "../../platform/background/service-factories/state-provider.factory";
|
||||
import { StateServiceInitOptions } from "../../platform/background/service-factories/state-service.factory";
|
||||
|
||||
type EventUploadServiceOptions = FactoryOptions;
|
||||
|
||||
@ -37,8 +36,9 @@ export function eventUploadServiceFactory(
|
||||
async () =>
|
||||
new EventUploadService(
|
||||
await apiServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
await logServiceFactory(cache, opts),
|
||||
await accountServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { SettingsService as AbstractSettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
|
||||
import {
|
||||
FactoryOptions,
|
||||
CachedServices,
|
||||
factory,
|
||||
} from "../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/state-service.factory";
|
||||
import { BrowserSettingsService } from "../../services/browser-settings.service";
|
||||
|
||||
type SettingsServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type SettingsServiceInitOptions = SettingsServiceFactoryOptions & StateServiceInitOptions;
|
||||
|
||||
export function settingsServiceFactory(
|
||||
cache: { settingsService?: AbstractSettingsService } & CachedServices,
|
||||
opts: SettingsServiceInitOptions,
|
||||
): Promise<AbstractSettingsService> {
|
||||
return factory(
|
||||
cache,
|
||||
"settingsService",
|
||||
opts,
|
||||
async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)),
|
||||
);
|
||||
}
|
@ -9,20 +9,15 @@ import {
|
||||
|
||||
import { CachedServices, FactoryOptions, factory } from "./factory-options";
|
||||
import {
|
||||
StateEventRegistrarServiceInitOptions,
|
||||
stateEventRegistrarServiceFactory,
|
||||
} from "./state-event-registrar-service.factory";
|
||||
import {
|
||||
StorageServiceProviderInitOptions,
|
||||
storageServiceProviderFactory,
|
||||
} from "./storage-service-provider.factory";
|
||||
SingleUserStateProviderInitOptions,
|
||||
singleUserStateProviderFactory,
|
||||
} from "./single-user-state-provider.factory";
|
||||
|
||||
type ActiveUserStateProviderFactory = FactoryOptions;
|
||||
|
||||
export type ActiveUserStateProviderInitOptions = ActiveUserStateProviderFactory &
|
||||
AccountServiceInitOptions &
|
||||
StorageServiceProviderInitOptions &
|
||||
StateEventRegistrarServiceInitOptions;
|
||||
SingleUserStateProviderInitOptions;
|
||||
|
||||
export async function activeUserStateProviderFactory(
|
||||
cache: { activeUserStateProvider?: ActiveUserStateProvider } & CachedServices,
|
||||
@ -35,8 +30,7 @@ export async function activeUserStateProviderFactory(
|
||||
async () =>
|
||||
new DefaultActiveUserStateProvider(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await storageServiceProviderFactory(cache, opts),
|
||||
await stateEventRegistrarServiceFactory(cache, opts),
|
||||
await singleUserStateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
PlatformUtilsServiceInitOptions,
|
||||
platformUtilsServiceFactory,
|
||||
} from "./platform-utils-service.factory";
|
||||
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
|
||||
|
||||
type ApiServiceFactoryOptions = FactoryOptions & {
|
||||
apiServiceOptions: {
|
||||
@ -32,7 +33,8 @@ export type ApiServiceInitOptions = ApiServiceFactoryOptions &
|
||||
TokenServiceInitOptions &
|
||||
PlatformUtilsServiceInitOptions &
|
||||
EnvironmentServiceInitOptions &
|
||||
AppIdServiceInitOptions;
|
||||
AppIdServiceInitOptions &
|
||||
StateServiceInitOptions;
|
||||
|
||||
export function apiServiceFactory(
|
||||
cache: { apiService?: AbstractApiService } & CachedServices,
|
||||
@ -48,6 +50,7 @@ export function apiServiceFactory(
|
||||
await platformUtilsServiceFactory(cache, opts),
|
||||
await environmentServiceFactory(cache, opts),
|
||||
await appIdServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
opts.apiServiceOptions.logoutCallback,
|
||||
opts.apiServiceOptions.customUserAgent,
|
||||
),
|
||||
|
@ -0,0 +1,28 @@
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
|
||||
import { activeUserStateProviderFactory } from "./active-user-state-provider.factory";
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
import { StateProviderInitOptions } from "./state-provider.factory";
|
||||
|
||||
type BillingAccountProfileStateServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type BillingAccountProfileStateServiceInitOptions =
|
||||
BillingAccountProfileStateServiceFactoryOptions & StateProviderInitOptions;
|
||||
|
||||
export function billingAccountProfileStateServiceFactory(
|
||||
cache: {
|
||||
billingAccountProfileStateService?: BillingAccountProfileStateService;
|
||||
} & CachedServices,
|
||||
opts: BillingAccountProfileStateServiceInitOptions,
|
||||
): Promise<BillingAccountProfileStateService> {
|
||||
return factory(
|
||||
cache,
|
||||
"billingAccountProfileStateService",
|
||||
opts,
|
||||
async () =>
|
||||
new DefaultBillingAccountProfileStateService(
|
||||
await activeUserStateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
@ -5,6 +5,10 @@ import {
|
||||
accountServiceFactory,
|
||||
AccountServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/account-service.factory";
|
||||
import {
|
||||
tokenServiceFactory,
|
||||
TokenServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/token-service.factory";
|
||||
import { Account } from "../../../models/account";
|
||||
import { BrowserStateService } from "../../services/browser-state.service";
|
||||
|
||||
@ -38,6 +42,7 @@ export type StateServiceInitOptions = StateServiceFactoryOptions &
|
||||
LogServiceInitOptions &
|
||||
AccountServiceInitOptions &
|
||||
EnvironmentServiceInitOptions &
|
||||
TokenServiceInitOptions &
|
||||
MigrationRunnerInitOptions;
|
||||
|
||||
export async function stateServiceFactory(
|
||||
@ -57,6 +62,7 @@ export async function stateServiceFactory(
|
||||
opts.stateServiceOptions.stateFactory,
|
||||
await accountServiceFactory(cache, opts),
|
||||
await environmentServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
await migrationRunnerFactory(cache, opts),
|
||||
opts.stateServiceOptions.useAccountCache,
|
||||
),
|
||||
|
@ -7,6 +7,7 @@ import localeBn from "@angular/common/locales/bn";
|
||||
import localeBs from "@angular/common/locales/bs";
|
||||
import localeCa from "@angular/common/locales/ca";
|
||||
import localeCs from "@angular/common/locales/cs";
|
||||
import localeCy from "@angular/common/locales/cy";
|
||||
import localeDa from "@angular/common/locales/da";
|
||||
import localeDe from "@angular/common/locales/de";
|
||||
import localeEl from "@angular/common/locales/el";
|
||||
@ -19,6 +20,7 @@ import localeFa from "@angular/common/locales/fa";
|
||||
import localeFi from "@angular/common/locales/fi";
|
||||
import localeFil from "@angular/common/locales/fil";
|
||||
import localeFr from "@angular/common/locales/fr";
|
||||
import localeGl from "@angular/common/locales/gl";
|
||||
import localeHe from "@angular/common/locales/he";
|
||||
import localeHi from "@angular/common/locales/hi";
|
||||
import localeHr from "@angular/common/locales/hr";
|
||||
@ -33,9 +35,13 @@ import localeKo from "@angular/common/locales/ko";
|
||||
import localeLt from "@angular/common/locales/lt";
|
||||
import localeLv from "@angular/common/locales/lv";
|
||||
import localeMl from "@angular/common/locales/ml";
|
||||
import localeMr from "@angular/common/locales/mr";
|
||||
import localeMy from "@angular/common/locales/my";
|
||||
import localeNb from "@angular/common/locales/nb";
|
||||
import localeNe from "@angular/common/locales/ne";
|
||||
import localeNl from "@angular/common/locales/nl";
|
||||
import localeNn from "@angular/common/locales/nn";
|
||||
import localeOr from "@angular/common/locales/or";
|
||||
import localePl from "@angular/common/locales/pl";
|
||||
import localePtBr from "@angular/common/locales/pt";
|
||||
import localePtPt from "@angular/common/locales/pt-PT";
|
||||
@ -46,6 +52,7 @@ import localeSk from "@angular/common/locales/sk";
|
||||
import localeSl from "@angular/common/locales/sl";
|
||||
import localeSr from "@angular/common/locales/sr";
|
||||
import localeSv from "@angular/common/locales/sv";
|
||||
import localeTe from "@angular/common/locales/te";
|
||||
import localeTh from "@angular/common/locales/th";
|
||||
import localeTr from "@angular/common/locales/tr";
|
||||
import localeUk from "@angular/common/locales/uk";
|
||||
@ -61,6 +68,7 @@ registerLocaleData(localeBn, "bn");
|
||||
registerLocaleData(localeBs, "bs");
|
||||
registerLocaleData(localeCa, "ca");
|
||||
registerLocaleData(localeCs, "cs");
|
||||
registerLocaleData(localeCy, "cy");
|
||||
registerLocaleData(localeDa, "da");
|
||||
registerLocaleData(localeDe, "de");
|
||||
registerLocaleData(localeEl, "el");
|
||||
@ -73,6 +81,7 @@ registerLocaleData(localeFa, "fa");
|
||||
registerLocaleData(localeFi, "fi");
|
||||
registerLocaleData(localeFil, "fil");
|
||||
registerLocaleData(localeFr, "fr");
|
||||
registerLocaleData(localeGl, "gl");
|
||||
registerLocaleData(localeHe, "he");
|
||||
registerLocaleData(localeHi, "hi");
|
||||
registerLocaleData(localeHr, "hr");
|
||||
@ -87,9 +96,13 @@ registerLocaleData(localeKo, "ko");
|
||||
registerLocaleData(localeLt, "lt");
|
||||
registerLocaleData(localeLv, "lv");
|
||||
registerLocaleData(localeMl, "ml");
|
||||
registerLocaleData(localeMr, "mr");
|
||||
registerLocaleData(localeMy, "my");
|
||||
registerLocaleData(localeNb, "nb");
|
||||
registerLocaleData(localeNe, "ne");
|
||||
registerLocaleData(localeNl, "nl");
|
||||
registerLocaleData(localeNn, "nn");
|
||||
registerLocaleData(localeOr, "or");
|
||||
registerLocaleData(localePl, "pl");
|
||||
registerLocaleData(localePtBr, "pt-BR");
|
||||
registerLocaleData(localePtPt, "pt-PT");
|
||||
@ -100,6 +113,7 @@ registerLocaleData(localeSk, "sk");
|
||||
registerLocaleData(localeSl, "sl");
|
||||
registerLocaleData(localeSr, "sr");
|
||||
registerLocaleData(localeSv, "sv");
|
||||
registerLocaleData(localeTe, "te");
|
||||
registerLocaleData(localeTh, "th");
|
||||
registerLocaleData(localeTr, "tr");
|
||||
registerLocaleData(localeUk, "uk");
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import {
|
||||
@ -32,6 +33,7 @@ describe("Browser State Service", () => {
|
||||
let stateFactory: MockProxy<StateFactory<GlobalState, Account>>;
|
||||
let useAccountCache: boolean;
|
||||
let environmentService: MockProxy<EnvironmentService>;
|
||||
let tokenService: MockProxy<TokenService>;
|
||||
let migrationRunner: MockProxy<MigrationRunner>;
|
||||
|
||||
let state: State<GlobalState, Account>;
|
||||
@ -46,6 +48,7 @@ describe("Browser State Service", () => {
|
||||
logService = mock();
|
||||
stateFactory = mock();
|
||||
environmentService = mock();
|
||||
tokenService = mock();
|
||||
migrationRunner = mock();
|
||||
// turn off account cache for tests
|
||||
useAccountCache = false;
|
||||
@ -77,6 +80,7 @@ describe("Browser State Service", () => {
|
||||
stateFactory,
|
||||
accountService,
|
||||
environmentService,
|
||||
tokenService,
|
||||
migrationRunner,
|
||||
useAccountCache,
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import {
|
||||
@ -45,6 +46,7 @@ export class BrowserStateService
|
||||
stateFactory: StateFactory<GlobalState, Account>,
|
||||
accountService: AccountService,
|
||||
environmentService: EnvironmentService,
|
||||
tokenService: TokenService,
|
||||
migrationRunner: MigrationRunner,
|
||||
useAccountCache = true,
|
||||
) {
|
||||
@ -56,6 +58,7 @@ export class BrowserStateService
|
||||
stateFactory,
|
||||
accountService,
|
||||
environmentService,
|
||||
tokenService,
|
||||
migrationRunner,
|
||||
useAccountCache,
|
||||
);
|
||||
|
@ -17,11 +17,8 @@ import {
|
||||
LoginStrategyServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
@ -42,6 +39,10 @@ import {
|
||||
AutofillSettingsService,
|
||||
AutofillSettingsServiceAbstraction,
|
||||
} from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import {
|
||||
DefaultDomainSettingsService,
|
||||
DomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import {
|
||||
UserNotificationSettingsService,
|
||||
UserNotificationSettingsServiceAbstraction,
|
||||
@ -98,7 +99,6 @@ import { DialogService } from "@bitwarden/components";
|
||||
import { ImportServiceAbstraction } from "@bitwarden/importer/core";
|
||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
||||
|
||||
import { BrowserOrganizationService } from "../../admin-console/services/browser-organization.service";
|
||||
import { UnauthGuardService } from "../../auth/popup/services";
|
||||
import { AutofillService } from "../../autofill/services/abstractions/autofill.service";
|
||||
import MainBackground from "../../background/main.background";
|
||||
@ -118,7 +118,6 @@ import { ForegroundPlatformUtilsService } from "../../platform/services/platform
|
||||
import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider";
|
||||
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
|
||||
import { BrowserSendService } from "../../services/browser-send.service";
|
||||
import { BrowserSettingsService } from "../../services/browser-settings.service";
|
||||
import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service";
|
||||
import { VaultFilterService } from "../../vault/services/vault-filter.service";
|
||||
|
||||
@ -233,7 +232,6 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
deps: [],
|
||||
},
|
||||
{ provide: TotpService, useFactory: getBgService<TotpService>("totpService"), deps: [] },
|
||||
{ provide: TokenService, useFactory: getBgService<TokenService>("tokenService"), deps: [] },
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (globalStateProvider: GlobalStateProvider) => {
|
||||
@ -265,16 +263,6 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
useFactory: getBgService<DevicesServiceAbstraction>("devicesService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: EventUploadService,
|
||||
useFactory: getBgService<EventUploadService>("eventUploadService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: EventCollectionService,
|
||||
useFactory: getBgService<EventCollectionService>("eventCollectionService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: PlatformUtilsService,
|
||||
useExisting: ForegroundPlatformUtilsService,
|
||||
@ -348,11 +336,9 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
},
|
||||
{ provide: SyncService, useFactory: getBgService<SyncService>("syncService"), deps: [] },
|
||||
{
|
||||
provide: SettingsService,
|
||||
useFactory: (stateService: StateServiceAbstraction) => {
|
||||
return new BrowserSettingsService(stateService);
|
||||
},
|
||||
deps: [StateServiceAbstraction],
|
||||
provide: DomainSettingsService,
|
||||
useClass: DefaultDomainSettingsService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
{
|
||||
provide: AbstractStorageService,
|
||||
@ -399,13 +385,6 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
useFactory: getBgService<NotificationsService>("notificationsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: OrganizationService,
|
||||
useFactory: (stateService: StateServiceAbstraction, stateProvider: StateProvider) => {
|
||||
return new BrowserOrganizationService(stateService, stateProvider);
|
||||
},
|
||||
deps: [StateServiceAbstraction, StateProvider],
|
||||
},
|
||||
{
|
||||
provide: VaultFilterService,
|
||||
useClass: VaultFilterService,
|
||||
@ -445,6 +424,7 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
logService: LogServiceAbstraction,
|
||||
accountService: AccountServiceAbstraction,
|
||||
environmentService: EnvironmentService,
|
||||
tokenService: TokenService,
|
||||
migrationRunner: MigrationRunner,
|
||||
) => {
|
||||
return new BrowserStateService(
|
||||
@ -455,6 +435,7 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
new StateFactory(GlobalState, Account),
|
||||
accountService,
|
||||
environmentService,
|
||||
tokenService,
|
||||
migrationRunner,
|
||||
);
|
||||
},
|
||||
@ -465,6 +446,7 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
LogServiceAbstraction,
|
||||
AccountServiceAbstraction,
|
||||
EnvironmentService,
|
||||
TokenService,
|
||||
MigrationRunner,
|
||||
],
|
||||
},
|
||||
|
@ -6,7 +6,6 @@ import { DomainSettingsService } from "@bitwarden/common/autofill/services/domai
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
@ -31,7 +30,6 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
|
||||
accountSwitcherEnabled = false;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private i18nService: I18nService,
|
||||
private router: Router,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
@ -13,7 +12,6 @@ import {
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
@ -51,14 +49,12 @@ export class OptionsComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private messagingService: MessagingService,
|
||||
private stateService: StateService,
|
||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private badgeSettingsService: BadgeSettingsServiceAbstraction,
|
||||
i18nService: I18nService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private settingsService: SettingsService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
) {
|
||||
this.themeOptions = [
|
||||
@ -114,12 +110,14 @@ export class OptionsComponent implements OnInit {
|
||||
this.autofillSettingsService.enableContextMenu$,
|
||||
);
|
||||
|
||||
this.showCardsCurrentTab = !(await this.stateService.getDontShowCardsCurrentTab());
|
||||
this.showIdentitiesCurrentTab = !(await this.stateService.getDontShowIdentitiesCurrentTab());
|
||||
this.showCardsCurrentTab = await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$);
|
||||
this.showIdentitiesCurrentTab = await firstValueFrom(
|
||||
this.vaultSettingsService.showIdentitiesCurrentTab$,
|
||||
);
|
||||
|
||||
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
|
||||
|
||||
this.enableFavicon = !this.settingsService.getDisableFavicon();
|
||||
this.enableFavicon = await firstValueFrom(this.domainSettingsService.showFavicons$);
|
||||
|
||||
this.enableBadgeCounter = await firstValueFrom(this.badgeSettingsService.enableBadgeCounter$);
|
||||
|
||||
@ -169,7 +167,7 @@ export class OptionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async updateFavicon() {
|
||||
await this.settingsService.setDisableFavicon(!this.enableFavicon);
|
||||
await this.domainSettingsService.setShowFavicons(this.enableFavicon);
|
||||
}
|
||||
|
||||
async updateBadgeCounter() {
|
||||
@ -178,11 +176,11 @@ export class OptionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async updateShowCardsCurrentTab() {
|
||||
await this.stateService.setDontShowCardsCurrentTab(!this.showCardsCurrentTab);
|
||||
await this.vaultSettingsService.setShowCardsCurrentTab(this.showCardsCurrentTab);
|
||||
}
|
||||
|
||||
async updateShowIdentitiesCurrentTab() {
|
||||
await this.stateService.setDontShowIdentitiesCurrentTab(!this.showIdentitiesCurrentTab);
|
||||
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
|
@ -12,7 +12,7 @@
|
||||
</header>
|
||||
<main tabindex="-1">
|
||||
<div class="content">
|
||||
<ng-container *ngIf="!isPremium">
|
||||
<ng-container *ngIf="!(isPremium$ | async)">
|
||||
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||
<ul class="bwi-ul">
|
||||
@ -61,7 +61,7 @@
|
||||
></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isPremium">
|
||||
<ng-container *ngIf="isPremium$ | async">
|
||||
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||
<button type="button" class="btn block primary" (click)="manage()">
|
||||
|
@ -3,6 +3,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -27,6 +28,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
private currencyPipe: CurrencyPipe,
|
||||
dialogService: DialogService,
|
||||
environmentService: EnvironmentService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@ -36,6 +38,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
stateService,
|
||||
dialogService,
|
||||
environmentService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
// Support old price string. Can be removed in future once all translations are properly updated.
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
takeUntil,
|
||||
} from "rxjs";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
@ -98,7 +97,6 @@ export class SettingsComponent implements OnInit {
|
||||
private environmentService: EnvironmentService,
|
||||
private cryptoService: CryptoService,
|
||||
private stateService: StateService,
|
||||
private modalService: ModalService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private dialogService: DialogService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
|
@ -1,11 +0,0 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
|
||||
import { browserSession, sessionSync } from "../platform/decorators/session-sync-observable";
|
||||
|
||||
@browserSession
|
||||
export class BrowserSettingsService extends SettingsService {
|
||||
@sessionSync({ initializer: (b: boolean) => b })
|
||||
protected _disableFavicon: BehaviorSubject<boolean>;
|
||||
}
|
@ -6,6 +6,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -49,6 +50,7 @@ export class SendAddEditComponent extends BaseAddEditComponent {
|
||||
dialogService: DialogService,
|
||||
formBuilder: FormBuilder,
|
||||
private filePopoutUtilsService: FilePopoutUtilsService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@ -63,6 +65,7 @@ export class SendAddEditComponent extends BaseAddEditComponent {
|
||||
sendApiService,
|
||||
dialogService,
|
||||
formBuilder,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
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 { 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";
|
||||
@ -15,7 +16,7 @@ import { PasswordRepromptService } from "@bitwarden/vault";
|
||||
selector: "app-action-buttons",
|
||||
templateUrl: "action-buttons.component.html",
|
||||
})
|
||||
export class ActionButtonsComponent {
|
||||
export class ActionButtonsComponent implements OnInit, OnDestroy {
|
||||
@Output() onView = new EventEmitter<CipherView>();
|
||||
@Output() launchEvent = new EventEmitter<CipherView>();
|
||||
@Input() cipher: CipherView;
|
||||
@ -24,17 +25,28 @@ export class ActionButtonsComponent {
|
||||
cipherType = CipherType;
|
||||
userHasPremiumAccess = false;
|
||||
|
||||
private componentIsDestroyed$ = new Subject<boolean>();
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private totpService: TotpService,
|
||||
private stateService: StateService,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
|
||||
ngOnInit() {
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$
|
||||
.pipe(takeUntil(this.componentIsDestroyed$))
|
||||
.subscribe((canAccessPremium: boolean) => {
|
||||
this.userHasPremiumAccess = canAccessPremium;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.componentIsDestroyed$.next(true);
|
||||
this.componentIsDestroyed$.complete();
|
||||
}
|
||||
|
||||
launchCipher() {
|
||||
|
@ -6,7 +6,6 @@ import { firstValueFrom } from "rxjs";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { BrowserFido2UserInterfaceSession } from "../../../fido2/browser-fido2-user-interface.service";
|
||||
@ -52,7 +51,6 @@ export class Fido2UseBrowserLinkComponent {
|
||||
protected fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
|
@ -5,6 +5,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -34,6 +35,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
logService: LogService,
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -46,6 +48,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
stateService,
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -65,11 +65,11 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private syncService: SyncService,
|
||||
private searchService: SearchService,
|
||||
private stateService: StateService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private organizationService: OrganizationService,
|
||||
private vaultFilterService: VaultFilterService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -271,9 +271,11 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
const otherTypes: CipherType[] = [];
|
||||
const dontShowCards = await this.stateService.getDontShowCardsCurrentTab();
|
||||
const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab();
|
||||
this.showOrganizations = this.organizationService.hasOrganizations();
|
||||
const dontShowCards = !(await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$));
|
||||
const dontShowIdentities = !(await firstValueFrom(
|
||||
this.vaultSettingsService.showIdentitiesCurrentTab$,
|
||||
));
|
||||
this.showOrganizations = await this.organizationService.hasOrganizations();
|
||||
if (!dontShowCards) {
|
||||
otherTypes.push(CipherType.Card);
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
|
||||
|
||||
async ngOnInit() {
|
||||
this.searchTypeSearch = !this.platformUtilsService.isSafari();
|
||||
this.showOrganizations = this.organizationService.hasOrganizations();
|
||||
this.showOrganizations = await this.organizationService.hasOrganizations();
|
||||
this.vaultFilter = this.vaultFilterService.getVaultFilter();
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
|
@ -9,6 +9,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -95,6 +96,7 @@ export class ViewComponent extends BaseViewComponent {
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
datePipe: DatePipe,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -117,6 +119,7 @@ export class ViewComponent extends BaseViewComponent {
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
datePipe,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,7 @@ export class LoginCommand {
|
||||
ssoCodeVerifier,
|
||||
this.ssoRedirectUri,
|
||||
orgIdentifier,
|
||||
undefined, // email to look up 2FA token not required as CLI can't remember 2FA token
|
||||
twoFactor,
|
||||
),
|
||||
);
|
||||
|
@ -23,10 +23,12 @@ import { PolicyApiService } from "@bitwarden/common/admin-console/services/polic
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||
@ -39,6 +41,8 @@ import {
|
||||
DefaultDomainSettingsService,
|
||||
DomainSettingsService,
|
||||
} from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
@ -86,7 +90,6 @@ import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
||||
import {
|
||||
@ -155,7 +158,6 @@ export class Main {
|
||||
appIdService: AppIdService;
|
||||
apiService: NodeApiService;
|
||||
environmentService: EnvironmentService;
|
||||
settingsService: SettingsService;
|
||||
cipherService: CipherService;
|
||||
folderService: InternalFolderService;
|
||||
organizationUserService: OrganizationUserService;
|
||||
@ -216,8 +218,10 @@ export class Main {
|
||||
derivedStateProvider: DerivedStateProvider;
|
||||
stateProvider: StateProvider;
|
||||
loginStrategyService: LoginStrategyServiceAbstraction;
|
||||
avatarService: AvatarServiceAbstraction;
|
||||
stateEventRunnerService: StateEventRunnerService;
|
||||
biometricStateService: BiometricStateService;
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||
|
||||
constructor() {
|
||||
let p = null;
|
||||
@ -291,8 +295,7 @@ export class Main {
|
||||
|
||||
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
|
||||
this.accountService,
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
this.singleUserStateProvider,
|
||||
);
|
||||
|
||||
this.derivedStateProvider = new DefaultDerivedStateProvider(
|
||||
@ -308,6 +311,13 @@ export class Main {
|
||||
|
||||
this.environmentService = new EnvironmentService(this.stateProvider, this.accountService);
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
this.singleUserStateProvider,
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService.supportsSecureStorage(),
|
||||
this.secureStorageService,
|
||||
);
|
||||
|
||||
const migrationRunner = new MigrationRunner(
|
||||
this.storageService,
|
||||
this.logService,
|
||||
@ -322,6 +332,7 @@ export class Main {
|
||||
new StateFactory(GlobalState, Account),
|
||||
this.accountService,
|
||||
this.environmentService,
|
||||
this.tokenService,
|
||||
migrationRunner,
|
||||
);
|
||||
|
||||
@ -339,7 +350,6 @@ export class Main {
|
||||
);
|
||||
|
||||
this.appIdService = new AppIdService(this.globalStateProvider);
|
||||
this.tokenService = new TokenService(this.stateService);
|
||||
|
||||
const customUserAgent =
|
||||
"Bitwarden_CLI/" +
|
||||
@ -352,6 +362,7 @@ export class Main {
|
||||
this.platformUtilsService,
|
||||
this.environmentService,
|
||||
this.appIdService,
|
||||
this.stateService,
|
||||
async (expired: boolean) => await this.logout(),
|
||||
customUserAgent,
|
||||
);
|
||||
@ -362,7 +373,6 @@ export class Main {
|
||||
|
||||
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
|
||||
|
||||
this.settingsService = new SettingsService(this.stateService);
|
||||
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
|
||||
|
||||
this.fileUploadService = new FileUploadService(this.logService);
|
||||
@ -397,7 +407,7 @@ export class Main {
|
||||
|
||||
this.providerService = new ProviderService(this.stateProvider);
|
||||
|
||||
this.organizationService = new OrganizationService(this.stateService, this.stateProvider);
|
||||
this.organizationService = new OrganizationService(this.stateProvider);
|
||||
|
||||
this.organizationUserService = new OrganizationUserServiceImplementation(this.apiService);
|
||||
|
||||
@ -446,6 +456,10 @@ export class Main {
|
||||
this.stateService,
|
||||
);
|
||||
|
||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||
this.activeUserStateProvider,
|
||||
);
|
||||
|
||||
this.loginStrategyService = new LoginStrategyService(
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
@ -465,6 +479,7 @@ export class Main {
|
||||
this.deviceTrustCryptoService,
|
||||
this.authRequestService,
|
||||
this.globalStateProvider,
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
this.authService = new AuthService(
|
||||
@ -555,6 +570,8 @@ export class Main {
|
||||
null,
|
||||
);
|
||||
|
||||
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
||||
|
||||
this.syncService = new SyncService(
|
||||
this.apiService,
|
||||
this.domainSettingsService,
|
||||
@ -572,7 +589,9 @@ export class Main {
|
||||
this.folderApiService,
|
||||
this.organizationService,
|
||||
this.sendApiService,
|
||||
this.avatarService,
|
||||
async (expired: boolean) => await this.logout(),
|
||||
this.billingAccountProfileStateService,
|
||||
);
|
||||
|
||||
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
|
||||
@ -619,15 +638,17 @@ export class Main {
|
||||
|
||||
this.eventUploadService = new EventUploadService(
|
||||
this.apiService,
|
||||
this.stateService,
|
||||
this.stateProvider,
|
||||
this.logService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.eventCollectionService = new EventCollectionService(
|
||||
this.cipherService,
|
||||
this.stateService,
|
||||
this.stateProvider,
|
||||
this.organizationService,
|
||||
this.eventUploadService,
|
||||
this.accountService,
|
||||
);
|
||||
}
|
||||
|
||||
@ -651,6 +672,7 @@ export class Main {
|
||||
});
|
||||
const userId = await this.stateService.getUserId();
|
||||
await Promise.all([
|
||||
this.eventUploadService.uploadEvents(userId as UserId),
|
||||
this.syncService.setLastSync(new Date(0)),
|
||||
this.cryptoService.clearKeys(),
|
||||
this.cipherService.clear(userId),
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { CardExport } from "@bitwarden/common/models/export/card.export";
|
||||
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
||||
@ -57,6 +60,7 @@ export class GetCommand extends DownloadCommand {
|
||||
private apiService: ApiService,
|
||||
private organizationService: OrganizationService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(cryptoService);
|
||||
}
|
||||
@ -251,7 +255,9 @@ export class GetCommand extends DownloadCommand {
|
||||
return Response.error("Couldn't generate TOTP code.");
|
||||
}
|
||||
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.accountProfileService.hasPremiumFromAnySource$,
|
||||
);
|
||||
if (!canAccessPremium) {
|
||||
const originalCipher = await this.cipherService.get(cipher.id);
|
||||
if (
|
||||
@ -334,7 +340,10 @@ export class GetCommand extends DownloadCommand {
|
||||
return Response.multipleResults(attachments.map((a) => a.id));
|
||||
}
|
||||
|
||||
if (!(await this.stateService.getCanAccessPremium())) {
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.accountProfileService.hasPremiumFromAnySource$,
|
||||
);
|
||||
if (!canAccessPremium) {
|
||||
const originalCipher = await this.cipherService.get(cipher.id);
|
||||
if (originalCipher == null || originalCipher.organizationId == null) {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
|
@ -68,6 +68,7 @@ export class ServeCommand {
|
||||
this.main.apiService,
|
||||
this.main.organizationService,
|
||||
this.main.eventCollectionService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
this.listCommand = new ListCommand(
|
||||
this.main.cipherService,
|
||||
@ -82,10 +83,10 @@ export class ServeCommand {
|
||||
this.createCommand = new CreateCommand(
|
||||
this.main.cipherService,
|
||||
this.main.folderService,
|
||||
this.main.stateService,
|
||||
this.main.cryptoService,
|
||||
this.main.apiService,
|
||||
this.main.folderApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
this.editCommand = new EditCommand(
|
||||
this.main.cipherService,
|
||||
@ -108,9 +109,9 @@ export class ServeCommand {
|
||||
this.deleteCommand = new DeleteCommand(
|
||||
this.main.cipherService,
|
||||
this.main.folderService,
|
||||
this.main.stateService,
|
||||
this.main.apiService,
|
||||
this.main.folderApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
this.confirmCommand = new ConfirmCommand(
|
||||
this.main.apiService,
|
||||
@ -135,9 +136,9 @@ export class ServeCommand {
|
||||
|
||||
this.sendCreateCommand = new SendCreateCommand(
|
||||
this.main.sendService,
|
||||
this.main.stateService,
|
||||
this.main.environmentService,
|
||||
this.main.sendApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
this.sendDeleteCommand = new SendDeleteCommand(this.main.sendService, this.main.sendApiService);
|
||||
this.sendGetCommand = new SendGetCommand(
|
||||
@ -148,9 +149,9 @@ export class ServeCommand {
|
||||
);
|
||||
this.sendEditCommand = new SendEditCommand(
|
||||
this.main.sendService,
|
||||
this.main.stateService,
|
||||
this.sendGetCommand,
|
||||
this.main.sendApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
this.sendListCommand = new SendListCommand(
|
||||
this.main.sendService,
|
||||
|
@ -6,6 +6,7 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
|
||||
(global as any).fetch = fe.default;
|
||||
@ -20,6 +21,7 @@ export class NodeApiService extends ApiService {
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
appIdService: AppIdService,
|
||||
stateService: StateService,
|
||||
logoutCallback: (expired: boolean) => Promise<void>,
|
||||
customUserAgent: string = null,
|
||||
) {
|
||||
@ -28,6 +30,7 @@ export class NodeApiService extends ApiService {
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
appIdService,
|
||||
stateService,
|
||||
logoutCallback,
|
||||
customUserAgent,
|
||||
);
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DefaultPassphraseGenerationOptions } from "@bitwarden/common/tools/generator/passphrase";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import {
|
||||
DefaultPasswordGenerationOptions,
|
||||
PasswordGenerationServiceAbstraction,
|
||||
} from "@bitwarden/common/tools/generator/password";
|
||||
import { PasswordGeneratorOptions } from "@bitwarden/common/tools/generator/password/password-generator-options";
|
||||
|
||||
import { Response } from "../models/response";
|
||||
@ -64,7 +67,10 @@ class Options {
|
||||
this.capitalize = CliUtils.convertBooleanOption(passedOptions?.capitalize);
|
||||
this.includeNumber = CliUtils.convertBooleanOption(passedOptions?.includeNumber);
|
||||
this.ambiguous = CliUtils.convertBooleanOption(passedOptions?.ambiguous);
|
||||
this.length = CliUtils.convertNumberOption(passedOptions?.length, 14);
|
||||
this.length = CliUtils.convertNumberOption(
|
||||
passedOptions?.length,
|
||||
DefaultPasswordGenerationOptions.length,
|
||||
);
|
||||
this.type = passedOptions?.passphrase ? "passphrase" : "password";
|
||||
this.separator = CliUtils.convertStringOption(
|
||||
passedOptions?.separator,
|
||||
@ -74,8 +80,14 @@ class Options {
|
||||
passedOptions?.words,
|
||||
DefaultPassphraseGenerationOptions.numWords,
|
||||
);
|
||||
this.minNumber = CliUtils.convertNumberOption(passedOptions?.minNumber, 1);
|
||||
this.minSpecial = CliUtils.convertNumberOption(passedOptions?.minSpecial, 1);
|
||||
this.minNumber = CliUtils.convertNumberOption(
|
||||
passedOptions?.minNumber,
|
||||
DefaultPasswordGenerationOptions.minNumber,
|
||||
);
|
||||
this.minSpecial = CliUtils.convertNumberOption(
|
||||
passedOptions?.minSpecial,
|
||||
DefaultPasswordGenerationOptions.minSpecial,
|
||||
);
|
||||
|
||||
if (!this.uppercase && !this.lowercase && !this.special && !this.number) {
|
||||
this.lowercase = true;
|
||||
|
@ -1,8 +1,10 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
@ -16,9 +18,9 @@ import { SendResponse } from "../models/send.response";
|
||||
export class SendCreateCommand {
|
||||
constructor(
|
||||
private sendService: SendService,
|
||||
private stateService: StateService,
|
||||
private environmentService: EnvironmentService,
|
||||
private sendApiService: SendApiService,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async run(requestJson: any, cmdOptions: Record<string, any>) {
|
||||
@ -82,7 +84,7 @@ export class SendCreateCommand {
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.stateService.getCanAccessPremium())) {
|
||||
if (!(await firstValueFrom(this.accountProfileService.hasPremiumFromAnySource$))) {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
@ -12,9 +14,9 @@ import { SendGetCommand } from "./get.command";
|
||||
export class SendEditCommand {
|
||||
constructor(
|
||||
private sendService: SendService,
|
||||
private stateService: StateService,
|
||||
private getCommand: SendGetCommand,
|
||||
private sendApiService: SendApiService,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async run(requestJson: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
@ -57,7 +59,10 @@ export class SendEditCommand {
|
||||
return Response.badRequest("Cannot change a Send's type");
|
||||
}
|
||||
|
||||
if (send.type === SendType.File && !(await this.stateService.getCanAccessPremium())) {
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.accountProfileService.hasPremiumFromAnySource$,
|
||||
);
|
||||
if (send.type === SendType.File && !canAccessPremium) {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
}
|
||||
|
||||
|
@ -153,6 +153,7 @@ export class SendProgram extends Program {
|
||||
this.main.apiService,
|
||||
this.main.organizationService,
|
||||
this.main.eventCollectionService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
const response = await cmd.run("template", object, null);
|
||||
this.processResponse(response);
|
||||
@ -253,9 +254,9 @@ export class SendProgram extends Program {
|
||||
);
|
||||
const cmd = new SendEditCommand(
|
||||
this.main.sendService,
|
||||
this.main.stateService,
|
||||
getCmd,
|
||||
this.main.sendApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
const response = await cmd.run(encodedJson, options);
|
||||
this.processResponse(response);
|
||||
@ -323,9 +324,9 @@ export class SendProgram extends Program {
|
||||
await this.exitIfLocked();
|
||||
const cmd = new SendCreateCommand(
|
||||
this.main.sendService,
|
||||
this.main.stateService,
|
||||
this.main.environmentService,
|
||||
this.main.sendApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
return await cmd.run(encodedJson, options);
|
||||
}
|
||||
|
@ -188,6 +188,7 @@ export class VaultProgram extends Program {
|
||||
this.main.apiService,
|
||||
this.main.organizationService,
|
||||
this.main.eventCollectionService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
const response = await command.run(object, id, cmd);
|
||||
this.processResponse(response);
|
||||
@ -226,10 +227,10 @@ export class VaultProgram extends Program {
|
||||
const command = new CreateCommand(
|
||||
this.main.cipherService,
|
||||
this.main.folderService,
|
||||
this.main.stateService,
|
||||
this.main.cryptoService,
|
||||
this.main.apiService,
|
||||
this.main.folderApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
const response = await command.run(object, encodedJson, cmd);
|
||||
this.processResponse(response);
|
||||
@ -313,9 +314,9 @@ export class VaultProgram extends Program {
|
||||
const command = new DeleteCommand(
|
||||
this.main.cipherService,
|
||||
this.main.folderService,
|
||||
this.main.stateService,
|
||||
this.main.apiService,
|
||||
this.main.folderApiService,
|
||||
this.main.billingAccountProfileStateService,
|
||||
);
|
||||
const response = await command.run(object, id, cmd);
|
||||
this.processResponse(response);
|
||||
|
@ -1,13 +1,15 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
||||
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
|
||||
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
|
||||
@ -26,10 +28,10 @@ export class CreateCommand {
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
private stateService: StateService,
|
||||
private cryptoService: CryptoService,
|
||||
private apiService: ApiService,
|
||||
private folderApiService: FolderApiServiceAbstraction,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async run(
|
||||
@ -124,7 +126,10 @@ export class CreateCommand {
|
||||
return Response.notFound();
|
||||
}
|
||||
|
||||
if (cipher.organizationId == null && !(await this.stateService.getCanAccessPremium())) {
|
||||
if (
|
||||
cipher.organizationId == null &&
|
||||
!(await firstValueFrom(this.accountProfileService.hasPremiumFromAnySource$))
|
||||
) {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
|
||||
@ -12,9 +14,9 @@ export class DeleteCommand {
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
private stateService: StateService,
|
||||
private apiService: ApiService,
|
||||
private folderApiService: FolderApiServiceAbstraction,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
@ -75,7 +77,10 @@ export class DeleteCommand {
|
||||
return Response.error("Attachment `" + id + "` was not found.");
|
||||
}
|
||||
|
||||
if (cipher.organizationId == null && !(await this.stateService.getCanAccessPremium())) {
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.accountProfileService.hasPremiumFromAnySource$,
|
||||
);
|
||||
if (cipher.organizationId == null && !canAccessPremium) {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
}
|
||||
|
||||
|
77
apps/desktop/desktop_native/Cargo.lock
generated
77
apps/desktop/desktop_native/Cargo.lock
generated
@ -130,12 +130,6 @@ version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
@ -333,7 +327,6 @@ dependencies = [
|
||||
"security-framework-sys",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"typenum",
|
||||
"widestring",
|
||||
"windows",
|
||||
@ -377,7 +370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -772,17 +765,6 @@ dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.13.3"
|
||||
@ -925,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1114,7 +1096,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1204,15 +1186,6 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@ -1228,16 +1201,6 @@ version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.6"
|
||||
@ -1295,7 +1258,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1329,32 +1292,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.32.0"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1606,15 +1550,6 @@ dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
@ -25,7 +25,6 @@ retry = "=2.0.0"
|
||||
scopeguard = "=1.2.0"
|
||||
sha2 = "=0.10.8"
|
||||
thiserror = "=1.0.51"
|
||||
tokio = { version = "=1.32.0", features = ["full"] }
|
||||
typenum = "=1.17.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -24,7 +24,7 @@
|
||||
"**/node_modules/argon2/package.json",
|
||||
"**/node_modules/argon2/lib/binding/napi-v3/argon2.node"
|
||||
],
|
||||
"electronVersion": "28.2.6",
|
||||
"electronVersion": "28.2.7",
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
|
@ -3,13 +3,12 @@ import { FormBuilder } from "@angular/forms";
|
||||
import { BehaviorSubject, firstValueFrom, Observable, Subject } from "rxjs";
|
||||
import { concatMap, debounceTime, filter, map, switchMap, takeUntil, tap } from "rxjs/operators";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { DeviceType } from "@bitwarden/common/enums";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
@ -115,9 +114,8 @@ export class SettingsComponent implements OnInit {
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private messagingService: MessagingService,
|
||||
private cryptoService: CryptoService,
|
||||
private modalService: ModalService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private settingsService: SettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private dialogService: DialogService,
|
||||
private userVerificationService: UserVerificationServiceAbstraction,
|
||||
private biometricStateService: BiometricStateService,
|
||||
@ -251,7 +249,7 @@ export class SettingsComponent implements OnInit {
|
||||
approveLoginRequests: (await this.stateService.getApproveLoginRequests()) ?? false,
|
||||
clearClipboard: await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$),
|
||||
minimizeOnCopyToClipboard: await this.stateService.getMinimizeOnCopyToClipboard(),
|
||||
enableFavicons: !(await this.stateService.getDisableFavicon()),
|
||||
enableFavicons: await firstValueFrom(this.domainSettingsService.showFavicons$),
|
||||
enableTray: await this.stateService.getEnableTray(),
|
||||
enableMinToTray: await this.stateService.getEnableMinimizeToTray(),
|
||||
enableCloseToTray: await this.stateService.getEnableCloseToTray(),
|
||||
@ -498,7 +496,7 @@ export class SettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async saveFavicons() {
|
||||
await this.settingsService.setDisableFavicon(!this.form.value.enableFavicons);
|
||||
await this.domainSettingsService.setShowFavicons(this.form.value.enableFavicons);
|
||||
this.messagingService.send("refreshCiphers");
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,9 @@ import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
|
||||
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
@ -122,7 +122,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private broadcasterService: BroadcasterService,
|
||||
private folderService: InternalFolderService,
|
||||
private settingsService: SettingsService,
|
||||
private syncService: SyncService,
|
||||
private passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
private cipherService: CipherService,
|
||||
@ -153,6 +152,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private biometricStateService: BiometricStateService,
|
||||
private stateEventRunnerService: StateEventRunnerService,
|
||||
private providerService: ProviderService,
|
||||
private organizationService: InternalOrganizationServiceAbstraction,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -574,7 +574,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
let preLogoutActiveUserId;
|
||||
try {
|
||||
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
|
||||
// Provide the userId of the user to upload events for
|
||||
await this.eventUploadService.uploadEvents(userBeingLoggedOut as UserId);
|
||||
await this.syncService.setLastSync(new Date(0), userBeingLoggedOut);
|
||||
await this.cryptoService.clearKeys(userBeingLoggedOut);
|
||||
await this.cipherService.clear(userBeingLoggedOut);
|
||||
|
@ -2,9 +2,10 @@ import { animate, state, style, transition, trigger } from "@angular/animations"
|
||||
import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
@ -12,6 +13,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { Account } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
type ActiveAccount = {
|
||||
id: string;
|
||||
@ -84,6 +86,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private authService: AuthService,
|
||||
private avatarService: AvatarService,
|
||||
private messagingService: MessagingService,
|
||||
private router: Router,
|
||||
private tokenService: TokenService,
|
||||
@ -101,7 +104,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
id: await this.tokenService.getUserId(),
|
||||
name: (await this.tokenService.getName()) ?? (await this.tokenService.getEmail()),
|
||||
email: await this.tokenService.getEmail(),
|
||||
avatarColor: await this.stateService.getAvatarColor(),
|
||||
avatarColor: await firstValueFrom(this.avatarService.avatarColor$),
|
||||
server: await this.environmentService.getHost(),
|
||||
};
|
||||
} catch {
|
||||
@ -154,7 +157,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
name: baseAccounts[userId].profile.name,
|
||||
email: baseAccounts[userId].profile.email,
|
||||
authenticationStatus: await this.authService.getAuthStatus(userId),
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: userId }),
|
||||
avatarColor: await firstValueFrom(this.avatarService.getUserAvatarColor$(userId as UserId)),
|
||||
server: await this.environmentService.getHost(userId),
|
||||
};
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
OBSERVABLE_MEMORY_STORAGE,
|
||||
OBSERVABLE_DISK_STORAGE,
|
||||
WINDOW,
|
||||
SUPPORTS_SECURE_STORAGE,
|
||||
SYSTEM_THEME_OBSERVABLE,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
@ -18,6 +19,7 @@ import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/adm
|
||||
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
@ -54,7 +56,10 @@ import { LoginGuard } from "../../auth/guards/login.guard";
|
||||
import { Account } from "../../models/account";
|
||||
import { ElectronCryptoService } from "../../platform/services/electron-crypto.service";
|
||||
import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service";
|
||||
import { ElectronPlatformUtilsService } from "../../platform/services/electron-platform-utils.service";
|
||||
import {
|
||||
ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
ElectronPlatformUtilsService,
|
||||
} from "../../platform/services/electron-platform-utils.service";
|
||||
import { ElectronRendererMessagingService } from "../../platform/services/electron-renderer-messaging.service";
|
||||
import { ElectronRendererSecureStorageService } from "../../platform/services/electron-renderer-secure-storage.service";
|
||||
import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service";
|
||||
@ -101,6 +106,13 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
useClass: ElectronPlatformUtilsService,
|
||||
deps: [I18nServiceAbstraction, MessagingServiceAbstraction],
|
||||
},
|
||||
{
|
||||
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
||||
// the TokenService having to inject the PlatformUtilsService which introduces a
|
||||
// circular dependency on Desktop only.
|
||||
provide: SUPPORTS_SECURE_STORAGE,
|
||||
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useClass: I18nRendererService,
|
||||
@ -140,6 +152,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
STATE_FACTORY,
|
||||
AccountServiceAbstraction,
|
||||
EnvironmentService,
|
||||
TokenService,
|
||||
MigrationRunner,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
],
|
||||
@ -170,7 +183,6 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
CryptoServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
EncryptedMessageHandlerService,
|
||||
DialogService,
|
||||
],
|
||||
|
@ -4,6 +4,7 @@ import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -32,6 +33,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
sendApiService: SendApiService,
|
||||
dialogService: DialogService,
|
||||
formBuilder: FormBuilder,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@ -46,6 +48,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
sendApiService,
|
||||
dialogService,
|
||||
formBuilder,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { Router } from "@angular/router";
|
||||
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "@bitwarden/angular/auth/components/two-factor-options.component";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
@ -16,7 +17,8 @@ export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
) {
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window);
|
||||
super(twoFactorService, router, i18nService, platformUtilsService, window, environmentService);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ import * as path from "path";
|
||||
|
||||
import { app } from "electron";
|
||||
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DefaultBiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
@ -36,6 +38,7 @@ import { ClipboardMain } from "./platform/main/clipboard.main";
|
||||
import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener";
|
||||
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
|
||||
import { ElectronLogMainService } from "./platform/services/electron-log.main.service";
|
||||
import { ELECTRON_SUPPORTS_SECURE_STORAGE } from "./platform/services/electron-platform-utils.service";
|
||||
import { ElectronStateService } from "./platform/services/electron-state.service";
|
||||
import { ElectronStorageService } from "./platform/services/electron-storage.service";
|
||||
import { I18nMainService } from "./platform/services/i18n.main.service";
|
||||
@ -53,6 +56,7 @@ export class Main {
|
||||
mainCryptoFunctionService: MainCryptoFunctionService;
|
||||
desktopCredentialStorageListener: DesktopCredentialStorageListener;
|
||||
migrationRunner: MigrationRunner;
|
||||
tokenService: TokenServiceAbstraction;
|
||||
|
||||
windowMain: WindowMain;
|
||||
messagingMain: MessagingMain;
|
||||
@ -124,19 +128,32 @@ export class Main {
|
||||
storageServiceProvider,
|
||||
);
|
||||
|
||||
const stateProvider = new DefaultStateProvider(
|
||||
new DefaultActiveUserStateProvider(
|
||||
accountService,
|
||||
const singleUserStateProvider = new DefaultSingleUserStateProvider(
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
),
|
||||
new DefaultSingleUserStateProvider(storageServiceProvider, stateEventRegistrarService),
|
||||
);
|
||||
|
||||
const activeUserStateProvider = new DefaultActiveUserStateProvider(
|
||||
accountService,
|
||||
singleUserStateProvider,
|
||||
);
|
||||
|
||||
const stateProvider = new DefaultStateProvider(
|
||||
activeUserStateProvider,
|
||||
singleUserStateProvider,
|
||||
globalStateProvider,
|
||||
new DefaultDerivedStateProvider(this.memoryStorageForStateProviders),
|
||||
);
|
||||
|
||||
this.environmentService = new EnvironmentService(stateProvider, accountService);
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
singleUserStateProvider,
|
||||
globalStateProvider,
|
||||
ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
this.storageService,
|
||||
);
|
||||
|
||||
this.migrationRunner = new MigrationRunner(
|
||||
this.storageService,
|
||||
this.logService,
|
||||
@ -154,6 +171,7 @@ export class Main {
|
||||
new StateFactory(GlobalState, Account),
|
||||
accountService, // will not broadcast logouts. This is a hack until we can remove messaging dependency
|
||||
this.environmentService,
|
||||
this.tokenService,
|
||||
this.migrationRunner,
|
||||
false, // Do not use disk caching because this will get out of sync with the renderer service
|
||||
);
|
||||
@ -175,6 +193,7 @@ export class Main {
|
||||
this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => {
|
||||
this.messagingMain.onMessage(message);
|
||||
});
|
||||
|
||||
this.powerMonitorMain = new PowerMonitorMain(this.messagingService);
|
||||
this.menuMain = new MenuMain(
|
||||
this.i18nService,
|
||||
|
@ -8,6 +8,7 @@ import localeBn from "@angular/common/locales/bn";
|
||||
import localeBs from "@angular/common/locales/bs";
|
||||
import localeCa from "@angular/common/locales/ca";
|
||||
import localeCs from "@angular/common/locales/cs";
|
||||
import localeCy from "@angular/common/locales/cy";
|
||||
import localeDa from "@angular/common/locales/da";
|
||||
import localeDe from "@angular/common/locales/de";
|
||||
import localeEl from "@angular/common/locales/el";
|
||||
@ -21,6 +22,7 @@ import localeFa from "@angular/common/locales/fa";
|
||||
import localeFi from "@angular/common/locales/fi";
|
||||
import localeFil from "@angular/common/locales/fil";
|
||||
import localeFr from "@angular/common/locales/fr";
|
||||
import localeGl from "@angular/common/locales/gl";
|
||||
import localeHe from "@angular/common/locales/he";
|
||||
import localeHi from "@angular/common/locales/hi";
|
||||
import localeHr from "@angular/common/locales/hr";
|
||||
@ -32,11 +34,16 @@ import localeKa from "@angular/common/locales/ka";
|
||||
import localeKm from "@angular/common/locales/km";
|
||||
import localeKn from "@angular/common/locales/kn";
|
||||
import localeKo from "@angular/common/locales/ko";
|
||||
import localeLt from "@angular/common/locales/lt";
|
||||
import localeLv from "@angular/common/locales/lv";
|
||||
import localeMl from "@angular/common/locales/ml";
|
||||
import localeMr from "@angular/common/locales/mr";
|
||||
import localeMy from "@angular/common/locales/my";
|
||||
import localeNb from "@angular/common/locales/nb";
|
||||
import localeNe from "@angular/common/locales/ne";
|
||||
import localeNl from "@angular/common/locales/nl";
|
||||
import localeNn from "@angular/common/locales/nn";
|
||||
import localeOr from "@angular/common/locales/or";
|
||||
import localePl from "@angular/common/locales/pl";
|
||||
import localePtBr from "@angular/common/locales/pt";
|
||||
import localePtPt from "@angular/common/locales/pt-PT";
|
||||
@ -48,6 +55,7 @@ import localeSl from "@angular/common/locales/sl";
|
||||
import localeSr from "@angular/common/locales/sr";
|
||||
import localeMe from "@angular/common/locales/sr-Latn-ME";
|
||||
import localeSv from "@angular/common/locales/sv";
|
||||
import localeTe from "@angular/common/locales/te";
|
||||
import localeTh from "@angular/common/locales/th";
|
||||
import localeTr from "@angular/common/locales/tr";
|
||||
import localeUk from "@angular/common/locales/uk";
|
||||
@ -64,6 +72,7 @@ registerLocaleData(localeBn, "bn");
|
||||
registerLocaleData(localeBs, "bs");
|
||||
registerLocaleData(localeCa, "ca");
|
||||
registerLocaleData(localeCs, "cs");
|
||||
registerLocaleData(localeCy, "cy");
|
||||
registerLocaleData(localeDa, "da");
|
||||
registerLocaleData(localeDe, "de");
|
||||
registerLocaleData(localeEl, "el");
|
||||
@ -77,6 +86,7 @@ registerLocaleData(localeFa, "fa");
|
||||
registerLocaleData(localeFi, "fi");
|
||||
registerLocaleData(localeFil, "fil");
|
||||
registerLocaleData(localeFr, "fr");
|
||||
registerLocaleData(localeGl, "gl");
|
||||
registerLocaleData(localeHe, "he");
|
||||
registerLocaleData(localeHi, "hi");
|
||||
registerLocaleData(localeHr, "hr");
|
||||
@ -88,12 +98,17 @@ registerLocaleData(localeKa, "ka");
|
||||
registerLocaleData(localeKm, "km");
|
||||
registerLocaleData(localeKn, "kn");
|
||||
registerLocaleData(localeKo, "ko");
|
||||
registerLocaleData(localeLt, "lt");
|
||||
registerLocaleData(localeLv, "lv");
|
||||
registerLocaleData(localeMe, "me");
|
||||
registerLocaleData(localeMl, "ml");
|
||||
registerLocaleData(localeMr, "mr");
|
||||
registerLocaleData(localeMy, "my");
|
||||
registerLocaleData(localeNb, "nb");
|
||||
registerLocaleData(localeNe, "ne");
|
||||
registerLocaleData(localeNl, "nl");
|
||||
registerLocaleData(localeNn, "nn");
|
||||
registerLocaleData(localeOr, "or");
|
||||
registerLocaleData(localePl, "pl");
|
||||
registerLocaleData(localePtBr, "pt-BR");
|
||||
registerLocaleData(localePtPt, "pt-PT");
|
||||
@ -104,6 +119,7 @@ registerLocaleData(localeSk, "sk");
|
||||
registerLocaleData(localeSl, "sl");
|
||||
registerLocaleData(localeSr, "sr");
|
||||
registerLocaleData(localeSv, "sv");
|
||||
registerLocaleData(localeTe, "te");
|
||||
registerLocaleData(localeTh, "th");
|
||||
registerLocaleData(localeTr, "tr");
|
||||
registerLocaleData(localeUk, "uk");
|
||||
|
@ -8,6 +8,8 @@ import {
|
||||
|
||||
import { ClipboardWriteMessage } from "../types/clipboard";
|
||||
|
||||
export const ELECTRON_SUPPORTS_SECURE_STORAGE = true;
|
||||
|
||||
export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
@ -142,7 +144,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
}
|
||||
|
||||
supportsSecureStorage(): boolean {
|
||||
return true;
|
||||
return ELECTRON_SUPPORTS_SECURE_STORAGE;
|
||||
}
|
||||
|
||||
getAutofillKeyboardShortcut(): Promise<string> {
|
||||
|
@ -4,7 +4,6 @@ import { firstValueFrom } from "rxjs";
|
||||
import { NativeMessagingVersion } from "@bitwarden/common/enums";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
@ -33,7 +32,6 @@ export class NativeMessageHandlerService {
|
||||
private cryptoService: CryptoService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private messagingService: MessagingService,
|
||||
private i18nService: I18nService,
|
||||
private encryptedMessageHandlerService: EncryptedMessageHandlerService,
|
||||
private dialogService: DialogService,
|
||||
) {}
|
||||
|
@ -3,7 +3,6 @@ import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -35,7 +34,6 @@ export class NativeMessagingService {
|
||||
private cryptoService: CryptoService,
|
||||
private platformUtilService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private i18nService: I18nService,
|
||||
private messagingService: MessagingService,
|
||||
private stateService: StateService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
|
@ -7,7 +7,7 @@
|
||||
{{ "premiumMembership" | i18n }}
|
||||
</h1>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<div *ngIf="!(isPremium$ | async)">
|
||||
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||
<ul class="bwi-ul">
|
||||
@ -40,7 +40,7 @@
|
||||
{{ "premiumPrice" | i18n: (price | currency: "$") }}
|
||||
</p>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<div *ngIf="isPremium$ | async">
|
||||
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||
</div>
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium">
|
||||
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium$ | async">
|
||||
<b>{{ "premiumManage" | i18n }}</b>
|
||||
</button>
|
||||
<button
|
||||
@ -56,13 +56,13 @@
|
||||
type="button"
|
||||
class="primary"
|
||||
(click)="purchase()"
|
||||
*ngIf="!isPremium"
|
||||
*ngIf="!(isPremium$ | async)"
|
||||
[disabled]="$any(purchaseBtn).loading"
|
||||
>
|
||||
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<div class="right" *ngIf="!(isPremium$ | async)">
|
||||
<button
|
||||
#refreshBtn
|
||||
type="button"
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -22,6 +23,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
stateService: StateService,
|
||||
dialogService: DialogService,
|
||||
environmentService: EnvironmentService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@ -31,6 +33,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
stateService,
|
||||
dialogService,
|
||||
environmentService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -26,6 +27,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
stateService: StateService,
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -38,6 +40,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
stateService,
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
ViewContainerRef,
|
||||
} from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
|
||||
@ -15,6 +16,7 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -84,6 +86,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
activeFilter: VaultFilter = new VaultFilter();
|
||||
|
||||
private modal: ModalRef = null;
|
||||
private componentIsDestroyed$ = new Subject<boolean>();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@ -103,10 +106,16 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private searchBarService: SearchBarService,
|
||||
private apiService: ApiService,
|
||||
private dialogService: DialogService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$
|
||||
.pipe(takeUntil(this.componentIsDestroyed$))
|
||||
.subscribe((canAccessPremium: boolean) => {
|
||||
this.userHasPremiumAccess = canAccessPremium;
|
||||
});
|
||||
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
// 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
|
||||
@ -229,6 +238,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
ngOnDestroy() {
|
||||
this.searchBarService.setEnabled(false);
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
this.componentIsDestroyed$.next(true);
|
||||
this.componentIsDestroyed$.complete();
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
@ -13,6 +13,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -58,6 +59,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
datePipe: DatePipe,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -80,6 +82,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
datePipe,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
ngOnInit() {
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||
import {
|
||||
OrganizationUserStatusType,
|
||||
OrganizationUserType,
|
||||
@ -17,7 +19,6 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se
|
||||
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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
@ -109,8 +110,8 @@ export abstract class BasePeopleComponent<
|
||||
private logService: LogService,
|
||||
private searchPipe: SearchPipe,
|
||||
protected userNamePipe: UserNamePipe,
|
||||
protected stateService: StateService,
|
||||
protected dialogService: DialogService,
|
||||
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||
) {}
|
||||
|
||||
abstract edit(user: UserType): void;
|
||||
@ -351,7 +352,9 @@ export abstract class BasePeopleComponent<
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(user.userId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
const autoConfirm = await this.stateService.getAutoConfirmFingerPrints();
|
||||
const autoConfirm = await firstValueFrom(
|
||||
this.organizationManagementPreferencesService.autoConfirmFingerPrints.state$,
|
||||
);
|
||||
if (autoConfirm == null || !autoConfirm) {
|
||||
const [modal] = await this.modalService.openViewRef(
|
||||
UserConfirmComponent,
|
||||
|
@ -17,7 +17,7 @@ export class IsPaidOrgGuard implements CanActivate {
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const org = this.organizationService.get(route.params.organizationId);
|
||||
const org = await this.organizationService.get(route.params.organizationId);
|
||||
|
||||
if (org == null) {
|
||||
return this.router.createUrlTree(["/"]);
|
||||
|
@ -66,7 +66,7 @@ describe("Organization Permissions Guard", () => {
|
||||
|
||||
it("permits navigation if no permissions are specified", async () => {
|
||||
const org = orgFactory();
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
@ -81,7 +81,7 @@ describe("Organization Permissions Guard", () => {
|
||||
};
|
||||
|
||||
const org = orgFactory();
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
@ -104,7 +104,7 @@ describe("Organization Permissions Guard", () => {
|
||||
});
|
||||
|
||||
const org = orgFactory();
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
@ -124,7 +124,7 @@ describe("Organization Permissions Guard", () => {
|
||||
}),
|
||||
});
|
||||
const org = orgFactory();
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
@ -141,7 +141,7 @@ describe("Organization Permissions Guard", () => {
|
||||
type: OrganizationUserType.Admin,
|
||||
enabled: false,
|
||||
});
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
@ -153,7 +153,7 @@ describe("Organization Permissions Guard", () => {
|
||||
type: OrganizationUserType.Owner,
|
||||
enabled: false,
|
||||
});
|
||||
organizationService.get.calledWith(org.id).mockReturnValue(org);
|
||||
organizationService.get.calledWith(org.id).mockResolvedValue(org);
|
||||
|
||||
const actual = await organizationPermissionsGuard.canActivate(route, state);
|
||||
|
||||
|
@ -28,7 +28,7 @@ export class OrganizationPermissionsGuard implements CanActivate {
|
||||
await this.syncService.fullSync(false);
|
||||
}
|
||||
|
||||
const org = this.organizationService.get(route.params.organizationId);
|
||||
const org = await this.organizationService.get(route.params.organizationId);
|
||||
if (org == null) {
|
||||
return this.router.createUrlTree(["/"]);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export class OrganizationRedirectGuard implements CanActivate {
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const org = this.organizationService.get(route.params.organizationId);
|
||||
const org = await this.organizationService.get(route.params.organizationId);
|
||||
|
||||
const customRedirect = route.data?.autoRedirectCallback;
|
||||
if (customRedirect) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
|
||||
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-user-confirm",
|
||||
@ -22,7 +22,7 @@ export class UserConfirmComponent implements OnInit {
|
||||
constructor(
|
||||
private cryptoService: CryptoService,
|
||||
private logService: LogService,
|
||||
private stateService: StateService,
|
||||
private organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -45,7 +45,7 @@ export class UserConfirmComponent implements OnInit {
|
||||
}
|
||||
|
||||
if (this.dontAskAgain) {
|
||||
await this.stateService.setAutoConfirmFingerprints(true);
|
||||
await this.organizationManagementPreferencesService.autoConfirmFingerPrints.set(true);
|
||||
}
|
||||
|
||||
this.onConfirmedUser.emit();
|
||||
|
@ -2,6 +2,7 @@ import { Component, Input } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import { BulkUserDetails } from "./bulk-status.component";
|
||||
@ -14,7 +15,9 @@ export class BulkRemoveComponent {
|
||||
@Input() organizationId: string;
|
||||
@Input() set users(value: BulkUserDetails[]) {
|
||||
this._users = value;
|
||||
this.showNoMasterPasswordWarning = this._users.some((u) => u.hasMasterPassword === false);
|
||||
this.showNoMasterPasswordWarning = this._users.some(
|
||||
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
|
||||
);
|
||||
}
|
||||
|
||||
get users(): BulkUserDetails[] {
|
||||
|
@ -2,6 +2,7 @@ import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
|
||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
@ -37,7 +38,9 @@ export class BulkRestoreRevokeComponent {
|
||||
this.isRevoking = data.isRevoking;
|
||||
this.organizationId = data.organizationId;
|
||||
this.users = data.users;
|
||||
this.showNoMasterPasswordWarning = this.users.some((u) => u.hasMasterPassword === false);
|
||||
this.showNoMasterPasswordWarning = this.users.some(
|
||||
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
|
||||
);
|
||||
}
|
||||
|
||||
get bulkTitle() {
|
||||
|
@ -399,7 +399,11 @@
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="organization.useGroups" [label]="'groups' | i18n">
|
||||
<div class="tw-mb-6">
|
||||
{{ "groupAccessUserDesc" | i18n }}
|
||||
{{
|
||||
(restrictedAccess$ | async)
|
||||
? ("restrictedGroupAccess" | i18n)
|
||||
: ("groupAccessUserDesc" | i18n)
|
||||
}}
|
||||
</div>
|
||||
<bit-access-selector
|
||||
formControlName="groups"
|
||||
@ -408,10 +412,14 @@
|
||||
[selectorLabelText]="'selectGroups' | i18n"
|
||||
[emptySelectionText]="'noGroupsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
></bit-access-selector>
|
||||
</bit-tab>
|
||||
<bit-tab [label]="'collections' | i18n">
|
||||
<div *ngIf="organization.useGroups" class="tw-mb-6">
|
||||
<div class="tw-mb-6" *ngIf="restrictedAccess$ | async">
|
||||
{{ "restrictedCollectionAccess" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="organization.useGroups && !(restrictedAccess$ | async)" class="tw-mb-6">
|
||||
{{ "userPermissionOverrideHelper" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="!organization.flexibleCollections" class="tw-mb-6">
|
||||
@ -441,6 +449,7 @@
|
||||
[selectorLabelText]="'selectCollections' | i18n"
|
||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
></bit-access-selector
|
||||
></bit-tab>
|
||||
</bit-tab-group>
|
||||
|
@ -4,6 +4,7 @@ import { FormBuilder, Validators } from "@angular/forms";
|
||||
import {
|
||||
combineLatest,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
of,
|
||||
shareReplay,
|
||||
@ -20,7 +21,9 @@ import {
|
||||
} from "@bitwarden/common/admin-console/enums";
|
||||
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -99,6 +102,8 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
groups: [[] as AccessItemValue[]],
|
||||
});
|
||||
|
||||
protected restrictedAccess$: Observable<boolean>;
|
||||
|
||||
protected permissionsGroup = this.formBuilder.group({
|
||||
manageAssignedCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
||||
manageAssignedCollections: false,
|
||||
@ -144,6 +149,7 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
private organizationUserService: OrganizationUserService,
|
||||
private dialogService: DialogService,
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
organizationService: OrganizationService,
|
||||
) {
|
||||
this.organization$ = organizationService
|
||||
@ -162,12 +168,43 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
),
|
||||
);
|
||||
|
||||
const userDetails$ = this.params.organizationUserId
|
||||
? this.userService.get(this.params.organizationId, this.params.organizationUserId)
|
||||
: of(null);
|
||||
|
||||
// The orgUser cannot manage their own Group assignments if collection access is restricted
|
||||
// TODO: fix disabled state of access-selector rows so that any controls are hidden
|
||||
this.restrictedAccess$ = combineLatest([
|
||||
this.organization$,
|
||||
userDetails$,
|
||||
this.accountService.activeAccount$,
|
||||
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
||||
]).pipe(
|
||||
map(
|
||||
([organization, userDetails, activeAccount, flexibleCollectionsV1Enabled]) =>
|
||||
// Feature flag conditionals
|
||||
flexibleCollectionsV1Enabled &&
|
||||
organization.flexibleCollections &&
|
||||
// Business logic conditionals
|
||||
userDetails != null &&
|
||||
userDetails.userId == activeAccount.id &&
|
||||
!organization.allowAdminAccessToAllCollectionItems,
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.restrictedAccess$.pipe(takeUntil(this.destroy$)).subscribe((restrictedAccess) => {
|
||||
if (restrictedAccess) {
|
||||
this.formGroup.controls.groups.disable();
|
||||
} else {
|
||||
this.formGroup.controls.groups.enable();
|
||||
}
|
||||
});
|
||||
|
||||
combineLatest({
|
||||
organization: this.organization$,
|
||||
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
||||
userDetails: this.params.organizationUserId
|
||||
? this.userService.get(this.params.organizationId, this.params.organizationUserId)
|
||||
: of(null),
|
||||
userDetails: userDetails$,
|
||||
groups: groups$,
|
||||
})
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
@ -369,7 +406,11 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
userView.collections = this.formGroup.value.access
|
||||
.filter((v) => v.type === AccessItemType.Collection)
|
||||
.map(convertToSelectionView);
|
||||
userView.groups = this.formGroup.value.groups.map((m) => m.id);
|
||||
|
||||
userView.groups = (await firstValueFrom(this.restrictedAccess$))
|
||||
? null
|
||||
: this.formGroup.value.groups.map((m) => m.id);
|
||||
|
||||
userView.accessSecretsManager = this.formGroup.value.accessSecretsManager;
|
||||
|
||||
if (this.editMode) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user