mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 15:03:26 +00:00
merging master into feature branch
This commit is contained in:
@@ -3,7 +3,6 @@ const { pathsToModuleNameMapper } = require("ts-jest");
|
||||
const { compilerOptions } = require("../shared/tsconfig.libs");
|
||||
|
||||
module.exports = {
|
||||
name: "angular",
|
||||
displayName: "libs/angular tests",
|
||||
preset: "jest-preset-angular",
|
||||
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||
|
||||
@@ -9,6 +9,7 @@ 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 { Cipher } from "@bitwarden/common/models/domain/cipher";
|
||||
import { EncArrayBuffer } from "@bitwarden/common/models/domain/encArrayBuffer";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/errorResponse";
|
||||
import { AttachmentView } from "@bitwarden/common/models/view/attachmentView";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
@@ -167,12 +168,12 @@ export class AttachmentsComponent implements OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
const buf = await response.arrayBuffer();
|
||||
const encBuf = await EncArrayBuffer.fromResponse(response);
|
||||
const key =
|
||||
attachment.key != null
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
|
||||
this.fileDownloadService.download({
|
||||
fileName: attachment.fileName,
|
||||
blobData: decBuf,
|
||||
@@ -237,12 +238,12 @@ export class AttachmentsComponent implements OnInit {
|
||||
|
||||
try {
|
||||
// 2. Resave
|
||||
const buf = await response.arrayBuffer();
|
||||
const encBuf = await EncArrayBuffer.fromResponse(response);
|
||||
const key =
|
||||
attachment.key != null
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
|
||||
this.cipherDomain = await this.cipherService.saveAttachmentRawWithServer(
|
||||
this.cipherDomain,
|
||||
attachment.fileName,
|
||||
|
||||
@@ -12,20 +12,22 @@ import { EncString } from "@bitwarden/common/models/domain/encString";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||
|
||||
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
||||
|
||||
@Directive()
|
||||
export class ChangePasswordComponent implements OnInit {
|
||||
masterPassword: string;
|
||||
masterPasswordRetype: string;
|
||||
formPromise: Promise<any>;
|
||||
masterPasswordScore: number;
|
||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
passwordStrengthResult: any;
|
||||
color: string;
|
||||
text: string;
|
||||
|
||||
protected email: string;
|
||||
protected kdf: KdfType;
|
||||
protected kdfIterations: number;
|
||||
|
||||
private masterPasswordStrengthTimeout: any;
|
||||
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
protected cryptoService: CryptoService,
|
||||
@@ -116,10 +118,7 @@ export class ChangePasswordComponent implements OnInit {
|
||||
return false;
|
||||
}
|
||||
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
this.masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
const strengthResult = this.passwordStrengthResult;
|
||||
|
||||
if (
|
||||
this.enforcedPolicyOptions != null &&
|
||||
@@ -153,19 +152,6 @@ export class ChangePasswordComponent implements OnInit {
|
||||
return true;
|
||||
}
|
||||
|
||||
updatePasswordStrength() {
|
||||
if (this.masterPasswordStrengthTimeout != null) {
|
||||
clearTimeout(this.masterPasswordStrengthTimeout);
|
||||
}
|
||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
this.masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
async logOut() {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("logOutConfirmation"),
|
||||
@@ -178,18 +164,12 @@ export class ChangePasswordComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private getPasswordStrengthUserInput() {
|
||||
let userInput: string[] = [];
|
||||
const atPosition = this.email.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput.concat(
|
||||
this.email
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
);
|
||||
}
|
||||
return userInput;
|
||||
getStrengthResult(result: any) {
|
||||
this.passwordStrengthResult = result;
|
||||
}
|
||||
|
||||
getPasswordScoreText(event: PasswordColorText) {
|
||||
this.color = event.color;
|
||||
this.text = event.text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Directive, EventEmitter, OnInit, Output } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
@@ -47,7 +47,7 @@ export class ExportComponent implements OnInit {
|
||||
protected win: Window,
|
||||
private logService: LogService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private formBuilder: FormBuilder,
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
protected fileDownloadService: FileDownloadService,
|
||||
protected modalService: ModalService
|
||||
) {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { AbstractControl, FormBuilder, ValidatorFn, Validators } from "@angular/forms";
|
||||
import { AbstractControl, UntypedFormBuilder, ValidatorFn, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { InputsFieldMatch } from "@bitwarden/angular/validators/inputsFieldMatch.validator";
|
||||
@@ -22,6 +22,8 @@ import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
|
||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
|
||||
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
|
||||
|
||||
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
||||
|
||||
import { CaptchaProtectedComponent } from "./captchaProtected.component";
|
||||
|
||||
@Directive()
|
||||
@@ -31,10 +33,12 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
|
||||
showPassword = false;
|
||||
formPromise: Promise<any>;
|
||||
masterPasswordScore: number;
|
||||
referenceData: ReferenceEventRequest;
|
||||
showTerms = true;
|
||||
showErrorSummary = false;
|
||||
passwordStrengthResult: any;
|
||||
color: string;
|
||||
text: string;
|
||||
|
||||
formGroup = this.formBuilder.group(
|
||||
{
|
||||
@@ -63,11 +67,10 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
);
|
||||
|
||||
protected successRoute = "login";
|
||||
private masterPasswordStrengthTimeout: any;
|
||||
|
||||
constructor(
|
||||
protected formValidationErrorService: FormValidationErrorsService,
|
||||
protected formBuilder: FormBuilder,
|
||||
protected formBuilder: UntypedFormBuilder,
|
||||
protected authService: AuthService,
|
||||
protected router: Router,
|
||||
i18nService: I18nService,
|
||||
@@ -87,36 +90,6 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
this.setupCaptcha();
|
||||
}
|
||||
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return "success";
|
||||
case 3:
|
||||
return "primary";
|
||||
case 2:
|
||||
return "warning";
|
||||
default:
|
||||
return "danger";
|
||||
}
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t("strong");
|
||||
case 3:
|
||||
return this.i18nService.t("good");
|
||||
case 2:
|
||||
return this.i18nService.t("weak");
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
}
|
||||
}
|
||||
|
||||
async submit(showToast = true) {
|
||||
let email = this.formGroup.get("email")?.value;
|
||||
let name = this.formGroup.get("name")?.value;
|
||||
@@ -147,11 +120,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
return;
|
||||
}
|
||||
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
if (strengthResult != null && strengthResult.score < 3) {
|
||||
if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) {
|
||||
const result = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("weakMasterPasswordDesc"),
|
||||
this.i18nService.t("weakMasterPassword"),
|
||||
@@ -235,39 +204,13 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
this.showPassword = !this.showPassword;
|
||||
}
|
||||
|
||||
updatePasswordStrength() {
|
||||
const masterPassword = this.formGroup.get("masterPassword")?.value;
|
||||
|
||||
if (this.masterPasswordStrengthTimeout != null) {
|
||||
clearTimeout(this.masterPasswordStrengthTimeout);
|
||||
}
|
||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||
}, 300);
|
||||
getStrengthResult(result: any) {
|
||||
this.passwordStrengthResult = result;
|
||||
}
|
||||
|
||||
private getPasswordStrengthUserInput() {
|
||||
let userInput: string[] = [];
|
||||
const email = this.formGroup.get("email")?.value;
|
||||
const name = this.formGroup.get("name").value;
|
||||
const atPosition = email.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput.concat(
|
||||
email
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
);
|
||||
}
|
||||
if (name != null && name !== "") {
|
||||
userInput = userInput.concat(name.trim().toLowerCase().split(" "));
|
||||
}
|
||||
return userInput;
|
||||
getPasswordScoreText(event: PasswordColorText) {
|
||||
this.color = event.color;
|
||||
this.text = event.text;
|
||||
}
|
||||
|
||||
private getErrorToastMessage() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { FormControl, FormGroup } from "@angular/forms";
|
||||
import { UntypedFormControl, UntypedFormGroup } from "@angular/forms";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -62,15 +62,15 @@ export class EffluxDatesComponent implements OnInit {
|
||||
return BrowserPath.Default;
|
||||
}
|
||||
|
||||
datesForm = new FormGroup({
|
||||
selectedDeletionDatePreset: new FormControl(),
|
||||
selectedExpirationDatePreset: new FormControl(),
|
||||
defaultDeletionDateTime: new FormControl(),
|
||||
defaultExpirationDateTime: new FormControl(),
|
||||
fallbackDeletionDate: new FormControl(),
|
||||
fallbackDeletionTime: new FormControl(),
|
||||
fallbackExpirationDate: new FormControl(),
|
||||
fallbackExpirationTime: new FormControl(),
|
||||
datesForm = new UntypedFormGroup({
|
||||
selectedDeletionDatePreset: new UntypedFormControl(),
|
||||
selectedExpirationDatePreset: new UntypedFormControl(),
|
||||
defaultDeletionDateTime: new UntypedFormControl(),
|
||||
defaultExpirationDateTime: new UntypedFormControl(),
|
||||
fallbackDeletionDate: new UntypedFormControl(),
|
||||
fallbackDeletionTime: new UntypedFormControl(),
|
||||
fallbackExpirationDate: new UntypedFormControl(),
|
||||
fallbackExpirationTime: new UntypedFormControl(),
|
||||
});
|
||||
|
||||
deletionDatePresets: any[] = [
|
||||
@@ -87,36 +87,36 @@ export class EffluxDatesComponent implements OnInit {
|
||||
{ name: this.i18nService.t("never"), value: DatePreset.Never },
|
||||
].concat([...this.deletionDatePresets]);
|
||||
|
||||
get selectedDeletionDatePreset(): FormControl {
|
||||
return this.datesForm.get("selectedDeletionDatePreset") as FormControl;
|
||||
get selectedDeletionDatePreset(): UntypedFormControl {
|
||||
return this.datesForm.get("selectedDeletionDatePreset") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get selectedExpirationDatePreset(): FormControl {
|
||||
return this.datesForm.get("selectedExpirationDatePreset") as FormControl;
|
||||
get selectedExpirationDatePreset(): UntypedFormControl {
|
||||
return this.datesForm.get("selectedExpirationDatePreset") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get defaultDeletionDateTime(): FormControl {
|
||||
return this.datesForm.get("defaultDeletionDateTime") as FormControl;
|
||||
get defaultDeletionDateTime(): UntypedFormControl {
|
||||
return this.datesForm.get("defaultDeletionDateTime") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get defaultExpirationDateTime(): FormControl {
|
||||
return this.datesForm.get("defaultExpirationDateTime") as FormControl;
|
||||
get defaultExpirationDateTime(): UntypedFormControl {
|
||||
return this.datesForm.get("defaultExpirationDateTime") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get fallbackDeletionDate(): FormControl {
|
||||
return this.datesForm.get("fallbackDeletionDate") as FormControl;
|
||||
get fallbackDeletionDate(): UntypedFormControl {
|
||||
return this.datesForm.get("fallbackDeletionDate") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get fallbackDeletionTime(): FormControl {
|
||||
return this.datesForm.get("fallbackDeletionTime") as FormControl;
|
||||
get fallbackDeletionTime(): UntypedFormControl {
|
||||
return this.datesForm.get("fallbackDeletionTime") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get fallbackExpirationDate(): FormControl {
|
||||
return this.datesForm.get("fallbackExpirationDate") as FormControl;
|
||||
get fallbackExpirationDate(): UntypedFormControl {
|
||||
return this.datesForm.get("fallbackExpirationDate") as UntypedFormControl;
|
||||
}
|
||||
|
||||
get fallbackExpirationTime(): FormControl {
|
||||
return this.datesForm.get("fallbackExpirationTime") as FormControl;
|
||||
get fallbackExpirationTime(): UntypedFormControl {
|
||||
return this.datesForm.get("fallbackExpirationTime") as UntypedFormControl;
|
||||
}
|
||||
|
||||
// Should be able to call these at any time and compute a submitable value
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Directive, Input, OnInit } from "@angular/core";
|
||||
import {
|
||||
AbstractControl,
|
||||
ControlValueAccessor,
|
||||
FormBuilder,
|
||||
UntypedFormBuilder,
|
||||
ValidationErrors,
|
||||
Validator,
|
||||
} from "@angular/forms";
|
||||
@@ -44,7 +44,7 @@ export class VaultTimeoutInputComponent implements ControlValueAccessor, Validat
|
||||
private validatorChange: () => void;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private policyService: PolicyService,
|
||||
private i18nService: I18nService
|
||||
) {}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
import { ControlValueAccessor, UntypedFormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification.service";
|
||||
import { VerificationType } from "@bitwarden/common/enums/verificationType";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { Verification } from "@bitwarden/common/types/verification";
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
disableRequestOTP = false;
|
||||
sentCode = false;
|
||||
|
||||
secret = new FormControl("");
|
||||
secret = new UntypedFormControl("");
|
||||
|
||||
private onChange: (value: Verification) => void;
|
||||
|
||||
@@ -90,7 +91,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
|
||||
this.onChange({
|
||||
type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword,
|
||||
secret: secret,
|
||||
secret: Utils.isNullOrWhitespace(secret) ? null : secret,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType";
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
import { FieldType } from "@bitwarden/common/enums/fieldType";
|
||||
import { EncArrayBuffer } from "@bitwarden/common/models/domain/encArrayBuffer";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/errorResponse";
|
||||
import { AttachmentView } from "@bitwarden/common/models/view/attachmentView";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
@@ -369,12 +370,12 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
const buf = await response.arrayBuffer();
|
||||
const encBuf = await EncArrayBuffer.fromResponse(response);
|
||||
const key =
|
||||
attachment.key != null
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
|
||||
this.fileDownloadService.download({
|
||||
fileName: attachment.fileName,
|
||||
blobData: decBuf,
|
||||
|
||||
@@ -29,6 +29,7 @@ import { I18nPipe } from "./pipes/i18n.pipe";
|
||||
import { SearchCiphersPipe } from "./pipes/search-ciphers.pipe";
|
||||
import { SearchPipe } from "./pipes/search.pipe";
|
||||
import { UserNamePipe } from "./pipes/user-name.pipe";
|
||||
import { PasswordStrengthComponent } from "./shared/components/password-strength/password-strength.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -68,6 +69,7 @@ import { UserNamePipe } from "./pipes/user-name.pipe";
|
||||
StopPropDirective,
|
||||
TrueFalseValueDirective,
|
||||
UserNamePipe,
|
||||
PasswordStrengthComponent,
|
||||
],
|
||||
exports: [
|
||||
A11yInvalidDirective,
|
||||
@@ -97,6 +99,7 @@ import { UserNamePipe } from "./pipes/user-name.pipe";
|
||||
StopPropDirective,
|
||||
TrueFalseValueDirective,
|
||||
UserNamePipe,
|
||||
PasswordStrengthComponent,
|
||||
],
|
||||
providers: [CreditCardNumberPipe, DatePipe, I18nPipe, SearchPipe, UserNamePipe],
|
||||
})
|
||||
|
||||
@@ -3,6 +3,8 @@ import { InjectionToken, Injector, LOCALE_ID, NgModule } from "@angular/core";
|
||||
import { ThemingService } from "@bitwarden/angular/services/theming/theming.service";
|
||||
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
|
||||
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
|
||||
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/abstractions/account/account-api.service.abstraction";
|
||||
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/abstractions/account/account.service.abstraction";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/abstractions/appId.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
@@ -49,6 +51,8 @@ import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarde
|
||||
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||
import { Account } from "@bitwarden/common/models/domain/account";
|
||||
import { GlobalState } from "@bitwarden/common/models/domain/globalState";
|
||||
import { AccountApiService } from "@bitwarden/common/services/account/account-api.service";
|
||||
import { AccountService } from "@bitwarden/common/services/account/account.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AppIdService } from "@bitwarden/common/services/appId.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
@@ -234,6 +238,21 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
useClass: FolderApiService,
|
||||
deps: [FolderServiceAbstraction, ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AccountApiServiceAbstraction,
|
||||
useClass: AccountApiService,
|
||||
deps: [ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AccountServiceAbstraction,
|
||||
useClass: AccountService,
|
||||
deps: [
|
||||
AccountApiServiceAbstraction,
|
||||
UserVerificationServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
LogService,
|
||||
],
|
||||
},
|
||||
{ provide: LogService, useFactory: () => new ConsoleLogService(false) },
|
||||
{
|
||||
provide: CollectionServiceAbstraction,
|
||||
|
||||
@@ -17,7 +17,8 @@ import { ModalRef } from "../components/modal/modal.ref";
|
||||
|
||||
export class ModalConfig<D = any> {
|
||||
data?: D;
|
||||
allowMultipleModals = false;
|
||||
allowMultipleModals?: boolean;
|
||||
replaceTopModal?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -63,13 +64,18 @@ export class ModalService {
|
||||
return [modalRef, modalComponentRef.instance.componentRef.instance];
|
||||
}
|
||||
|
||||
open(componentType: Type<any>, config?: ModalConfig) {
|
||||
if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) {
|
||||
open(componentType: Type<any>, config: ModalConfig = {}) {
|
||||
const { replaceTopModal = false, allowMultipleModals = false } = config;
|
||||
|
||||
if (this.modalCount > 0 && replaceTopModal) {
|
||||
this.topModal.instance.close();
|
||||
}
|
||||
|
||||
if (this.modalCount > 0 && !allowMultipleModals) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const [modalRef, _] = this.openInternal(componentType, config, true);
|
||||
const [modalRef] = this.openInternal(componentType, config, true);
|
||||
|
||||
return modalRef;
|
||||
}
|
||||
@@ -89,6 +95,10 @@ export class ModalService {
|
||||
return this.componentFactoryResolver.resolveComponentFactory(componentType);
|
||||
}
|
||||
|
||||
closeAll(): void {
|
||||
this.modalList.forEach((modal) => modal.instance.close());
|
||||
}
|
||||
|
||||
protected openInternal(
|
||||
componentType: Type<any>,
|
||||
config?: ModalConfig,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar {{ color }}"
|
||||
role="progressbar"
|
||||
[ngStyle]="{ width: scoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ scoreWidth }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
>
|
||||
<ng-container *ngIf="showText && text">
|
||||
{{ text }}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,133 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
|
||||
export interface PasswordColorText {
|
||||
color: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-password-strength",
|
||||
templateUrl: "password-strength.component.html",
|
||||
})
|
||||
export class PasswordStrengthComponent implements OnChanges {
|
||||
@Input() showText = false;
|
||||
@Input() email: string;
|
||||
@Input() password: string;
|
||||
@Input() name: string;
|
||||
|
||||
@Output() passwordStrengthResult = new EventEmitter<any>();
|
||||
@Output() passwordScoreColor = new EventEmitter<PasswordColorText>();
|
||||
|
||||
masterPasswordScore: number;
|
||||
scoreWidth = 0;
|
||||
color = "bg-danger";
|
||||
text: string;
|
||||
|
||||
private masterPasswordStrengthTimeout: any;
|
||||
|
||||
//used by desktop and browser to display strength text color
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return "success";
|
||||
case 3:
|
||||
return "primary";
|
||||
case 2:
|
||||
return "warning";
|
||||
default:
|
||||
return "danger";
|
||||
}
|
||||
}
|
||||
|
||||
//used by desktop and browser to display strength text
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t("strong");
|
||||
case 3:
|
||||
return this.i18nService.t("good");
|
||||
case 2:
|
||||
return this.i18nService.t("weak");
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private passwordGenerationService: PasswordGenerationService
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
||||
this.updatePasswordStrength(changes.password?.currentValue);
|
||||
|
||||
this.scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
this.color = "bg-success";
|
||||
this.text = this.i18nService.t("strong");
|
||||
break;
|
||||
case 3:
|
||||
this.color = "bg-primary";
|
||||
this.text = this.i18nService.t("good");
|
||||
break;
|
||||
case 2:
|
||||
this.color = "bg-warning";
|
||||
this.text = this.i18nService.t("weak");
|
||||
break;
|
||||
default:
|
||||
this.color = "bg-danger";
|
||||
this.text = this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
break;
|
||||
}
|
||||
|
||||
this.setPasswordScoreText(this.color, this.text);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
updatePasswordStrength(password: string) {
|
||||
const masterPassword = password;
|
||||
|
||||
if (this.masterPasswordStrengthTimeout != null) {
|
||||
clearTimeout(this.masterPasswordStrengthTimeout);
|
||||
}
|
||||
|
||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||
masterPassword,
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
this.passwordStrengthResult.emit(strengthResult);
|
||||
|
||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||
}
|
||||
|
||||
getPasswordStrengthUserInput() {
|
||||
let userInput: string[] = [];
|
||||
const email = this.email;
|
||||
const name = this.name;
|
||||
const atPosition = email.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput.concat(
|
||||
email
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
);
|
||||
}
|
||||
if (name != null && name !== "") {
|
||||
userInput = userInput.concat(name.trim().toLowerCase().split(" "));
|
||||
}
|
||||
return userInput;
|
||||
}
|
||||
|
||||
setPasswordScoreText(color: string, text: string) {
|
||||
color = color.slice(3);
|
||||
this.passwordScoreColor.emit({ color: color, text: text });
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractControl, FormGroup, ValidatorFn } from "@angular/forms";
|
||||
import { AbstractControl, UntypedFormGroup, ValidatorFn } from "@angular/forms";
|
||||
|
||||
import { FormGroupControls } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||
|
||||
@@ -39,7 +39,7 @@ export class InputsFieldMatch {
|
||||
|
||||
//checks the formGroup if two fields have the same value and validation is controlled from either field
|
||||
static validateFormInputsMatch(field: string, fieldMatchTo: string, errorMessage: string) {
|
||||
return (formGroup: FormGroup) => {
|
||||
return (formGroup: UntypedFormGroup) => {
|
||||
const fieldCtrl = formGroup.controls[field];
|
||||
const fieldMatchToCtrl = formGroup.controls[fieldMatchTo];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user