mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
Merge branch 'master' into feature/org-admin-refresh
This commit is contained in:
6
.github/workflows/release-cli.yml
vendored
6
.github/workflows/release-cli.yml
vendored
@@ -287,7 +287,9 @@ jobs:
|
||||
artifacts: bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
|
||||
|
||||
- name: Setup NPM
|
||||
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
|
||||
run: |
|
||||
echo 'registry="https://registry.npmjs.org/"' > ./.npmrc
|
||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ steps.retrieve-secrets.outputs.cli-npm-api-key }}
|
||||
|
||||
@@ -296,5 +298,5 @@ jobs:
|
||||
|
||||
- name: Publish NPM
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
run: npm publish --access public
|
||||
run: npm publish --access public --regsitry=https://registry.npmjs.org/ --userconfig=./.npmrc
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "jest"],
|
||||
"types": ["node", "jest", "chrome"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": ["../src/test.setup.ts", "../apps/src/**/*.spec.ts", "../libs/**/*.spec.ts"],
|
||||
|
||||
@@ -1967,6 +1967,9 @@
|
||||
},
|
||||
"ssoKeyConnectorError": {
|
||||
"message": "Key Connector error: make sure Key Connector is available and working correctly."
|
||||
},
|
||||
"premiumSubcriptionRequired": {
|
||||
"message": "Premium subscription required"
|
||||
},
|
||||
"organizationIsDisabled": {
|
||||
"message": "Organization is disabled."
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import MainBackground from "./background/main.background";
|
||||
import { onCommandListener } from "./listeners/onCommandListener";
|
||||
|
||||
const bitwardenMain = ((window as any).bitwardenMain = new MainBackground());
|
||||
bitwardenMain.bootstrap().then(() => {
|
||||
// Finished bootstrapping
|
||||
});
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
|
||||
if (manifest.manifest_version === 3) {
|
||||
chrome.commands.onCommand.addListener(onCommandListener);
|
||||
} else {
|
||||
const bitwardenMain = ((window as any).bitwardenMain = new MainBackground());
|
||||
bitwardenMain.bootstrap().then(() => {
|
||||
// Finished bootstrapping
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@ export default class ContextMenusBackground {
|
||||
}
|
||||
|
||||
private async cipherAction(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) {
|
||||
if (typeof info.menuItemId !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = info.menuItemId.split("_")[1];
|
||||
|
||||
if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) {
|
||||
|
||||
@@ -31,7 +31,8 @@ import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abs
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "@bitwarden/common/abstractions/usernameGeneration.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
||||
@@ -71,7 +72,8 @@ import { SystemService } from "@bitwarden/common/services/system.service";
|
||||
import { TokenService } from "@bitwarden/common/services/token.service";
|
||||
import { TotpService } from "@bitwarden/common/services/totp.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/services/twoFactor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification.service";
|
||||
import { UserVerificationApiService } from "@bitwarden/common/services/userVerification/userVerification-api.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification/userVerification.service";
|
||||
import { UsernameGenerationService } from "@bitwarden/common/services/usernameGeneration.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service";
|
||||
|
||||
@@ -152,6 +154,7 @@ export default class MainBackground {
|
||||
encryptService: EncryptService;
|
||||
folderApiService: FolderApiServiceAbstraction;
|
||||
policyApiService: PolicyApiServiceAbstraction;
|
||||
userVerificationApiService: UserVerificationApiServiceAbstraction;
|
||||
|
||||
// Passed to the popup for Safari to workaround issues with theming, downloading, etc.
|
||||
backgroundWindow = window;
|
||||
@@ -422,10 +425,12 @@ export default class MainBackground {
|
||||
);
|
||||
this.popupUtilsService = new PopupUtilsService(isPrivateMode);
|
||||
|
||||
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
|
||||
|
||||
this.userVerificationService = new UserVerificationService(
|
||||
this.cryptoService,
|
||||
this.i18nService,
|
||||
this.apiService
|
||||
this.userVerificationApiService
|
||||
);
|
||||
|
||||
const systemUtilsServiceReloadCallback = () => {
|
||||
|
||||
@@ -14,7 +14,10 @@ export default class WebRequestBackground {
|
||||
private cipherService: CipherService,
|
||||
private authService: AuthService
|
||||
) {
|
||||
this.webRequest = (window as any).chrome.webRequest;
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
if (manifest.manifest_version === 2) {
|
||||
this.webRequest = (window as any).chrome.webRequest;
|
||||
}
|
||||
this.isFirefox = platformUtilsService.isFirefox();
|
||||
}
|
||||
|
||||
|
||||
44
apps/browser/src/commands/autoFillActiveTabCommand.ts
Normal file
44
apps/browser/src/commands/autoFillActiveTabCommand.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import AutofillPageDetails from "../models/autofillPageDetails";
|
||||
import { AutofillService } from "../services/abstractions/autofill.service";
|
||||
|
||||
export class AutoFillActiveTabCommand {
|
||||
constructor(private autofillService: AutofillService) {}
|
||||
|
||||
async doAutoFillActiveTabCommand(tab: chrome.tabs.Tab) {
|
||||
if (!tab.id) {
|
||||
throw new Error("Tab does not have an id, cannot complete autofill.");
|
||||
}
|
||||
|
||||
const details = await this.collectPageDetails(tab.id);
|
||||
await this.autofillService.doAutoFillOnTab(
|
||||
[
|
||||
{
|
||||
frameId: 0,
|
||||
tab: tab,
|
||||
details: details,
|
||||
},
|
||||
],
|
||||
tab,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private async collectPageDetails(tabId: number): Promise<AutofillPageDetails> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.tabs.sendMessage(
|
||||
tabId,
|
||||
{
|
||||
command: "collectPageDetailsImmediately",
|
||||
},
|
||||
(response: AutofillPageDetails) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(chrome.runtime.lastError);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
6. Rename com.agilebits.* stuff to com.bitwarden.*
|
||||
7. Remove "some useful globals" on window
|
||||
8. Add ability to autofill span[data-bwautofill] elements
|
||||
9. Add new handler, for new command that responds with page details in response callback
|
||||
*/
|
||||
|
||||
function collect(document, undefined) {
|
||||
@@ -1037,6 +1038,11 @@
|
||||
fill(document, msg.fillScript);
|
||||
sendResponse();
|
||||
return true;
|
||||
} else if (msg.command === 'collectPageDetailsImmediately') {
|
||||
var pageDetails = collect(document);
|
||||
var pageDetailsObj = JSON.parse(pageDetails);
|
||||
sendResponse(pageDetailsObj);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
140
apps/browser/src/listeners/onCommandListener.ts
Normal file
140
apps/browser/src/listeners/onCommandListener.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
import { GlobalState } from "@bitwarden/common/models/domain/globalState";
|
||||
import { AuthService } from "@bitwarden/common/services/auth.service";
|
||||
import { CipherService } from "@bitwarden/common/services/cipher.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
|
||||
import { EncryptService } from "@bitwarden/common/services/encrypt.service";
|
||||
import NoOpEventService from "@bitwarden/common/services/noOpEvent.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service";
|
||||
|
||||
import { AutoFillActiveTabCommand } from "../commands/autoFillActiveTabCommand";
|
||||
import { Account } from "../models/account";
|
||||
import { StateService as AbstractStateService } from "../services/abstractions/state.service";
|
||||
import AutofillService from "../services/autofill.service";
|
||||
import { BrowserCryptoService } from "../services/browserCrypto.service";
|
||||
import BrowserLocalStorageService from "../services/browserLocalStorage.service";
|
||||
import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service";
|
||||
import I18nService from "../services/i18n.service";
|
||||
import { KeyGenerationService } from "../services/keyGeneration.service";
|
||||
import { LocalBackedSessionStorageService } from "../services/localBackedSessionStorage.service";
|
||||
import { StateService } from "../services/state.service";
|
||||
|
||||
export const onCommandListener = async (command: string, tab: chrome.tabs.Tab) => {
|
||||
switch (command) {
|
||||
case "autofill_login":
|
||||
await doAutoFillLogin(tab);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const doAutoFillLogin = async (tab: chrome.tabs.Tab): Promise<void> => {
|
||||
const logService = new ConsoleLogService(false);
|
||||
|
||||
const cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
|
||||
const storageService = new BrowserLocalStorageService();
|
||||
|
||||
const secureStorageService = new BrowserLocalStorageService();
|
||||
|
||||
const memoryStorageService = new LocalBackedSessionStorageService(
|
||||
new EncryptService(cryptoFunctionService, logService, false),
|
||||
new KeyGenerationService(cryptoFunctionService)
|
||||
);
|
||||
|
||||
const stateFactory = new StateFactory(GlobalState, Account);
|
||||
|
||||
const stateMigrationService = new StateMigrationService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
stateFactory
|
||||
);
|
||||
|
||||
const stateService: AbstractStateService = new StateService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
memoryStorageService, // AbstractStorageService
|
||||
logService,
|
||||
stateMigrationService,
|
||||
stateFactory
|
||||
);
|
||||
|
||||
await stateService.init();
|
||||
|
||||
const platformUtils = new BrowserPlatformUtilsService(
|
||||
null, // MessagingService
|
||||
stateService,
|
||||
null, // clipboardWriteCallback
|
||||
null // biometricCallback
|
||||
);
|
||||
|
||||
const cryptoService = new BrowserCryptoService(
|
||||
cryptoFunctionService,
|
||||
null, // AbstractEncryptService
|
||||
platformUtils,
|
||||
logService,
|
||||
stateService
|
||||
);
|
||||
|
||||
const settingsService = new SettingsService(stateService);
|
||||
|
||||
const i18nService = new I18nService(chrome.i18n.getUILanguage());
|
||||
|
||||
await i18nService.init();
|
||||
|
||||
// Don't love this pt.1
|
||||
let searchService: SearchService = null;
|
||||
|
||||
const cipherService = new CipherService(
|
||||
cryptoService,
|
||||
settingsService,
|
||||
null, // ApiService
|
||||
null, // FileUploadService,
|
||||
i18nService,
|
||||
() => searchService, // Don't love this pt.2
|
||||
logService,
|
||||
stateService
|
||||
);
|
||||
|
||||
// Don't love this pt.3
|
||||
searchService = new SearchService(cipherService, logService, i18nService);
|
||||
|
||||
// TODO: Remove this before we encourage anyone to start using this
|
||||
const eventService = new NoOpEventService();
|
||||
|
||||
const autofillService = new AutofillService(
|
||||
cipherService,
|
||||
stateService,
|
||||
null, // TotpService
|
||||
eventService,
|
||||
logService
|
||||
);
|
||||
|
||||
const authService = new AuthService(
|
||||
cryptoService, // CryptoService
|
||||
null, // ApiService
|
||||
null, // TokenService
|
||||
null, // AppIdService
|
||||
platformUtils,
|
||||
null, // MessagingService
|
||||
logService,
|
||||
null, // KeyConnectorService
|
||||
null, // EnvironmentService
|
||||
stateService,
|
||||
null, // TwoFactorService
|
||||
i18nService
|
||||
);
|
||||
|
||||
const authStatus = await authService.getAuthStatus();
|
||||
if (authStatus < AuthenticationStatus.Unlocked) {
|
||||
// TODO: Add back in unlock on autofill
|
||||
logService.info("Currently not unlocked, MV3 does not support unlock on autofill currently.");
|
||||
return;
|
||||
}
|
||||
|
||||
const command = new AutoFillActiveTabCommand(autofillService);
|
||||
await command.doAutoFillActiveTabCommand(tab);
|
||||
};
|
||||
@@ -47,7 +47,8 @@
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"action": {
|
||||
"default_icon": {
|
||||
|
||||
@@ -22,4 +22,5 @@ export default class AutofillField {
|
||||
selectInfo: any;
|
||||
maxLength: number;
|
||||
tagName: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
// Clear them aggressively to make sure this doesn't occur
|
||||
await this.clearComponentStates();
|
||||
|
||||
this.stateService.activeAccount.pipe(takeUntil(this.destroy$)).subscribe((userId) => {
|
||||
this.stateService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((userId) => {
|
||||
this.activeUserId = userId;
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.stateService.activeAccount.getValue() == null) {
|
||||
if (this.activeUserId === null) {
|
||||
this.router.navigate(["home"]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||
import { TokenService } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { UsernameGenerationService } from "@bitwarden/common/abstractions/usernameGeneration.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout.service";
|
||||
import { AuthService } from "@bitwarden/common/services/auth.service";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
@Component({
|
||||
selector: "app-export",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" routerLink="/tabs/settings">
|
||||
<button type="button" (click)="goBack()">
|
||||
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CurrencyPipe } from "@angular/common";
|
||||
import { CurrencyPipe, Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/components/premium.component";
|
||||
@@ -21,6 +21,7 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
apiService: ApiService,
|
||||
stateService: StateService,
|
||||
logService: LogService,
|
||||
private location: Location,
|
||||
private currencyPipe: CurrencyPipe
|
||||
) {
|
||||
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
||||
@@ -32,4 +33,8 @@ export class PremiumComponent extends BasePremiumComponent {
|
||||
this.priceString = this.priceString.replace("%price%", thePrice);
|
||||
}
|
||||
}
|
||||
|
||||
goBack() {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
<div class="no-items">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<img class="no-items-image" aria-hidden="true" />
|
||||
<p>{{ "noItemsInList" | i18n }}</p>
|
||||
<button type="button" (click)="addCipher()" class="btn block primary link">
|
||||
{{ "addItem" | i18n }}
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
<div
|
||||
class="box-content-row box-content-row-flex totp"
|
||||
[ngClass]="{ low: totpLow }"
|
||||
*ngIf="cipher.login.totp && totpCode"
|
||||
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span
|
||||
@@ -177,6 +177,20 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="box-content-row box-content-row-flex totp"
|
||||
*ngIf="cipher.login.totp && !canAccessPremium"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
|
||||
<span class="row-label">
|
||||
<a routerLink="/premium">
|
||||
{{ "premiumSubcriptionRequired" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.card">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
|
||||
export default abstract class AbstractChromeStorageService implements AbstractStorageService {
|
||||
protected abstract chromeStorageApi: any;
|
||||
protected abstract chromeStorageApi: chrome.storage.StorageArea;
|
||||
|
||||
async get<T>(key: string): Promise<T> {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -1,7 +1,41 @@
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import AutofillField from "../../models/autofillField";
|
||||
import AutofillForm from "../../models/autofillForm";
|
||||
import AutofillPageDetails from "../../models/autofillPageDetails";
|
||||
|
||||
export abstract class AutofillService {
|
||||
getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => any[];
|
||||
doAutoFill: (options: any) => Promise<string>;
|
||||
doAutoFillActiveTab: (pageDetails: any, fromCommand: boolean) => Promise<string>;
|
||||
export interface PageDetail {
|
||||
frameId: number;
|
||||
tab: chrome.tabs.Tab;
|
||||
details: AutofillPageDetails;
|
||||
}
|
||||
|
||||
export interface AutoFillOptions {
|
||||
cipher: CipherView;
|
||||
pageDetails: PageDetail[];
|
||||
doc?: typeof window.document;
|
||||
tab: chrome.tabs.Tab;
|
||||
skipUsernameOnlyFill?: boolean;
|
||||
onlyEmptyFields?: boolean;
|
||||
onlyVisibleFields?: boolean;
|
||||
fillNewPassword?: boolean;
|
||||
skipLastUsed?: boolean;
|
||||
}
|
||||
|
||||
export interface FormData {
|
||||
form: AutofillForm;
|
||||
password: AutofillField;
|
||||
username: AutofillField;
|
||||
passwords: AutofillField[];
|
||||
}
|
||||
|
||||
export abstract class AutofillService {
|
||||
getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => FormData[];
|
||||
doAutoFill: (options: AutoFillOptions) => Promise<string>;
|
||||
doAutoFillOnTab: (
|
||||
pageDetails: PageDetail[],
|
||||
tab: chrome.tabs.Tab,
|
||||
fromCommand: boolean
|
||||
) => Promise<string>;
|
||||
doAutoFillActiveTab: (pageDetails: PageDetail[], fromCommand: boolean) => Promise<string>;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,26 @@ import AutofillPageDetails from "../models/autofillPageDetails";
|
||||
import AutofillScript from "../models/autofillScript";
|
||||
import { StateService } from "../services/abstractions/state.service";
|
||||
|
||||
import { AutofillService as AutofillServiceInterface } from "./abstractions/autofill.service";
|
||||
import {
|
||||
AutoFillOptions,
|
||||
AutofillService as AutofillServiceInterface,
|
||||
PageDetail,
|
||||
FormData,
|
||||
} from "./abstractions/autofill.service";
|
||||
import {
|
||||
AutoFillConstants,
|
||||
CreditCardAutoFillConstants,
|
||||
IdentityAutoFillConstants,
|
||||
} from "./autofillConstants";
|
||||
|
||||
export interface GenerateFillScriptOptions {
|
||||
skipUsernameOnlyFill: boolean;
|
||||
onlyEmptyFields: boolean;
|
||||
onlyVisibleFields: boolean;
|
||||
fillNewPassword: boolean;
|
||||
cipher: CipherView;
|
||||
}
|
||||
|
||||
export default class AutofillService implements AutofillServiceInterface {
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
@@ -31,10 +44,16 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] {
|
||||
const formData: any[] = [];
|
||||
getFormsWithPasswordFields(pageDetails: AutofillPageDetails): FormData[] {
|
||||
const formData: FormData[] = [];
|
||||
|
||||
const passwordFields = this.loadPasswordFields(pageDetails, true, true, false, false);
|
||||
const passwordFields = AutofillService.loadPasswordFields(
|
||||
pageDetails,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
);
|
||||
if (passwordFields.length === 0) {
|
||||
return formData;
|
||||
}
|
||||
@@ -64,16 +83,17 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return formData;
|
||||
}
|
||||
|
||||
async doAutoFill(options: any) {
|
||||
let totpPromise: Promise<string> = null;
|
||||
async doAutoFill(options: AutoFillOptions) {
|
||||
const tab = options.tab;
|
||||
if (!tab || !options.cipher || !options.pageDetails || !options.pageDetails.length) {
|
||||
throw new Error("Nothing to auto-fill.");
|
||||
}
|
||||
|
||||
let totpPromise: Promise<string> = null;
|
||||
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
let didAutofill = false;
|
||||
options.pageDetails.forEach((pd: any) => {
|
||||
options.pageDetails.forEach((pd) => {
|
||||
// make sure we're still on correct tab
|
||||
if (pd.tab.id !== tab.id || pd.tab.url !== tab.url) {
|
||||
return;
|
||||
@@ -138,12 +158,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async doAutoFillActiveTab(pageDetails: any, fromCommand: boolean) {
|
||||
const tab = await this.getActiveTab();
|
||||
if (!tab || !tab.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
async doAutoFillOnTab(pageDetails: PageDetail[], tab: chrome.tabs.Tab, fromCommand: boolean) {
|
||||
let cipher: CipherView;
|
||||
if (fromCommand) {
|
||||
cipher = await this.cipherService.getNextCipherForUrl(tab.url);
|
||||
@@ -186,9 +201,18 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return totpCode;
|
||||
}
|
||||
|
||||
async doAutoFillActiveTab(pageDetails: PageDetail[], fromCommand: boolean) {
|
||||
const tab = await this.getActiveTab();
|
||||
if (!tab || !tab.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await this.doAutoFillOnTab(pageDetails, tab, fromCommand);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private async getActiveTab(): Promise<any> {
|
||||
private async getActiveTab(): Promise<chrome.tabs.Tab> {
|
||||
const tab = await BrowserApi.getTabFromCurrentWindow();
|
||||
if (!tab) {
|
||||
throw new Error("No tab found.");
|
||||
@@ -197,7 +221,10 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return tab;
|
||||
}
|
||||
|
||||
private generateFillScript(pageDetails: AutofillPageDetails, options: any): AutofillScript {
|
||||
private generateFillScript(
|
||||
pageDetails: AutofillPageDetails,
|
||||
options: GenerateFillScriptOptions
|
||||
): AutofillScript {
|
||||
if (!pageDetails || !options.cipher) {
|
||||
return null;
|
||||
}
|
||||
@@ -209,13 +236,13 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
if (fields && fields.length) {
|
||||
const fieldNames: string[] = [];
|
||||
|
||||
fields.forEach((f: any) => {
|
||||
if (this.hasValue(f.name)) {
|
||||
fields.forEach((f) => {
|
||||
if (AutofillService.hasValue(f.name)) {
|
||||
fieldNames.push(f.name.toLowerCase());
|
||||
}
|
||||
});
|
||||
|
||||
pageDetails.fields.forEach((field: any) => {
|
||||
pageDetails.fields.forEach((field) => {
|
||||
// eslint-disable-next-line
|
||||
if (filledFields.hasOwnProperty(field.opid)) {
|
||||
return;
|
||||
@@ -228,10 +255,10 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
const matchingIndex = this.findMatchingFieldIndex(field, fieldNames);
|
||||
if (matchingIndex > -1) {
|
||||
const matchingField: FieldView = fields[matchingIndex];
|
||||
let val;
|
||||
let val: string;
|
||||
if (matchingField.type === FieldType.Linked) {
|
||||
// Assumption: Linked Field is not being used to autofill a boolean value
|
||||
val = options.cipher.linkedFieldValue(matchingField.linkedId);
|
||||
val = options.cipher.linkedFieldValue(matchingField.linkedId) as string;
|
||||
} else {
|
||||
val = matchingField.value;
|
||||
if (val == null && matchingField.type === FieldType.Boolean) {
|
||||
@@ -240,7 +267,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
filledFields[field.opid] = field;
|
||||
this.fillByOpid(fillScript, field, val);
|
||||
AutofillService.fillByOpid(fillScript, field, val);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -269,9 +296,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
private generateLoginFillScript(
|
||||
fillScript: AutofillScript,
|
||||
pageDetails: any,
|
||||
pageDetails: AutofillPageDetails,
|
||||
filledFields: { [id: string]: AutofillField },
|
||||
options: any
|
||||
options: GenerateFillScriptOptions
|
||||
): AutofillScript {
|
||||
if (!options.cipher.login) {
|
||||
return null;
|
||||
@@ -285,11 +312,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
if (!login.password || login.password === "") {
|
||||
// No password for this login. Maybe they just wanted to auto-fill some custom fields?
|
||||
fillScript = this.setFillScriptForFocus(filledFields, fillScript);
|
||||
fillScript = AutofillService.setFillScriptForFocus(filledFields, fillScript);
|
||||
return fillScript;
|
||||
}
|
||||
|
||||
let passwordFields = this.loadPasswordFields(
|
||||
let passwordFields = AutofillService.loadPasswordFields(
|
||||
pageDetails,
|
||||
false,
|
||||
false,
|
||||
@@ -298,7 +325,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
);
|
||||
if (!passwordFields.length && !options.onlyVisibleFields) {
|
||||
// not able to find any viewable password fields. maybe there are some "hidden" ones?
|
||||
passwordFields = this.loadPasswordFields(
|
||||
passwordFields = AutofillService.loadPasswordFields(
|
||||
pageDetails,
|
||||
true,
|
||||
true,
|
||||
@@ -362,11 +389,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
if (!passwordFields.length && !options.skipUsernameOnlyFill) {
|
||||
// No password fields on this page. Let's try to just fuzzy fill the username.
|
||||
pageDetails.fields.forEach((f: any) => {
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (
|
||||
f.viewable &&
|
||||
(f.type === "text" || f.type === "email" || f.type === "tel") &&
|
||||
this.fieldIsFuzzyMatch(f, AutoFillConstants.UsernameFieldNames)
|
||||
AutofillService.fieldIsFuzzyMatch(f, AutoFillConstants.UsernameFieldNames)
|
||||
) {
|
||||
usernames.push(f);
|
||||
}
|
||||
@@ -380,7 +407,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
filledFields[u.opid] = u;
|
||||
this.fillByOpid(fillScript, u, login.username);
|
||||
AutofillService.fillByOpid(fillScript, u, login.username);
|
||||
});
|
||||
|
||||
passwords.forEach((p) => {
|
||||
@@ -390,18 +417,18 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
filledFields[p.opid] = p;
|
||||
this.fillByOpid(fillScript, p, login.password);
|
||||
AutofillService.fillByOpid(fillScript, p, login.password);
|
||||
});
|
||||
|
||||
fillScript = this.setFillScriptForFocus(filledFields, fillScript);
|
||||
fillScript = AutofillService.setFillScriptForFocus(filledFields, fillScript);
|
||||
return fillScript;
|
||||
}
|
||||
|
||||
private generateCardFillScript(
|
||||
fillScript: AutofillScript,
|
||||
pageDetails: any,
|
||||
pageDetails: AutofillPageDetails,
|
||||
filledFields: { [id: string]: AutofillField },
|
||||
options: any
|
||||
options: GenerateFillScriptOptions
|
||||
): AutofillScript {
|
||||
if (!options.cipher.card) {
|
||||
return null;
|
||||
@@ -409,8 +436,8 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
const fillFields: { [id: string]: AutofillField } = {};
|
||||
|
||||
pageDetails.fields.forEach((f: any) => {
|
||||
if (this.forCustomFieldsOnly(f)) {
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (AutofillService.forCustomFieldsOnly(f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -429,7 +456,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
// ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/
|
||||
if (
|
||||
!fillFields.cardholderName &&
|
||||
this.isFieldMatch(
|
||||
AutofillService.isFieldMatch(
|
||||
f[attr],
|
||||
CreditCardAutoFillConstants.CardHolderFieldNames,
|
||||
CreditCardAutoFillConstants.CardHolderFieldNameValues
|
||||
@@ -439,7 +466,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.number &&
|
||||
this.isFieldMatch(
|
||||
AutofillService.isFieldMatch(
|
||||
f[attr],
|
||||
CreditCardAutoFillConstants.CardNumberFieldNames,
|
||||
CreditCardAutoFillConstants.CardNumberFieldNameValues
|
||||
@@ -449,7 +476,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.exp &&
|
||||
this.isFieldMatch(
|
||||
AutofillService.isFieldMatch(
|
||||
f[attr],
|
||||
CreditCardAutoFillConstants.CardExpiryFieldNames,
|
||||
CreditCardAutoFillConstants.CardExpiryFieldNameValues
|
||||
@@ -459,25 +486,25 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.expMonth &&
|
||||
this.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryMonthFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryMonthFieldNames)
|
||||
) {
|
||||
fillFields.expMonth = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.expYear &&
|
||||
this.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryYearFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.ExpiryYearFieldNames)
|
||||
) {
|
||||
fillFields.expYear = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.code &&
|
||||
this.isFieldMatch(f[attr], CreditCardAutoFillConstants.CVVFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.CVVFieldNames)
|
||||
) {
|
||||
fillFields.code = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.brand &&
|
||||
this.isFieldMatch(f[attr], CreditCardAutoFillConstants.CardBrandFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], CreditCardAutoFillConstants.CardBrandFieldNames)
|
||||
) {
|
||||
fillFields.brand = f;
|
||||
break;
|
||||
@@ -491,7 +518,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
this.makeScriptAction(fillScript, card, fillFields, filledFields, "code");
|
||||
this.makeScriptAction(fillScript, card, fillFields, filledFields, "brand");
|
||||
|
||||
if (fillFields.expMonth && this.hasValue(card.expMonth)) {
|
||||
if (fillFields.expMonth && AutofillService.hasValue(card.expMonth)) {
|
||||
let expMonth: string = card.expMonth;
|
||||
|
||||
if (fillFields.expMonth.selectInfo && fillFields.expMonth.selectInfo.options) {
|
||||
@@ -526,10 +553,10 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
filledFields[fillFields.expMonth.opid] = fillFields.expMonth;
|
||||
this.fillByOpid(fillScript, fillFields.expMonth, expMonth);
|
||||
AutofillService.fillByOpid(fillScript, fillFields.expMonth, expMonth);
|
||||
}
|
||||
|
||||
if (fillFields.expYear && this.hasValue(card.expYear)) {
|
||||
if (fillFields.expYear && AutofillService.hasValue(card.expYear)) {
|
||||
let expYear: string = card.expYear;
|
||||
if (fillFields.expYear.selectInfo && fillFields.expYear.selectInfo.options) {
|
||||
for (let i = 0; i < fillFields.expYear.selectInfo.options.length; i++) {
|
||||
@@ -572,10 +599,14 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
filledFields[fillFields.expYear.opid] = fillFields.expYear;
|
||||
this.fillByOpid(fillScript, fillFields.expYear, expYear);
|
||||
AutofillService.fillByOpid(fillScript, fillFields.expYear, expYear);
|
||||
}
|
||||
|
||||
if (fillFields.exp && this.hasValue(card.expMonth) && this.hasValue(card.expYear)) {
|
||||
if (
|
||||
fillFields.exp &&
|
||||
AutofillService.hasValue(card.expMonth) &&
|
||||
AutofillService.hasValue(card.expYear)
|
||||
) {
|
||||
const fullMonth = ("0" + card.expMonth).slice(-2);
|
||||
|
||||
let fullYear: string = card.expYear;
|
||||
@@ -712,7 +743,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return fillScript;
|
||||
}
|
||||
|
||||
private fieldAttrsContain(field: any, containsVal: string) {
|
||||
private fieldAttrsContain(field: AutofillField, containsVal: string) {
|
||||
if (!field) {
|
||||
return false;
|
||||
}
|
||||
@@ -734,9 +765,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
private generateIdentityFillScript(
|
||||
fillScript: AutofillScript,
|
||||
pageDetails: any,
|
||||
pageDetails: AutofillPageDetails,
|
||||
filledFields: { [id: string]: AutofillField },
|
||||
options: any
|
||||
options: GenerateFillScriptOptions
|
||||
): AutofillScript {
|
||||
if (!options.cipher.identity) {
|
||||
return null;
|
||||
@@ -744,8 +775,8 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
const fillFields: { [id: string]: AutofillField } = {};
|
||||
|
||||
pageDetails.fields.forEach((f: any) => {
|
||||
if (this.forCustomFieldsOnly(f)) {
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (AutofillService.forCustomFieldsOnly(f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -764,7 +795,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
// ref https://developers.google.com/web/fundamentals/design-and-ux/input/forms/
|
||||
if (
|
||||
!fillFields.name &&
|
||||
this.isFieldMatch(
|
||||
AutofillService.isFieldMatch(
|
||||
f[attr],
|
||||
IdentityAutoFillConstants.FullNameFieldNames,
|
||||
IdentityAutoFillConstants.FullNameFieldNameValues
|
||||
@@ -774,37 +805,37 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.firstName &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.FirstnameFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.FirstnameFieldNames)
|
||||
) {
|
||||
fillFields.firstName = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.middleName &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.MiddlenameFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.MiddlenameFieldNames)
|
||||
) {
|
||||
fillFields.middleName = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.lastName &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.LastnameFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.LastnameFieldNames)
|
||||
) {
|
||||
fillFields.lastName = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.title &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.TitleFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.TitleFieldNames)
|
||||
) {
|
||||
fillFields.title = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.email &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.EmailFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.EmailFieldNames)
|
||||
) {
|
||||
fillFields.email = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.address &&
|
||||
this.isFieldMatch(
|
||||
AutofillService.isFieldMatch(
|
||||
f[attr],
|
||||
IdentityAutoFillConstants.AddressFieldNames,
|
||||
IdentityAutoFillConstants.AddressFieldNameValues
|
||||
@@ -814,61 +845,61 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.address1 &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address1FieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address1FieldNames)
|
||||
) {
|
||||
fillFields.address1 = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.address2 &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address2FieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address2FieldNames)
|
||||
) {
|
||||
fillFields.address2 = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.address3 &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.Address3FieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.Address3FieldNames)
|
||||
) {
|
||||
fillFields.address3 = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.postalCode &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.PostalCodeFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.PostalCodeFieldNames)
|
||||
) {
|
||||
fillFields.postalCode = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.city &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.CityFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CityFieldNames)
|
||||
) {
|
||||
fillFields.city = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.state &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.StateFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.StateFieldNames)
|
||||
) {
|
||||
fillFields.state = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.country &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.CountryFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CountryFieldNames)
|
||||
) {
|
||||
fillFields.country = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.phone &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.PhoneFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.PhoneFieldNames)
|
||||
) {
|
||||
fillFields.phone = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.username &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.UserNameFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.UserNameFieldNames)
|
||||
) {
|
||||
fillFields.username = f;
|
||||
break;
|
||||
} else if (
|
||||
!fillFields.company &&
|
||||
this.isFieldMatch(f[attr], IdentityAutoFillConstants.CompanyFieldNames)
|
||||
AutofillService.isFieldMatch(f[attr], IdentityAutoFillConstants.CompanyFieldNames)
|
||||
) {
|
||||
fillFields.company = f;
|
||||
break;
|
||||
@@ -923,16 +954,16 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
if (fillFields.name && (identity.firstName || identity.lastName)) {
|
||||
let fullName = "";
|
||||
if (this.hasValue(identity.firstName)) {
|
||||
if (AutofillService.hasValue(identity.firstName)) {
|
||||
fullName = identity.firstName;
|
||||
}
|
||||
if (this.hasValue(identity.middleName)) {
|
||||
if (AutofillService.hasValue(identity.middleName)) {
|
||||
if (fullName !== "") {
|
||||
fullName += " ";
|
||||
}
|
||||
fullName += identity.middleName;
|
||||
}
|
||||
if (this.hasValue(identity.lastName)) {
|
||||
if (AutofillService.hasValue(identity.lastName)) {
|
||||
if (fullName !== "") {
|
||||
fullName += " ";
|
||||
}
|
||||
@@ -942,18 +973,18 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
this.makeScriptActionWithValue(fillScript, fullName, fillFields.name, filledFields);
|
||||
}
|
||||
|
||||
if (fillFields.address && this.hasValue(identity.address1)) {
|
||||
if (fillFields.address && AutofillService.hasValue(identity.address1)) {
|
||||
let address = "";
|
||||
if (this.hasValue(identity.address1)) {
|
||||
if (AutofillService.hasValue(identity.address1)) {
|
||||
address = identity.address1;
|
||||
}
|
||||
if (this.hasValue(identity.address2)) {
|
||||
if (AutofillService.hasValue(identity.address2)) {
|
||||
if (address !== "") {
|
||||
address += ", ";
|
||||
}
|
||||
address += identity.address2;
|
||||
}
|
||||
if (this.hasValue(identity.address3)) {
|
||||
if (AutofillService.hasValue(identity.address3)) {
|
||||
if (address !== "") {
|
||||
address += ", ";
|
||||
}
|
||||
@@ -970,7 +1001,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return excludedTypes.indexOf(type) > -1;
|
||||
}
|
||||
|
||||
private isFieldMatch(value: string, options: string[], containsOptions?: string[]): boolean {
|
||||
private static isFieldMatch(
|
||||
value: string,
|
||||
options: string[],
|
||||
containsOptions?: string[]
|
||||
): boolean {
|
||||
value = value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
@@ -1011,12 +1046,15 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
filledFields: { [id: string]: AutofillField }
|
||||
) {
|
||||
let doFill = false;
|
||||
if (this.hasValue(dataValue) && field) {
|
||||
if (AutofillService.hasValue(dataValue) && field) {
|
||||
if (field.type === "select-one" && field.selectInfo && field.selectInfo.options) {
|
||||
for (let i = 0; i < field.selectInfo.options.length; i++) {
|
||||
const option = field.selectInfo.options[i];
|
||||
for (let j = 0; j < option.length; j++) {
|
||||
if (this.hasValue(option[j]) && option[j].toLowerCase() === dataValue.toLowerCase()) {
|
||||
if (
|
||||
AutofillService.hasValue(option[j]) &&
|
||||
option[j].toLowerCase() === dataValue.toLowerCase()
|
||||
) {
|
||||
doFill = true;
|
||||
if (option.length > 1) {
|
||||
dataValue = option[1];
|
||||
@@ -1036,11 +1074,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
if (doFill) {
|
||||
filledFields[field.opid] = field;
|
||||
this.fillByOpid(fillScript, field, dataValue);
|
||||
AutofillService.fillByOpid(fillScript, field, dataValue);
|
||||
}
|
||||
}
|
||||
|
||||
private loadPasswordFields(
|
||||
static loadPasswordFields(
|
||||
pageDetails: AutofillPageDetails,
|
||||
canBeHidden: boolean,
|
||||
canBeReadOnly: boolean,
|
||||
@@ -1049,7 +1087,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
) {
|
||||
const arr: AutofillField[] = [];
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (this.forCustomFieldsOnly(f)) {
|
||||
if (AutofillService.forCustomFieldsOnly(f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1111,7 +1149,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
let usernameField: AutofillField = null;
|
||||
for (let i = 0; i < pageDetails.fields.length; i++) {
|
||||
const f = pageDetails.fields[i];
|
||||
if (this.forCustomFieldsOnly(f)) {
|
||||
if (AutofillService.forCustomFieldsOnly(f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1195,7 +1233,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
|
||||
private fieldPropertyIsMatch(field: any, property: string, name: string): boolean {
|
||||
let fieldVal = field[property] as string;
|
||||
if (!this.hasValue(fieldVal)) {
|
||||
if (!AutofillService.hasValue(fieldVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1227,33 +1265,45 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return fieldVal.toLowerCase() === name;
|
||||
}
|
||||
|
||||
private fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean {
|
||||
if (this.hasValue(field.htmlID) && this.fuzzyMatch(names, field.htmlID)) {
|
||||
static fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean {
|
||||
if (AutofillService.hasValue(field.htmlID) && this.fuzzyMatch(names, field.htmlID)) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field.htmlName) && this.fuzzyMatch(names, field.htmlName)) {
|
||||
if (AutofillService.hasValue(field.htmlName) && this.fuzzyMatch(names, field.htmlName)) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field["label-tag"]) && this.fuzzyMatch(names, field["label-tag"])) {
|
||||
if (
|
||||
AutofillService.hasValue(field["label-tag"]) &&
|
||||
this.fuzzyMatch(names, field["label-tag"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field.placeholder) && this.fuzzyMatch(names, field.placeholder)) {
|
||||
if (AutofillService.hasValue(field.placeholder) && this.fuzzyMatch(names, field.placeholder)) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field["label-left"]) && this.fuzzyMatch(names, field["label-left"])) {
|
||||
if (
|
||||
AutofillService.hasValue(field["label-left"]) &&
|
||||
this.fuzzyMatch(names, field["label-left"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field["label-top"]) && this.fuzzyMatch(names, field["label-top"])) {
|
||||
if (
|
||||
AutofillService.hasValue(field["label-top"]) &&
|
||||
this.fuzzyMatch(names, field["label-top"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasValue(field["label-aria"]) && this.fuzzyMatch(names, field["label-aria"])) {
|
||||
if (
|
||||
AutofillService.hasValue(field["label-aria"]) &&
|
||||
this.fuzzyMatch(names, field["label-aria"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private fuzzyMatch(options: string[], value: string): boolean {
|
||||
private static fuzzyMatch(options: string[], value: string): boolean {
|
||||
if (options == null || options.length === 0 || value == null || value === "") {
|
||||
return false;
|
||||
}
|
||||
@@ -1272,11 +1322,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
private hasValue(str: string): boolean {
|
||||
static hasValue(str: string): boolean {
|
||||
return str && str !== "";
|
||||
}
|
||||
|
||||
private setFillScriptForFocus(
|
||||
static setFillScriptForFocus(
|
||||
filledFields: { [id: string]: AutofillField },
|
||||
fillScript: AutofillScript
|
||||
): AutofillScript {
|
||||
@@ -1304,7 +1354,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return fillScript;
|
||||
}
|
||||
|
||||
private fillByOpid(fillScript: AutofillScript, field: AutofillField, value: string): void {
|
||||
static fillByOpid(fillScript: AutofillScript, field: AutofillField, value: string): void {
|
||||
if (field.maxLength && value && value.length > field.maxLength) {
|
||||
value = value.substr(0, value.length);
|
||||
}
|
||||
@@ -1315,7 +1365,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
fillScript.script.push(["fill_by_opid", field.opid, value]);
|
||||
}
|
||||
|
||||
private forCustomFieldsOnly(field: AutofillField): boolean {
|
||||
static forCustomFieldsOnly(field: AutofillField): boolean {
|
||||
return field.tagName === "span";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AbstractChromeStorageService from "./abstractChromeStorageApi.service";
|
||||
|
||||
export default class BrowserLocalStorageService extends AbstractChromeStorageService {
|
||||
protected chromeStorageApi: any = chrome.storage.local;
|
||||
protected chromeStorageApi = chrome.storage.local;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AbstractChromeStorageService from "./abstractChromeStorageApi.service";
|
||||
|
||||
export default class BrowserMemoryStorageService extends AbstractChromeStorageService {
|
||||
protected chromeStorageApi: any = (chrome.storage as any).session;
|
||||
protected chromeStorageApi = chrome.storage.session;
|
||||
}
|
||||
|
||||
@@ -176,13 +176,6 @@ const config = {
|
||||
return chunk.name === "popup/main";
|
||||
},
|
||||
},
|
||||
commons2: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: "vendor",
|
||||
chunks: (chunk) => {
|
||||
return chunk.name === "background";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -209,4 +202,16 @@ const config = {
|
||||
plugins: plugins,
|
||||
};
|
||||
|
||||
if (manifestVersion == 2) {
|
||||
// We can't use this in manifest v3
|
||||
// Ideally we understand why this breaks it and we don't have to do this
|
||||
config.optimization.splitChunks.cacheGroups.commons2 = {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: "vendor",
|
||||
chunks: (chunk) => {
|
||||
return chunk.name === "background";
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -42,7 +42,8 @@ import { SyncService } from "@bitwarden/common/services/sync.service";
|
||||
import { TokenService } from "@bitwarden/common/services/token.service";
|
||||
import { TotpService } from "@bitwarden/common/services/totp.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/services/twoFactor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification.service";
|
||||
import { UserVerificationApiService } from "@bitwarden/common/services/userVerification/userVerification-api.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification/userVerification.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout.service";
|
||||
import { CliPlatformUtilsService } from "@bitwarden/node/cli/services/cliPlatformUtils.service";
|
||||
import { ConsoleLogService } from "@bitwarden/node/cli/services/consoleLog.service";
|
||||
@@ -106,6 +107,7 @@ export class Main {
|
||||
twoFactorService: TwoFactorService;
|
||||
broadcasterService: BroadcasterService;
|
||||
folderApiService: FolderApiService;
|
||||
userVerificationApiService: UserVerificationApiService;
|
||||
|
||||
constructor() {
|
||||
let p = null;
|
||||
@@ -330,10 +332,13 @@ export class Main {
|
||||
this.program = new Program(this);
|
||||
this.vaultProgram = new VaultProgram(this);
|
||||
this.sendProgram = new SendProgram(this);
|
||||
|
||||
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
|
||||
|
||||
this.userVerificationService = new UserVerificationService(
|
||||
this.cryptoService,
|
||||
this.i18nService,
|
||||
this.apiService
|
||||
this.userVerificationApiService
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||
import { Component, NgZone } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
@@ -22,7 +22,7 @@ const BroadcasterSubscriptionId = "LockComponent";
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
})
|
||||
export class LockComponent extends BaseLockComponent implements OnDestroy {
|
||||
export class LockComponent extends BaseLockComponent {
|
||||
private deferFocus: boolean = null;
|
||||
authenicatedUrl = "vault";
|
||||
unAuthenicatedUrl = "update-temp-password";
|
||||
@@ -103,6 +103,7 @@ export class LockComponent extends BaseLockComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SecurityContext,
|
||||
Type,
|
||||
@@ -77,7 +78,7 @@ const systemTimeoutOptions = {
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef;
|
||||
@ViewChild("premium", { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef;
|
||||
@ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
|
||||
@@ -129,7 +130,7 @@ export class AppComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.stateService.activeAccount.pipe(takeUntil(this.destroy$)).subscribe((userId) => {
|
||||
this.stateService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((userId) => {
|
||||
this.activeUserId = userId;
|
||||
});
|
||||
|
||||
|
||||
@@ -8,15 +8,16 @@ export type SearchBarState = {
|
||||
|
||||
@Injectable()
|
||||
export class SearchBarService {
|
||||
searchText = new BehaviorSubject<string>(null);
|
||||
private searchTextSubject = new BehaviorSubject<string>(null);
|
||||
searchText$ = this.searchTextSubject.asObservable();
|
||||
|
||||
private _state = {
|
||||
enabled: false,
|
||||
placeholderText: "",
|
||||
};
|
||||
|
||||
// tslint:disable-next-line:member-ordering
|
||||
state = new BehaviorSubject<SearchBarState>(this._state);
|
||||
private stateSubject = new BehaviorSubject<SearchBarState>(this._state);
|
||||
state$ = this.stateSubject.asObservable();
|
||||
|
||||
setEnabled(enabled: boolean) {
|
||||
this._state.enabled = enabled;
|
||||
@@ -29,10 +30,10 @@ export class SearchBarService {
|
||||
}
|
||||
|
||||
setSearchText(value: string) {
|
||||
this.searchText.next(value);
|
||||
this.searchTextSubject.next(value);
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
this.state.next(this._state);
|
||||
this.stateSubject.next(this._state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { UntypedFormControl } from "@angular/forms";
|
||||
import { Subscription } from "rxjs";
|
||||
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
|
||||
@@ -13,8 +14,10 @@ export class SearchComponent implements OnInit, OnDestroy {
|
||||
state: SearchBarState;
|
||||
searchText: UntypedFormControl = new UntypedFormControl(null);
|
||||
|
||||
private activeAccountSubscription: Subscription;
|
||||
|
||||
constructor(private searchBarService: SearchBarService, private stateService: StateService) {
|
||||
this.searchBarService.state.subscribe((state) => {
|
||||
this.searchBarService.state$.subscribe((state) => {
|
||||
this.state = state;
|
||||
});
|
||||
|
||||
@@ -24,13 +27,13 @@ export class SearchComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.stateService.activeAccount.subscribe((value) => {
|
||||
this.activeAccountSubscription = this.stateService.activeAccount$.subscribe((value) => {
|
||||
this.searchBarService.setSearchText("");
|
||||
this.searchText.patchValue("");
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.stateService.activeAccount.unsubscribe();
|
||||
this.activeAccountSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro
|
||||
policyService,
|
||||
logService
|
||||
);
|
||||
this.searchBarService.searchText.subscribe((searchText) => {
|
||||
this.searchBarService.searchText$.subscribe((searchText) => {
|
||||
this.searchText = searchText;
|
||||
this.searchTextChanged();
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
constructor(searchService: SearchService, searchBarService: SearchBarService) {
|
||||
super(searchService);
|
||||
|
||||
searchBarService.searchText.subscribe((searchText) => {
|
||||
searchBarService.searchText$.subscribe((searchText) => {
|
||||
this.searchText = searchText;
|
||||
this.search(200);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
const BroadcasterSubscriptionId = "ExportComponent";
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<div
|
||||
class="box-content-row box-content-row-flex totp"
|
||||
[ngClass]="{ low: totpLow }"
|
||||
*ngIf="cipher.login.totp && totpCode"
|
||||
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span
|
||||
@@ -138,6 +138,19 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row box-content-row-flex totp"
|
||||
*ngIf="cipher.login.totp && !canAccessPremium"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
|
||||
<span class="row-label">
|
||||
<a [routerLink]="" (click)="showGetPremium()"
|
||||
>{{ "premiumSubcriptionRequired" | i18n }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.card">
|
||||
|
||||
@@ -115,4 +115,10 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showGetPremium() {
|
||||
if (!this.canAccessPremium) {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1979,6 +1979,9 @@
|
||||
"apiKey": {
|
||||
"message": "API Key"
|
||||
},
|
||||
"premiumSubcriptionRequired": {
|
||||
"message": "Premium subscription required"
|
||||
},
|
||||
"organizationIsDisabled": {
|
||||
"message": "Organization is disabled."
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwo
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
@Component({
|
||||
selector: "app-update-password",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
[password]="masterPassword"
|
||||
[email]="email"
|
||||
[showText]="true"
|
||||
(passwordStrengthResult)="getStrengthResult($event)"
|
||||
>
|
||||
</app-password-strength>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +147,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
const premiumConfirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("premiumRequiredDesc"),
|
||||
this.i18nService.t("premiumRequired"),
|
||||
this.i18nService.t("learnMore"),
|
||||
this.i18nService.t("upgrade"),
|
||||
this.i18nService.t("cancel")
|
||||
);
|
||||
if (premiumConfirmed) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component } from "@angular/core";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { OrganizationApiKeyType } from "@bitwarden/common/enums/organizationApiKeyType";
|
||||
import { OrganizationApiKeyRequest } from "@bitwarden/common/models/request/organizationApiKeyRequest";
|
||||
import { ApiKeyResponse } from "@bitwarden/common/models/response/apiKeyResponse";
|
||||
|
||||
@@ -77,7 +77,12 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<app-password-strength [password]="newPassword" [email]="email" [showText]="true">
|
||||
<app-password-strength
|
||||
[password]="newPassword"
|
||||
[email]="email"
|
||||
[showText]="true"
|
||||
(passwordStrengthResult)="getStrengthResult($event)"
|
||||
>
|
||||
</app-password-strength>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core";
|
||||
import zxcvbn from "zxcvbn";
|
||||
|
||||
import { PasswordStrengthComponent } from "@bitwarden/angular/shared/components/password-strength/password-strength.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -28,7 +29,7 @@ export class ResetPasswordComponent implements OnInit {
|
||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
newPassword: string = null;
|
||||
showPassword = false;
|
||||
masterPasswordScore: number;
|
||||
passwordStrengthResult: zxcvbn.ZXCVBNResult;
|
||||
formPromise: Promise<any>;
|
||||
|
||||
constructor(
|
||||
@@ -97,7 +98,7 @@ export class ResetPasswordComponent implements OnInit {
|
||||
if (
|
||||
this.enforcedPolicyOptions != null &&
|
||||
!this.policyService.evaluateMasterPassword(
|
||||
this.masterPasswordScore,
|
||||
this.passwordStrengthResult.score,
|
||||
this.newPassword,
|
||||
this.enforcedPolicyOptions
|
||||
)
|
||||
@@ -110,7 +111,7 @@ export class ResetPasswordComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.masterPasswordScore < 3) {
|
||||
if (this.passwordStrengthResult.score < 3) {
|
||||
const result = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("weakMasterPasswordDesc"),
|
||||
this.i18nService.t("weakMasterPassword"),
|
||||
@@ -184,4 +185,8 @@ export class ResetPasswordComponent implements OnInit {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
getStrengthResult(result: zxcvbn.ZXCVBNResult) {
|
||||
this.passwordStrengthResult = result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
@@ -53,10 +53,10 @@ export class DeleteOrganizationComponent implements OnInit {
|
||||
deleteOrganizationRequestType: "InvalidFamiliesForEnterprise" | "RegularDelete" = "RegularDelete";
|
||||
organizationName: string;
|
||||
organizationContentSummary: OrganizationContentSummary = new OrganizationContentSummary();
|
||||
@Output() onSuccess: EventEmitter<any> = new EventEmitter();
|
||||
@Output() onSuccess: EventEmitter<unknown> = new EventEmitter();
|
||||
|
||||
masterPassword: Verification;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
|
||||
import { ExportComponent } from "../../../tools/import-export/export.component";
|
||||
@@ -66,7 +66,7 @@ export class OrganizationExportComponent extends ExportComponent {
|
||||
return super.getFileName("org");
|
||||
}
|
||||
|
||||
async collectEvent(): Promise<any> {
|
||||
async collectEvent(): Promise<void> {
|
||||
await this.eventService.collect(
|
||||
EventType.Organization_ClientExportedVault,
|
||||
null,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/models/request/organizationUserResetPasswordEnrollmentRequest";
|
||||
@@ -22,7 +22,7 @@ export class EnrollMasterPasswordReset {
|
||||
organization: Organization;
|
||||
|
||||
verification: Verification;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<void>;
|
||||
|
||||
constructor(
|
||||
private userVerificationService: UserVerificationService,
|
||||
|
||||
@@ -189,6 +189,7 @@
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<img class="no-items-image" aria-hidden="true" />
|
||||
<p>{{ "noSendsInList" | i18n }}</p>
|
||||
<button (click)="addSend()" class="btn btn-outline-primary" [disabled]="disableSend">
|
||||
<i class="bwi bwi-plus bwi-fw"></i>{{ "createSend" | i18n }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
import { ApiKeyResponse } from "@bitwarden/common/models/response/apiKeyResponse";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
|
||||
@@ -5,7 +5,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
|
||||
@Component({
|
||||
@@ -14,7 +14,7 @@ import { Verification } from "@bitwarden/common/types/verification";
|
||||
})
|
||||
export class DeauthorizeSessionsComponent {
|
||||
masterPassword: Verification;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
|
||||
@Component({
|
||||
@@ -16,7 +16,7 @@ export class PurgeVaultComponent {
|
||||
@Input() organizationId?: string = null;
|
||||
|
||||
masterPassword: Verification;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
|
||||
@@ -5,13 +5,28 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/models/request/updateTwoFactorAuthenticatorRequest";
|
||||
import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/models/response/twoFactorAuthenticatorResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
||||
// NOTE: There are additional options available but these are just the ones we are current using.
|
||||
// See: https://github.com/neocotic/qrious#examples
|
||||
interface QRiousOptions {
|
||||
element: HTMLElement;
|
||||
value: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
QRious: new (options: QRiousOptions) => unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-two-factor-authenticator",
|
||||
templateUrl: "two-factor-authenticator.component.html",
|
||||
@@ -23,7 +38,7 @@ export class TwoFactorAuthenticatorComponent
|
||||
type = TwoFactorProviderType.Authenticator;
|
||||
key: string;
|
||||
token: string;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<TwoFactorAuthenticatorResponse>;
|
||||
|
||||
private qrScript: HTMLScriptElement;
|
||||
|
||||
@@ -49,7 +64,7 @@ export class TwoFactorAuthenticatorComponent
|
||||
window.document.body.removeChild(this.qrScript);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
auth(authResponse: AuthResponse<TwoFactorAuthenticatorResponse>) {
|
||||
super.auth(authResponse);
|
||||
return this.processResponse(authResponse.response);
|
||||
}
|
||||
@@ -80,7 +95,7 @@ export class TwoFactorAuthenticatorComponent
|
||||
this.key = response.key;
|
||||
const email = await this.stateService.getEmail();
|
||||
window.setTimeout(() => {
|
||||
new (window as any).QRious({
|
||||
new window.QRious({
|
||||
element: document.getElementById("qr"),
|
||||
value:
|
||||
"otpauth://totp/Bitwarden:" +
|
||||
|
||||
@@ -4,11 +4,12 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
import { TwoFactorProviderRequest } from "@bitwarden/common/models/request/twoFactorProviderRequest";
|
||||
import { AuthResponseBase } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
@Directive()
|
||||
export abstract class TwoFactorBaseComponent {
|
||||
@@ -31,7 +32,7 @@ export abstract class TwoFactorBaseComponent {
|
||||
protected userVerificationService: UserVerificationService
|
||||
) {}
|
||||
|
||||
protected auth(authResponse: any) {
|
||||
protected auth(authResponse: AuthResponseBase) {
|
||||
this.hashedSecret = authResponse.secret;
|
||||
this.verificationType = authResponse.verificationType;
|
||||
this.authed = true;
|
||||
@@ -46,7 +47,7 @@ export abstract class TwoFactorBaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
protected async disable(promise: Promise<any>) {
|
||||
protected async disable(promise: Promise<unknown>) {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("twoStepDisableDesc"),
|
||||
this.i18nService.t("disable"),
|
||||
|
||||
@@ -4,10 +4,11 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { UpdateTwoFactorDuoRequest } from "@bitwarden/common/models/request/updateTwoFactorDuoRequest";
|
||||
import { TwoFactorDuoResponse } from "@bitwarden/common/models/response/twoFactorDuoResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
||||
@@ -20,7 +21,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||
ikey: string;
|
||||
skey: string;
|
||||
host: string;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<TwoFactorDuoResponse>;
|
||||
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
@@ -32,7 +33,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||
super(apiService, i18nService, platformUtilsService, logService, userVerificationService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
auth(authResponse: AuthResponse<TwoFactorDuoResponse>) {
|
||||
super.auth(authResponse);
|
||||
this.processResponse(authResponse.response);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { TwoFactorEmailRequest } from "@bitwarden/common/models/request/twoFactorEmailRequest";
|
||||
import { UpdateTwoFactorEmailRequest } from "@bitwarden/common/models/request/updateTwoFactorEmailRequest";
|
||||
import { TwoFactorEmailResponse } from "@bitwarden/common/models/response/twoFactorEmailResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
||||
@@ -22,8 +23,8 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
||||
email: string;
|
||||
token: string;
|
||||
sentEmail: string;
|
||||
formPromise: Promise<any>;
|
||||
emailPromise: Promise<any>;
|
||||
formPromise: Promise<TwoFactorEmailResponse>;
|
||||
emailPromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
@@ -36,7 +37,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
||||
super(apiService, i18nService, platformUtilsService, logService, userVerificationService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
auth(authResponse: AuthResponse<TwoFactorEmailResponse>) {
|
||||
super.auth(authResponse);
|
||||
return this.processResponse(authResponse.response);
|
||||
}
|
||||
|
||||
@@ -2,26 +2,14 @@ import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/models/response/twoFactorAuthenticatorResponse";
|
||||
import { TwoFactorDuoResponse } from "@bitwarden/common/models/response/twoFactorDuoResponse";
|
||||
import { TwoFactorEmailResponse } from "@bitwarden/common/models/response/twoFactorEmailResponse";
|
||||
import { TwoFactorRecoverResponse } from "@bitwarden/common/models/response/twoFactorRescoverResponse";
|
||||
import { TwoFactorWebAuthnResponse } from "@bitwarden/common/models/response/twoFactorWebAuthnResponse";
|
||||
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/models/response/twoFactorYubiKeyResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
import { TwoFactorResponse } from "@bitwarden/common/types/twoFactorResponse";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
|
||||
type TwoFactorResponse =
|
||||
| TwoFactorRecoverResponse
|
||||
| TwoFactorDuoResponse
|
||||
| TwoFactorEmailResponse
|
||||
| TwoFactorWebAuthnResponse
|
||||
| TwoFactorAuthenticatorResponse
|
||||
| TwoFactorYubiKeyResponse;
|
||||
|
||||
@Component({
|
||||
selector: "app-two-factor-verify",
|
||||
templateUrl: "two-factor-verify.component.html",
|
||||
@@ -29,7 +17,7 @@ type TwoFactorResponse =
|
||||
export class TwoFactorVerifyComponent {
|
||||
@Input() type: TwoFactorProviderType;
|
||||
@Input() organizationId: string;
|
||||
@Output() onAuthed = new EventEmitter<any>();
|
||||
@Output() onAuthed = new EventEmitter<AuthResponse<TwoFactorResponse>>();
|
||||
|
||||
secret: Verification;
|
||||
formPromise: Promise<TwoFactorResponse>;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
import { UpdateTwoFactorWebAuthnDeleteRequest } from "@bitwarden/common/models/request/updateTwoFactorWebAuthnDeleteRequest";
|
||||
@@ -13,9 +13,18 @@ import {
|
||||
ChallengeResponse,
|
||||
TwoFactorWebAuthnResponse,
|
||||
} from "@bitwarden/common/models/response/twoFactorWebAuthnResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
||||
interface Key {
|
||||
id: number;
|
||||
name: string;
|
||||
configured: boolean;
|
||||
migrated?: boolean;
|
||||
removePromise: Promise<TwoFactorWebAuthnResponse> | null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-two-factor-webauthn",
|
||||
templateUrl: "two-factor-webauthn.component.html",
|
||||
@@ -23,14 +32,14 @@ import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
|
||||
type = TwoFactorProviderType.WebAuthn;
|
||||
name: string;
|
||||
keys: any[];
|
||||
keys: Key[];
|
||||
keyIdAvailable: number = null;
|
||||
keysConfiguredCount = 0;
|
||||
webAuthnError: boolean;
|
||||
webAuthnListening: boolean;
|
||||
webAuthnResponse: PublicKeyCredential;
|
||||
challengePromise: Promise<ChallengeResponse>;
|
||||
formPromise: Promise<any>;
|
||||
formPromise: Promise<TwoFactorWebAuthnResponse>;
|
||||
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
@@ -43,7 +52,7 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
|
||||
super(apiService, i18nService, platformUtilsService, logService, userVerificationService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
auth(authResponse: AuthResponse<TwoFactorWebAuthnResponse>) {
|
||||
super.auth(authResponse);
|
||||
this.processResponse(authResponse.response);
|
||||
}
|
||||
@@ -69,11 +78,11 @@ export class TwoFactorWebAuthnComponent extends TwoFactorBaseComponent {
|
||||
return super.disable(this.formPromise);
|
||||
}
|
||||
|
||||
async remove(key: any) {
|
||||
async remove(key: Key) {
|
||||
if (this.keysConfiguredCount <= 1 || key.removePromise != null) {
|
||||
return;
|
||||
}
|
||||
const name = key.name != null ? key.name : this.i18nService.t("webAuthnkeyX", key.id);
|
||||
const name = key.name != null ? key.name : this.i18nService.t("webAuthnkeyX", key.id as any);
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("removeU2fConfirmation"),
|
||||
name,
|
||||
|
||||
@@ -4,24 +4,30 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType";
|
||||
import { UpdateTwoFactorYubioOtpRequest } from "@bitwarden/common/models/request/updateTwoFactorYubioOtpRequest";
|
||||
import { TwoFactorYubiKeyResponse } from "@bitwarden/common/models/response/twoFactorYubiKeyResponse";
|
||||
import { AuthResponse } from "@bitwarden/common/types/authResponse";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
||||
interface Key {
|
||||
key: string;
|
||||
existingKey: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-two-factor-yubikey",
|
||||
templateUrl: "two-factor-yubikey.component.html",
|
||||
})
|
||||
export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
|
||||
type = TwoFactorProviderType.Yubikey;
|
||||
keys: any[];
|
||||
keys: Key[];
|
||||
nfc = false;
|
||||
|
||||
formPromise: Promise<any>;
|
||||
disablePromise: Promise<any>;
|
||||
formPromise: Promise<TwoFactorYubiKeyResponse>;
|
||||
disablePromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
@@ -33,7 +39,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
|
||||
super(apiService, i18nService, platformUtilsService, logService, userVerificationService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
auth(authResponse: AuthResponse<TwoFactorYubiKeyResponse>) {
|
||||
super.auth(authResponse);
|
||||
this.processResponse(authResponse.response);
|
||||
}
|
||||
@@ -59,7 +65,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
|
||||
return super.disable(this.disablePromise);
|
||||
}
|
||||
|
||||
remove(key: any) {
|
||||
remove(key: Key) {
|
||||
key.existingKey = null;
|
||||
key.key = null;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti
|
||||
templateUrl: "verify-email.component.html",
|
||||
})
|
||||
export class VerifyEmailComponent {
|
||||
actionPromise: Promise<any>;
|
||||
actionPromise: Promise<unknown>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
@Component({
|
||||
selector: "app-export",
|
||||
|
||||
@@ -166,8 +166,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 form-group">
|
||||
<div class="tw-flex tw-flex-row">
|
||||
<div class="tw-mb-4 tw-w-1/2">
|
||||
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
|
||||
<input
|
||||
id="loginTotp"
|
||||
@@ -179,14 +179,41 @@
|
||||
[disabled]="cipher.isDeleted || !cipher.viewPassword || viewOnly"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6 form-group totp d-flex align-items-end" [ngClass]="{ low: totpLow }">
|
||||
<div *ngIf="!cipher.login.totp || !totpCode">
|
||||
<img
|
||||
src="../../images/totp-countdown.png"
|
||||
id="totpImage"
|
||||
<div class="tw-mb-4 tw-ml-4 tw-flex tw-w-1/2 tw-items-end" [ngClass]="{ low: totpLow }">
|
||||
<div
|
||||
class="totp tw-flex tw-flex-row tw-items-center"
|
||||
*ngIf="!cipher.login.totp || (cipher.login.totp && !canAccessPremium)"
|
||||
>
|
||||
<span class="totp-countdown">
|
||||
<span class="totp-sec tw-text-muted">15</span>
|
||||
<svg>
|
||||
<g>
|
||||
<circle
|
||||
class="totp-circle-muted inner"
|
||||
r="12.6"
|
||||
cy="16"
|
||||
cx="16"
|
||||
opacity="0.25"
|
||||
[ngStyle]="{ 'stroke-dashoffset.px': 40 }"
|
||||
></circle>
|
||||
<circle
|
||||
class="totp-circle-muted outer"
|
||||
opacity="0.25"
|
||||
r="14"
|
||||
cy="16"
|
||||
cx="16"
|
||||
></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="totp-code tw-mr-3 tw-ml-2 tw-text-muted"
|
||||
title="{{ 'verificationCodeTotp' | i18n }}"
|
||||
class="ml-2"
|
||||
/>
|
||||
>--- ---</span
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone tw-text-muted" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="tw-pb-2" *ngIf="!cipher.login.totp || !totpCode">
|
||||
<app-premium-badge
|
||||
*ngIf="!organization && !cipher.organizationId"
|
||||
class="ml-3"
|
||||
@@ -209,8 +236,11 @@
|
||||
{{ "upgrade" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="cipher.login.totp && totpCode" class="d-flex align-items-center">
|
||||
<span class="totp-countdown mr-3 ml-2">
|
||||
<div
|
||||
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
|
||||
class="totp tw-flex tw-flex-row tw-items-center"
|
||||
>
|
||||
<span class="totp-countdown">
|
||||
<span class="totp-sec">{{ totpSec }}</span>
|
||||
<svg>
|
||||
<g>
|
||||
@@ -225,16 +255,18 @@
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="totp-code mr-2" title="{{ 'verificationCodeTotp' | i18n }}">{{
|
||||
totpCodeFormatted
|
||||
}}</span>
|
||||
<span
|
||||
class="totp-code tw-mr-2 tw-ml-2 tw-mt-1"
|
||||
title="{{ 'verificationCodeTotp' | i18n }}"
|
||||
>{{ totpCodeFormatted }}</span
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link"
|
||||
class="tw-items-center tw-border-none tw-bg-transparent tw-text-primary-500"
|
||||
appA11yTitle="{{ 'copyVerificationCode' | i18n }}"
|
||||
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')"
|
||||
>
|
||||
<i class="bwi bwi-clone" aria-hidden="true"></i>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -152,6 +152,17 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
});
|
||||
}
|
||||
|
||||
showGetPremium() {
|
||||
if (this.canAccessPremium) {
|
||||
return;
|
||||
}
|
||||
if (this.cipher.organizationUseTotp) {
|
||||
this.upgradeOrganization();
|
||||
} else {
|
||||
this.premiumRequired();
|
||||
}
|
||||
}
|
||||
|
||||
viewHistory() {
|
||||
this.viewingPasswordHistory = !this.viewingPasswordHistory;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loaded">
|
||||
<img class="no-items-image" aria-hidden="true" />
|
||||
<p>{{ "noItemsInList" | i18n }}</p>
|
||||
<button (click)="addCipher()" class="btn btn-outline-primary" *ngIf="showAddNew">
|
||||
<i class="bwi bwi-plus bwi-fw"></i>{{ "addItem" | i18n }}
|
||||
|
||||
34
apps/web/src/images/search-web-dark.svg
Normal file
34
apps/web/src/images/search-web-dark.svg
Normal file
@@ -0,0 +1,34 @@
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.7">
|
||||
<g opacity="0.7">
|
||||
<g clip-path="url(#clip0_44_9647)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.3599 73.2564C43.579 74.4366 47.0654 75.0822 50.7059 75.0822C66.9882 75.0822 80.1876 62.1696 80.1876 46.2411C80.1876 45.8578 80.1804 45.4762 80.1648 45.0966H108.891V84.6672H40.3599V73.2564Z" fill="#6e7689"/>
|
||||
<path d="M21.5461 46.241C21.5461 62.1696 34.7456 75.0822 51.028 75.0822C67.3104 75.0822 80.5098 62.1696 80.5098 46.241C80.5098 30.3125 67.3104 17.4 51.028 17.4C34.7456 17.4 21.5461 30.3125 21.5461 46.241Z" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M35.3603 70.5954C35.3603 69.933 34.823 69.3954 34.1603 69.3954C33.4976 69.3954 32.9603 69.933 32.9603 70.5954H35.3603ZM112.835 40.2387C114.169 40.2387 115.2 41.3027 115.2 42.5698H117.6C117.6 39.9762 115.493 37.8387 112.835 37.8387V40.2387ZM115.2 42.5698V88.6158H117.6V42.5698H115.2ZM115.2 88.6158C115.2 89.9094 114.142 90.9468 112.835 90.9468V93.3468C115.425 93.3468 117.6 91.2774 117.6 88.6158H115.2ZM112.835 90.9468H37.7256V93.3468H112.835V90.9468ZM37.7256 90.9468C36.3913 90.9468 35.3603 89.883 35.3603 88.6158H32.9603C32.9603 91.2096 35.0667 93.3468 37.7256 93.3468V90.9468ZM35.3603 88.6158V70.5954H32.9603V88.6158H35.3603ZM79.8684 40.2387H112.835V37.8387H79.8684V40.2387Z" fill="#bac0ce"/>
|
||||
<path d="M79.9068 45.2867H109.021V84.8574H40.4873V73.0512" stroke="#bac0ce" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M57.3565 102.56H93.2046" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M68.9544 92.1468V102.56" stroke="#bac0ce" stroke-width="4" stroke-linejoin="round"/>
|
||||
<path d="M80.553 92.1468V102.56" stroke="#bac0ce" stroke-width="4" stroke-linejoin="round"/>
|
||||
<path d="M27.4398 64.9452L22.9296 69.4554L5.72134 86.6634C4.54976 87.8352 4.54976 89.7342 5.72133 90.906L6.95929 92.1438C8.13085 93.3156 10.0304 93.3156 11.202 92.1438L28.4102 74.9358L32.9204 70.4256" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M101.293 53.1537H85.1784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 59.1966H90.2142" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M85.1784 59.1966H77.625" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 65.2392H94.2426" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M88.7034 65.2392H73.0926" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 71.2824H85.1784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M79.6392 71.2824H71.0784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 77.325H78.6318" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M73.0926 77.325H59.9997" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M54.4604 77.325H46.4032" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M29.1638 33.0108H70.6926C72.0181 33.0108 73.0926 34.0853 73.0926 35.4108V41.6894C73.0926 43.0149 72.0181 44.0894 70.6926 44.0894H29.1638C27.8383 44.0894 26.7638 43.0149 26.7638 41.6894V35.4108C26.7638 34.0853 27.8383 33.0108 29.1638 33.0108Z" stroke="#bac0ce" stroke-width="4"/>
|
||||
<path d="M22.7354 54.1609H57.0962C58.4217 54.1609 59.4962 55.2354 59.4962 56.5609V62.8392C59.4962 64.1652 58.4217 65.2392 57.0962 65.2392H28.7783" stroke="#bac0ce" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M79.1358 54.1609H72.975C71.6496 54.1609 70.575 55.2354 70.575 56.5609V62.9736C70.575 64.2252 71.5896 65.2392 72.8406 65.2392" stroke="#bac0ce" stroke-width="4" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_44_9647">
|
||||
<rect width="120" height="120" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
34
apps/web/src/images/search-web-light.svg
Normal file
34
apps/web/src/images/search-web-light.svg
Normal file
@@ -0,0 +1,34 @@
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.7">
|
||||
<g opacity="0.7">
|
||||
<g clip-path="url(#clip0_44_9647)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.3599 73.2564C43.579 74.4366 47.0654 75.0822 50.7059 75.0822C66.9882 75.0822 80.1876 62.1696 80.1876 46.2411C80.1876 45.8578 80.1804 45.4762 80.1648 45.0966H108.891V84.6672H40.3599V73.2564Z" fill="#ced4dc"/>
|
||||
<path d="M21.5461 46.241C21.5461 62.1696 34.7456 75.0822 51.028 75.0822C67.3104 75.0822 80.5098 62.1696 80.5098 46.241C80.5098 30.3125 67.3104 17.4 51.028 17.4C34.7456 17.4 21.5461 30.3125 21.5461 46.241Z" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M35.3603 70.5954C35.3603 69.933 34.823 69.3954 34.1603 69.3954C33.4976 69.3954 32.9603 69.933 32.9603 70.5954H35.3603ZM112.835 40.2387C114.169 40.2387 115.2 41.3027 115.2 42.5698H117.6C117.6 39.9762 115.493 37.8387 112.835 37.8387V40.2387ZM115.2 42.5698V88.6158H117.6V42.5698H115.2ZM115.2 88.6158C115.2 89.9094 114.142 90.9468 112.835 90.9468V93.3468C115.425 93.3468 117.6 91.2774 117.6 88.6158H115.2ZM112.835 90.9468H37.7256V93.3468H112.835V90.9468ZM37.7256 90.9468C36.3913 90.9468 35.3603 89.883 35.3603 88.6158H32.9603C32.9603 91.2096 35.0667 93.3468 37.7256 93.3468V90.9468ZM35.3603 88.6158V70.5954H32.9603V88.6158H35.3603ZM79.8684 40.2387H112.835V37.8387H79.8684V40.2387Z" fill="#6d757e"/>
|
||||
<path d="M79.9068 45.2867H109.021V84.8574H40.4873V73.0512" stroke="#6d757e" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M57.3565 102.56H93.2046" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M68.9544 92.1468V102.56" stroke="#6d757e" stroke-width="4" stroke-linejoin="round"/>
|
||||
<path d="M80.553 92.1468V102.56" stroke="#6d757e" stroke-width="4" stroke-linejoin="round"/>
|
||||
<path d="M27.4398 64.9452L22.9296 69.4554L5.72134 86.6634C4.54976 87.8352 4.54976 89.7342 5.72133 90.906L6.95929 92.1438C8.13085 93.3156 10.0304 93.3156 11.202 92.1438L28.4102 74.9358L32.9204 70.4256" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M101.293 53.1537H85.1784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 59.1966H90.2142" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M85.1784 59.1966H77.625" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 65.2392H94.2426" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M88.7034 65.2392H73.0926" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 71.2824H85.1784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M79.6392 71.2824H71.0784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M101.293 77.325H78.6318" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M73.0926 77.325H59.9997" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M54.4604 77.325H46.4032" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M29.1638 33.0108H70.6926C72.0181 33.0108 73.0926 34.0853 73.0926 35.4108V41.6894C73.0926 43.0149 72.0181 44.0894 70.6926 44.0894H29.1638C27.8383 44.0894 26.7638 43.0149 26.7638 41.6894V35.4108C26.7638 34.0853 27.8383 33.0108 29.1638 33.0108Z" stroke="#6d757e" stroke-width="4"/>
|
||||
<path d="M22.7354 54.1609H57.0962C58.4217 54.1609 59.4962 55.2354 59.4962 56.5609V62.8392C59.4962 64.1652 58.4217 65.2392 57.0962 65.2392H28.7783" stroke="#6d757e" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M79.1358 54.1609H72.975C71.6496 54.1609 70.575 55.2354 70.575 56.5609V62.9736C70.575 64.2252 71.5896 65.2392 72.8406 65.2392" stroke="#6d757e" stroke-width="4" stroke-linecap="round"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_44_9647">
|
||||
<rect width="120" height="120" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 950 B |
@@ -5227,6 +5227,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"premiumSubcriptionRequired": {
|
||||
"message": "Premium subscription required"
|
||||
},
|
||||
"scim": {
|
||||
"message": "SCIM Provisioning",
|
||||
"description": "The text, 'SCIM', is an acronymn and should not be translated."
|
||||
|
||||
@@ -306,3 +306,18 @@ button i.bwi,
|
||||
a i.bwi {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.no-items {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.no-items-image {
|
||||
@include themify($themes) {
|
||||
content: url("../images/search-web" + themed("svgSuffix"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,25 @@
|
||||
stroke-width: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.totp-circle-muted {
|
||||
fill: none;
|
||||
@include themify($themes) {
|
||||
stroke: themed("info");
|
||||
}
|
||||
|
||||
&.inner {
|
||||
stroke-dasharray: 78.6;
|
||||
stroke-dashoffset: 0;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
&.outer {
|
||||
stroke-dasharray: 88;
|
||||
stroke-dashoffset: 0;
|
||||
stroke-width: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .align-items-center {
|
||||
|
||||
@@ -219,6 +219,7 @@ $themes: (
|
||||
textMuted: #6c757d,
|
||||
textSuccessColor: $white,
|
||||
textWarningColor: $white,
|
||||
svgSuffix: "-light.svg",
|
||||
),
|
||||
dark: (
|
||||
primary: $darkPrimary,
|
||||
@@ -330,6 +331,7 @@ $themes: (
|
||||
textMuted: $darkGrey1,
|
||||
textSuccessColor: $darkDarkBlue2,
|
||||
textWarningColor: $darkDarkBlue2,
|
||||
svgSuffix: "-dark.svg",
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||
|
||||
@@ -142,7 +142,7 @@ export class ExportComponent implements OnInit {
|
||||
return this.exportService.getFileName(prefix, extension);
|
||||
}
|
||||
|
||||
protected async collectEvent(): Promise<any> {
|
||||
protected async collectEvent(): Promise<void> {
|
||||
await this.eventService.collect(EventType.User_ClientExportedVault);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Directive, NgZone, OnInit } from "@angular/core";
|
||||
import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subscription } from "rxjs";
|
||||
import { take } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -20,7 +21,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCry
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/models/request/secretVerificationRequest";
|
||||
|
||||
@Directive()
|
||||
export class LockComponent implements OnInit {
|
||||
export class LockComponent implements OnInit, OnDestroy {
|
||||
masterPassword = "";
|
||||
pin = "";
|
||||
showPassword = false;
|
||||
@@ -39,6 +40,8 @@ export class LockComponent implements OnInit {
|
||||
private invalidPinAttempts = 0;
|
||||
private pinSet: [boolean, boolean];
|
||||
|
||||
private activeAccountSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
protected i18nService: I18nService,
|
||||
@@ -57,11 +60,15 @@ export class LockComponent implements OnInit {
|
||||
async ngOnInit() {
|
||||
// Load the first and observe updates
|
||||
await this.load();
|
||||
this.stateService.activeAccount.subscribe(async () => {
|
||||
this.activeAccountSubscription = this.stateService.activeAccount$.subscribe(async () => {
|
||||
await this.load();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.activeAccountSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.pinLock && (this.pin == null || this.pin === "")) {
|
||||
this.platformUtilsService.showToast(
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwo
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { EncString } from "@bitwarden/common/models/domain/encString";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
|
||||
@@ -28,7 +28,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
||||
showPassword = false;
|
||||
currentMasterPassword: string;
|
||||
|
||||
onSuccessfulChangePassword: () => Promise<any>;
|
||||
onSuccessfulChangePassword: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
protected router: Router,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ControlValueAccessor, UntypedFormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl } from "@angular/forms";
|
||||
|
||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
@@ -35,7 +35,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
disableRequestOTP = false;
|
||||
sentCode = false;
|
||||
|
||||
secret = new UntypedFormControl("");
|
||||
secret = new FormControl("");
|
||||
|
||||
private onChange: (value: Verification) => void;
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstrac
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "@bitwarden/common/abstractions/usernameGeneration.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout.service";
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
@@ -89,7 +90,8 @@ import { SyncService } from "@bitwarden/common/services/sync.service";
|
||||
import { TokenService } from "@bitwarden/common/services/token.service";
|
||||
import { TotpService } from "@bitwarden/common/services/totp.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/services/twoFactor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification.service";
|
||||
import { UserVerificationApiService } from "@bitwarden/common/services/userVerification/userVerification-api.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/services/userVerification/userVerification.service";
|
||||
import { UsernameGenerationService } from "@bitwarden/common/services/usernameGeneration.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service";
|
||||
@@ -476,7 +478,11 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
{
|
||||
provide: UserVerificationServiceAbstraction,
|
||||
useClass: UserVerificationService,
|
||||
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction],
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
UserVerificationApiServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
@@ -502,6 +508,11 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
provide: FormValidationErrorsServiceAbstraction,
|
||||
useClass: FormValidationErrorsService,
|
||||
},
|
||||
{
|
||||
provide: UserVerificationApiServiceAbstraction,
|
||||
useClass: UserVerificationApiService,
|
||||
deps: [ApiServiceAbstraction],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
|
||||
@@ -78,25 +78,4 @@ describe("ConsoleLogService", () => {
|
||||
error: { 0: "this is an error message" },
|
||||
});
|
||||
});
|
||||
|
||||
it("times with output to info", async () => {
|
||||
logService.time();
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
const duration = logService.timeEnd();
|
||||
expect(duration[0]).toBe(0);
|
||||
expect(duration[1]).toBeGreaterThan(0);
|
||||
expect(duration[1]).toBeLessThan(500 * 10e6);
|
||||
|
||||
expect(caughtMessage).toEqual(expect.arrayContaining([]));
|
||||
expect(caughtMessage.log.length).toBe(1);
|
||||
expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/));
|
||||
});
|
||||
|
||||
it("filters time output", async () => {
|
||||
logService = new ConsoleLogService(true, () => true);
|
||||
logService.time();
|
||||
logService.timeEnd();
|
||||
|
||||
expect(caughtMessage).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("Folder Service", () => {
|
||||
stateService.getEncryptedFolders().resolves({
|
||||
"1": folderData("1", "test"),
|
||||
});
|
||||
stateService.activeAccount.returns(activeAccount);
|
||||
stateService.activeAccount$.returns(activeAccount);
|
||||
stateService.activeAccountUnlocked.returns(activeAccountUnlocked);
|
||||
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { AttachmentRequest } from "../models/request/attachmentRequest";
|
||||
import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest";
|
||||
import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest";
|
||||
@@ -228,8 +227,6 @@ export abstract class ApiService {
|
||||
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
||||
postAccountRequestOTP: () => Promise<void>;
|
||||
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||
postConvertToKeyConnector: () => Promise<void>;
|
||||
|
||||
getUserBillingHistory: () => Promise<BillingHistoryResponse>;
|
||||
|
||||
@@ -6,6 +6,4 @@ export abstract class LogService {
|
||||
warning: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
write: (level: LogLevelType, message: string) => void;
|
||||
time: (label: string) => void;
|
||||
timeEnd: (label: string) => [number, number];
|
||||
}
|
||||
|
||||
@@ -26,9 +26,8 @@ import { SendView } from "../models/view/sendView";
|
||||
|
||||
export abstract class StateService<T extends Account = Account> {
|
||||
accounts: BehaviorSubject<{ [userId: string]: T }>;
|
||||
activeAccount: BehaviorSubject<string>;
|
||||
|
||||
activeAccountUnlocked: Observable<boolean>;
|
||||
activeAccount$: Observable<string>;
|
||||
activeAccountUnlocked$: Observable<boolean>;
|
||||
|
||||
addAccount: (account: T) => Promise<void>;
|
||||
setActiveUser: (userId: string) => Promise<void>;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { VerifyOTPRequest } from "@bitwarden/common/models/request/account/verifyOTPRequest";
|
||||
|
||||
export abstract class UserVerificationApiServiceAbstraction {
|
||||
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||
postAccountRequestOTP: () => Promise<void>;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../types/verification";
|
||||
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
export abstract class UserVerificationService {
|
||||
buildRequest: <T extends SecretVerificationRequest>(
|
||||
@@ -1,17 +1,28 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
import * as tldjs from "tldjs";
|
||||
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
|
||||
const nodeURL = typeof window === "undefined" ? require("url") : null;
|
||||
|
||||
declare global {
|
||||
/* eslint-disable-next-line no-var */
|
||||
var bitwardenContainerService: BitwardenContainerService;
|
||||
}
|
||||
|
||||
interface BitwardenContainerService {
|
||||
getCryptoService: () => CryptoService;
|
||||
}
|
||||
|
||||
export class Utils {
|
||||
static inited = false;
|
||||
static isNode = false;
|
||||
static isBrowser = true;
|
||||
static isMobileBrowser = false;
|
||||
static isAppleMobileBrowser = false;
|
||||
static global: any = null;
|
||||
static global: typeof global = null;
|
||||
static tldEndingRegex =
|
||||
/.*\.(com|net|org|edu|uk|gov|ca|de|jp|fr|au|ru|ch|io|es|us|co|xyz|info|ly|mil)$/;
|
||||
// Transpiled version of /\p{Emoji_Presentation}/gu using https://mothereff.in/regexpu. Used for compatability in older browsers.
|
||||
@@ -29,16 +40,25 @@ export class Utils {
|
||||
(process as any).release != null &&
|
||||
(process as any).release.name === "node";
|
||||
Utils.isBrowser = typeof window !== "undefined";
|
||||
|
||||
Utils.isMobileBrowser = Utils.isBrowser && this.isMobile(window);
|
||||
Utils.isAppleMobileBrowser = Utils.isBrowser && this.isAppleMobile(window);
|
||||
Utils.global = Utils.isNode && !Utils.isBrowser ? global : window;
|
||||
|
||||
if (Utils.isNode) {
|
||||
Utils.global = global;
|
||||
} else if (Utils.isBrowser) {
|
||||
Utils.global = window;
|
||||
} else {
|
||||
// If it's not browser or node then it must be a service worker
|
||||
Utils.global = self;
|
||||
}
|
||||
}
|
||||
|
||||
static fromB64ToArray(str: string): Uint8Array {
|
||||
if (Utils.isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "base64"));
|
||||
} else {
|
||||
const binaryString = window.atob(str);
|
||||
const binaryString = Utils.global.atob(str);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
@@ -93,7 +113,7 @@ export class Utils {
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return window.btoa(binary);
|
||||
return Utils.global.btoa(binary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +177,7 @@ export class Utils {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(utfStr, "utf8").toString("base64");
|
||||
} else {
|
||||
return decodeURIComponent(escape(window.btoa(utfStr)));
|
||||
return decodeURIComponent(escape(Utils.global.btoa(utfStr)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +189,7 @@ export class Utils {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(b64Str, "base64").toString("utf8");
|
||||
} else {
|
||||
return decodeURIComponent(escape(window.atob(b64Str)));
|
||||
return decodeURIComponent(escape(Utils.global.atob(b64Str)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +404,7 @@ export class Utils {
|
||||
return new nodeURL.URL(uriString);
|
||||
} else if (typeof URL === "function") {
|
||||
return new URL(uriString);
|
||||
} else if (window != null) {
|
||||
} else if (typeof window !== "undefined") {
|
||||
const hasProtocol = uriString.indexOf("://") > -1;
|
||||
if (!hasProtocol && uriString.indexOf(".") > -1) {
|
||||
uriString = "http://" + uriString;
|
||||
|
||||
@@ -48,7 +48,7 @@ export class Attachment extends Domain {
|
||||
|
||||
if (this.key != null) {
|
||||
let cryptoService: CryptoService;
|
||||
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||
const containerService = Utils.global.bitwardenContainerService;
|
||||
if (containerService) {
|
||||
cryptoService = containerService.getCryptoService();
|
||||
} else {
|
||||
|
||||
@@ -104,7 +104,7 @@ export class EncString implements IEncrypted {
|
||||
}
|
||||
|
||||
let cryptoService: CryptoService;
|
||||
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||
const containerService = Utils.global.bitwardenContainerService;
|
||||
if (containerService) {
|
||||
cryptoService = containerService.getCryptoService();
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AccountApiService } from "@bitwarden/common/abstractions/account/account-api.service.abstraction";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
import { AccountService as AccountServiceAbstraction } from "../../abstractions/account/account.service.abstraction";
|
||||
import { Verification } from "../../types/verification";
|
||||
@@ -14,7 +14,7 @@ export class AccountService implements AccountServiceAbstraction {
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async delete(verification: Verification): Promise<any> {
|
||||
async delete(verification: Verification): Promise<void> {
|
||||
try {
|
||||
const verificationRequest = await this.userVerificationService.buildRequest(verification);
|
||||
await this.accountApiService.deleteAccount(verificationRequest);
|
||||
|
||||
@@ -8,7 +8,6 @@ import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { AttachmentRequest } from "../models/request/attachmentRequest";
|
||||
import { BitPayInvoiceRequest } from "../models/request/bitPayInvoiceRequest";
|
||||
import { CipherBulkDeleteRequest } from "../models/request/cipherBulkDeleteRequest";
|
||||
@@ -457,14 +456,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return this.send("PUT", "/accounts/update-temp-password", request, true, false);
|
||||
}
|
||||
|
||||
postAccountRequestOTP(): Promise<void> {
|
||||
return this.send("POST", "/accounts/request-otp", null, true, false);
|
||||
}
|
||||
|
||||
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||
return this.send("POST", "/accounts/verify-otp", request, true, false);
|
||||
}
|
||||
|
||||
postConvertToKeyConnector(): Promise<void> {
|
||||
return this.send("POST", "/accounts/convert-to-key-connector", null, true, false);
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
throw new Error("No key.");
|
||||
}
|
||||
|
||||
const promises: any[] = [];
|
||||
const promises: Promise<number>[] = [];
|
||||
const ciphers = await this.getAll();
|
||||
ciphers.forEach(async (cipher) => {
|
||||
promises.push(cipher.decrypt().then((c) => decCiphers.push(c)));
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as hrtime from "browser-hrtime";
|
||||
|
||||
import { LogService as LogServiceAbstraction } from "../abstractions/log.service";
|
||||
import { LogLevelType } from "../enums/logLevelType";
|
||||
|
||||
@@ -56,17 +54,4 @@ export class ConsoleLogService implements LogServiceAbstraction {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
time(label = "default") {
|
||||
if (!this.timersMap.has(label)) {
|
||||
this.timersMap.set(label, hrtime());
|
||||
}
|
||||
}
|
||||
|
||||
timeEnd(label = "default"): [number, number] {
|
||||
const elapsed = hrtime(this.timersMap.get(label));
|
||||
this.timersMap.delete(label);
|
||||
this.write(LogLevelType.Info, `${label}: ${elapsed[0] * 1000 + elapsed[1] / 10e6}ms`);
|
||||
return elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private scimUrl: string = null;
|
||||
|
||||
constructor(private stateService: StateService) {
|
||||
this.stateService.activeAccount.subscribe(async () => {
|
||||
this.stateService.activeAccount$.subscribe(async () => {
|
||||
await this.setUrlsFromStorage();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class FolderService implements InternalFolderServiceAbstraction {
|
||||
private cipherService: CipherService,
|
||||
private stateService: StateService
|
||||
) {
|
||||
this.stateService.activeAccountUnlocked.subscribe(async (unlocked) => {
|
||||
this.stateService.activeAccountUnlocked$.subscribe(async (unlocked) => {
|
||||
if ((Utils.global as any).bitwardenContainerService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
24
libs/common/src/services/noopEvent.service.ts
Normal file
24
libs/common/src/services/noopEvent.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
|
||||
/**
|
||||
* If you want to use this, don't.
|
||||
* If you think you should use that after the warning, don't.
|
||||
*/
|
||||
export default class NoOpEventService implements EventService {
|
||||
constructor() {
|
||||
if (chrome.runtime.getManifest().manifest_version !== 3) {
|
||||
throw new Error("You are not allowed to use this when not in manifest_version 3");
|
||||
}
|
||||
}
|
||||
|
||||
collect(eventType: EventType, cipherId?: string, uploadImmediately?: boolean) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
uploadEvents(userId?: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
clearEvents(userId?: string) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import { CipherView } from "../models/view/cipherView";
|
||||
import { SendView } from "../models/view/sendView";
|
||||
|
||||
export class SearchService implements SearchServiceAbstraction {
|
||||
private static registeredPipeline = false;
|
||||
|
||||
indexedEntityId?: string = null;
|
||||
private indexing = false;
|
||||
private index: lunr.Index = null;
|
||||
@@ -31,8 +33,13 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
}
|
||||
});
|
||||
|
||||
//register lunr pipeline function
|
||||
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
||||
// Currently have to ensure this is only done a single time. Lunr allows you to register a function
|
||||
// multiple times but they will add a warning message to the console. The way they do that breaks when ran on a service worker.
|
||||
if (!SearchService.registeredPipeline) {
|
||||
SearchService.registeredPipeline = true;
|
||||
//register lunr pipeline function
|
||||
lunr.Pipeline.registerFunction(this.normalizeAccentsPipelineFunction, "normalizeAccents");
|
||||
}
|
||||
}
|
||||
|
||||
clearIndex(): void {
|
||||
@@ -54,7 +61,6 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.time("search indexing");
|
||||
this.indexing = true;
|
||||
this.indexedEntityId = indexedEntityId;
|
||||
this.index = null;
|
||||
@@ -95,7 +101,7 @@ export class SearchService implements SearchServiceAbstraction {
|
||||
|
||||
this.indexing = false;
|
||||
|
||||
this.logService.timeEnd("search indexing");
|
||||
this.logService.info("Finished search indexing");
|
||||
}
|
||||
|
||||
async searchCiphers(
|
||||
|
||||
@@ -55,8 +55,11 @@ export class StateService<
|
||||
> implements StateServiceAbstraction<TAccount>
|
||||
{
|
||||
accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({});
|
||||
activeAccount = new BehaviorSubject<string>(null);
|
||||
activeAccountUnlocked = new BehaviorSubject<boolean>(false);
|
||||
private activeAccountSubject = new BehaviorSubject<string>(null);
|
||||
activeAccount$ = this.activeAccountSubject.asObservable();
|
||||
|
||||
private activeAccountUnlockedSubject = new BehaviorSubject<boolean>(false);
|
||||
activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable();
|
||||
|
||||
private hasBeenInited = false;
|
||||
private isRecoveredSession = false;
|
||||
@@ -73,17 +76,17 @@ export class StateService<
|
||||
protected useAccountCache: boolean = true
|
||||
) {
|
||||
// If the account gets changed, verify the new account is unlocked
|
||||
this.activeAccount.subscribe(async (userId) => {
|
||||
if (userId == null && this.activeAccountUnlocked.getValue() == false) {
|
||||
this.activeAccountSubject.subscribe(async (userId) => {
|
||||
if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) {
|
||||
return;
|
||||
} else if (userId == null) {
|
||||
this.activeAccountUnlocked.next(false);
|
||||
this.activeAccountUnlockedSubject.next(false);
|
||||
}
|
||||
|
||||
// FIXME: This should be refactored into AuthService or a similar service,
|
||||
// as checking for the existance of the crypto key is a low level
|
||||
// implementation detail.
|
||||
this.activeAccountUnlocked.next((await this.getCryptoMasterKey()) != null);
|
||||
this.activeAccountUnlockedSubject.next((await this.getCryptoMasterKey()) != null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -125,7 +128,7 @@ export class StateService<
|
||||
state.activeUserId = storedActiveUser;
|
||||
}
|
||||
await this.pushAccounts();
|
||||
this.activeAccount.next(state.activeUserId);
|
||||
this.activeAccountSubject.next(state.activeUserId);
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -154,7 +157,7 @@ export class StateService<
|
||||
await this.scaffoldNewAccountStorage(account);
|
||||
await this.setLastActive(new Date().getTime(), { userId: account.profile.userId });
|
||||
await this.setActiveUser(account.profile.userId);
|
||||
this.activeAccount.next(account.profile.userId);
|
||||
this.activeAccountSubject.next(account.profile.userId);
|
||||
}
|
||||
|
||||
async setActiveUser(userId: string): Promise<void> {
|
||||
@@ -162,7 +165,7 @@ export class StateService<
|
||||
await this.updateState(async (state) => {
|
||||
state.activeUserId = userId;
|
||||
await this.storageService.save(keys.activeUserId, userId);
|
||||
this.activeAccount.next(state.activeUserId);
|
||||
this.activeAccountSubject.next(state.activeUserId);
|
||||
return state;
|
||||
});
|
||||
|
||||
@@ -497,12 +500,12 @@ export class StateService<
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||
);
|
||||
|
||||
if (options.userId == this.activeAccount.getValue()) {
|
||||
if (options.userId == this.activeAccountSubject.getValue()) {
|
||||
const nextValue = value != null;
|
||||
|
||||
// Avoid emitting if we are already unlocked
|
||||
if (this.activeAccountUnlocked.getValue() != nextValue) {
|
||||
this.activeAccountUnlocked.next(nextValue);
|
||||
if (this.activeAccountUnlockedSubject.getValue() != nextValue) {
|
||||
this.activeAccountUnlockedSubject.next(nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -607,7 +610,7 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(CipherView)
|
||||
@withPrototypeForArrayMembers(CipherView, CipherView.fromJSON)
|
||||
async getDecryptedCiphers(options?: StorageOptions): Promise<CipherView[]> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verifyOTPRequest";
|
||||
|
||||
export class UserVerificationApiService implements UserVerificationApiServiceAbstraction {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/verify-otp", request, true, false);
|
||||
}
|
||||
async postAccountRequestOTP(): Promise<void> {
|
||||
return this.apiService.send("POST", "/accounts/request-otp", null, true, false);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../abstractions/userVerification.service";
|
||||
import { VerificationType } from "../enums/verificationType";
|
||||
import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
||||
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../types/verification";
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { I18nService } from "../../abstractions/i18n.service";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/userVerification/userVerification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { VerificationType } from "../../enums/verificationType";
|
||||
import { VerifyOTPRequest } from "../../models/request/account/verifyOTPRequest";
|
||||
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
|
||||
import { Verification } from "../../types/verification";
|
||||
|
||||
/**
|
||||
* Used for general-purpose user verification throughout the app.
|
||||
@@ -15,7 +15,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
constructor(
|
||||
private cryptoService: CryptoService,
|
||||
private i18nService: I18nService,
|
||||
private apiService: ApiService
|
||||
private userVerificationApiService: UserVerificationApiServiceAbstraction
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
if (verification.type === VerificationType.OTP) {
|
||||
const request = new VerifyOTPRequest(verification.secret);
|
||||
try {
|
||||
await this.apiService.postAccountVerifyOTP(request);
|
||||
await this.userVerificationApiService.postAccountVerifyOTP(request);
|
||||
} catch (e) {
|
||||
throw new Error(this.i18nService.t("invalidVerificationCode"));
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
||||
}
|
||||
|
||||
async requestOTP() {
|
||||
await this.apiService.postAccountRequestOTP();
|
||||
await this.userVerificationApiService.postAccountRequestOTP();
|
||||
}
|
||||
|
||||
private validateInput(verification: Verification) {
|
||||
@@ -9,7 +9,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
|
||||
private crypto: Crypto;
|
||||
private subtle: SubtleCrypto;
|
||||
|
||||
constructor(win: Window) {
|
||||
constructor(win: Window | typeof global) {
|
||||
this.crypto = typeof win.crypto !== "undefined" ? win.crypto : null;
|
||||
this.subtle =
|
||||
!!this.crypto && typeof win.crypto.subtle !== "undefined" ? win.crypto.subtle : null;
|
||||
|
||||
12
libs/common/src/types/authResponse.ts
Normal file
12
libs/common/src/types/authResponse.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { VerificationType } from "../enums/verificationType";
|
||||
|
||||
import { TwoFactorResponse } from "./twoFactorResponse";
|
||||
|
||||
export type AuthResponseBase = {
|
||||
secret: string;
|
||||
verificationType: VerificationType;
|
||||
};
|
||||
|
||||
export type AuthResponse<T extends TwoFactorResponse> = AuthResponseBase & {
|
||||
response: T;
|
||||
};
|
||||
14
libs/common/src/types/twoFactorResponse.ts
Normal file
14
libs/common/src/types/twoFactorResponse.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TwoFactorAuthenticatorResponse } from "../models/response/twoFactorAuthenticatorResponse";
|
||||
import { TwoFactorDuoResponse } from "../models/response/twoFactorDuoResponse";
|
||||
import { TwoFactorEmailResponse } from "../models/response/twoFactorEmailResponse";
|
||||
import { TwoFactorRecoverResponse } from "../models/response/twoFactorRescoverResponse";
|
||||
import { TwoFactorWebAuthnResponse } from "../models/response/twoFactorWebAuthnResponse";
|
||||
import { TwoFactorYubiKeyResponse } from "../models/response/twoFactorYubiKeyResponse";
|
||||
|
||||
export type TwoFactorResponse =
|
||||
| TwoFactorRecoverResponse
|
||||
| TwoFactorDuoResponse
|
||||
| TwoFactorEmailResponse
|
||||
| TwoFactorWebAuthnResponse
|
||||
| TwoFactorAuthenticatorResponse
|
||||
| TwoFactorYubiKeyResponse;
|
||||
@@ -5,23 +5,48 @@ import { ipcMain } from "electron";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { NodeUtils } from "@bitwarden/common/misc/nodeUtils";
|
||||
|
||||
// See: https://github.com/sindresorhus/electron-store/blob/main/index.d.ts
|
||||
interface ElectronStoreOptions {
|
||||
defaults: unknown;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type ElectronStoreConstructor = new (options: ElectronStoreOptions) => ElectronStore;
|
||||
|
||||
// eslint-disable-next-line
|
||||
const Store = require("electron-store");
|
||||
const Store: ElectronStoreConstructor = require("electron-store");
|
||||
|
||||
interface ElectronStore {
|
||||
get: (key: string) => unknown;
|
||||
set: (key: string, obj: unknown) => void;
|
||||
delete: (key: string) => void;
|
||||
}
|
||||
|
||||
interface BaseOptions<T extends string> {
|
||||
action: T;
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface SaveOptions extends BaseOptions<"save"> {
|
||||
obj: unknown;
|
||||
}
|
||||
|
||||
type Options = BaseOptions<"get"> | BaseOptions<"has"> | SaveOptions | BaseOptions<"remove">;
|
||||
|
||||
export class ElectronStorageService implements AbstractStorageService {
|
||||
private store: any;
|
||||
private store: ElectronStore;
|
||||
|
||||
constructor(dir: string, defaults = {}) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
NodeUtils.mkdirpSync(dir, "700");
|
||||
}
|
||||
const storeConfig: any = {
|
||||
const storeConfig: ElectronStoreOptions = {
|
||||
defaults: defaults,
|
||||
name: "data",
|
||||
};
|
||||
this.store = new Store(storeConfig);
|
||||
|
||||
ipcMain.handle("storageService", (event, options) => {
|
||||
ipcMain.handle("storageService", (event, options: Options) => {
|
||||
switch (options.action) {
|
||||
case "get":
|
||||
return this.get(options.key);
|
||||
@@ -45,7 +70,7 @@ export class ElectronStorageService implements AbstractStorageService {
|
||||
return Promise.resolve(val != null);
|
||||
}
|
||||
|
||||
save(key: string, obj: any): Promise<any> {
|
||||
save(key: string, obj: unknown): Promise<void> {
|
||||
if (obj instanceof Set) {
|
||||
obj = Array.from(obj);
|
||||
}
|
||||
@@ -53,7 +78,7 @@ export class ElectronStorageService implements AbstractStorageService {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
remove(key: string): Promise<any> {
|
||||
remove(key: string): Promise<void> {
|
||||
this.store.delete(key);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -30,7 +30,6 @@
|
||||
"big-integer": "^1.6.51",
|
||||
"bootstrap": "4.6.0",
|
||||
"braintree-web-drop-in": "^1.33.1",
|
||||
"browser-hrtime": "^1.1.8",
|
||||
"bufferutil": "^4.0.6",
|
||||
"chalk": "^4.1.0",
|
||||
"commander": "^7.2.0",
|
||||
@@ -83,7 +82,7 @@
|
||||
"@storybook/angular": "^6.5.7",
|
||||
"@storybook/builder-webpack5": "^6.5.7",
|
||||
"@storybook/manager-webpack5": "^6.5.7",
|
||||
"@types/chrome": "^0.0.139",
|
||||
"@types/chrome": "^0.0.190",
|
||||
"@types/duo_web_sdk": "^2.7.1",
|
||||
"@types/firefox-webext-browser": "^82.0.0",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
@@ -12504,9 +12503,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chrome": {
|
||||
"version": "0.0.139",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.139.tgz",
|
||||
"integrity": "sha512-YZDKFlSVGFp4zldJlO+PUpxMH8N9vLke0fD6K9PA+TzXxPXu8LBLo5X2dzlOs2N/n+uMdI1lw7OPT1Emop10lQ==",
|
||||
"version": "0.0.190",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.190.tgz",
|
||||
"integrity": "sha512-lCwwIBfaD+PhG62qFB46mIBpD+xBIa+PedNB24KR9YnmJ0Zn9h0OwP1NQBhI8Cbu1rKwTQHTxhs7GhWGyUvinw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/filesystem": "*",
|
||||
@@ -52084,9 +52083,9 @@
|
||||
}
|
||||
},
|
||||
"@types/chrome": {
|
||||
"version": "0.0.139",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.139.tgz",
|
||||
"integrity": "sha512-YZDKFlSVGFp4zldJlO+PUpxMH8N9vLke0fD6K9PA+TzXxPXu8LBLo5X2dzlOs2N/n+uMdI1lw7OPT1Emop10lQ==",
|
||||
"version": "0.0.190",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.190.tgz",
|
||||
"integrity": "sha512-lCwwIBfaD+PhG62qFB46mIBpD+xBIa+PedNB24KR9YnmJ0Zn9h0OwP1NQBhI8Cbu1rKwTQHTxhs7GhWGyUvinw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/filesystem": "*",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"@storybook/angular": "^6.5.7",
|
||||
"@storybook/builder-webpack5": "^6.5.7",
|
||||
"@storybook/manager-webpack5": "^6.5.7",
|
||||
"@types/chrome": "^0.0.139",
|
||||
"@types/chrome": "^0.0.190",
|
||||
"@types/duo_web_sdk": "^2.7.1",
|
||||
"@types/firefox-webext-browser": "^82.0.0",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
@@ -155,7 +155,6 @@
|
||||
"big-integer": "^1.6.51",
|
||||
"bootstrap": "4.6.0",
|
||||
"braintree-web-drop-in": "^1.33.1",
|
||||
"browser-hrtime": "^1.1.8",
|
||||
"bufferutil": "^4.0.6",
|
||||
"chalk": "^4.1.0",
|
||||
"commander": "^7.2.0",
|
||||
|
||||
Reference in New Issue
Block a user