mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-17 16:53:25 +00:00
Copy jslib into Directory Connector [TI-6] (#262)
This commit is contained in:
35
jslib/angular/src/components/callout.component.html
Normal file
35
jslib/angular/src/components/callout.component.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<div
|
||||
#callout
|
||||
class="callout callout-{{ calloutStyle }}"
|
||||
[ngClass]="{ clickable: clickable }"
|
||||
[attr.role]="useAlertRole ? 'alert' : null"
|
||||
>
|
||||
<h3 class="callout-heading" *ngIf="title">
|
||||
<i class="bwi {{ icon }}" *ngIf="icon" aria-hidden="true"></i>
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="enforced-policy-options" *ngIf="enforcedPolicyOptions">
|
||||
{{ enforcedPolicyMessage }}
|
||||
<ul>
|
||||
<li *ngIf="enforcedPolicyOptions?.minComplexity > 0">
|
||||
{{ "policyInEffectMinComplexity" | i18n: getPasswordScoreAlertDisplay() }}
|
||||
</li>
|
||||
<li *ngIf="enforcedPolicyOptions?.minLength > 0">
|
||||
{{ "policyInEffectMinLength" | i18n: enforcedPolicyOptions?.minLength.toString() }}
|
||||
</li>
|
||||
<li *ngIf="enforcedPolicyOptions?.requireUpper">
|
||||
{{ "policyInEffectUppercase" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="enforcedPolicyOptions?.requireLower">
|
||||
{{ "policyInEffectLowercase" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="enforcedPolicyOptions?.requireNumbers">
|
||||
{{ "policyInEffectNumbers" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="enforcedPolicyOptions?.requireSpecial">
|
||||
{{ "policyInEffectSpecial" | i18n: "!@#$%^&*" }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
78
jslib/angular/src/components/callout.component.ts
Normal file
78
jslib/angular/src/components/callout.component.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
|
||||
|
||||
@Component({
|
||||
selector: "app-callout",
|
||||
templateUrl: "callout.component.html",
|
||||
})
|
||||
export class CalloutComponent implements OnInit {
|
||||
@Input() type = "info";
|
||||
@Input() icon: string;
|
||||
@Input() title: string;
|
||||
@Input() clickable: boolean;
|
||||
@Input() enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
@Input() enforcedPolicyMessage: string;
|
||||
@Input() useAlertRole = false;
|
||||
|
||||
calloutStyle: string;
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.calloutStyle = this.type;
|
||||
|
||||
if (this.enforcedPolicyMessage === undefined) {
|
||||
this.enforcedPolicyMessage = this.i18nService.t("masterPasswordPolicyInEffect");
|
||||
}
|
||||
|
||||
if (this.type === "warning" || this.type === "danger") {
|
||||
if (this.type === "danger") {
|
||||
this.calloutStyle = "danger";
|
||||
}
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("warning");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-exclamation-triangle";
|
||||
}
|
||||
} else if (this.type === "error") {
|
||||
this.calloutStyle = "danger";
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("error");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-error";
|
||||
}
|
||||
} else if (this.type === "tip") {
|
||||
this.calloutStyle = "success";
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("tip");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-lightbulb";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getPasswordScoreAlertDisplay() {
|
||||
if (this.enforcedPolicyOptions == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let str: string;
|
||||
switch (this.enforcedPolicyOptions.minComplexity) {
|
||||
case 4:
|
||||
str = this.i18nService.t("strong");
|
||||
break;
|
||||
case 3:
|
||||
str = this.i18nService.t("good");
|
||||
break;
|
||||
default:
|
||||
str = this.i18nService.t("weak");
|
||||
break;
|
||||
}
|
||||
return str + " (" + this.enforcedPolicyOptions.minComplexity + ")";
|
||||
}
|
||||
}
|
||||
63
jslib/angular/src/components/environment.component.ts
Normal file
63
jslib/angular/src/components/environment.component.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Directive, EventEmitter, Output } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
@Directive()
|
||||
export class EnvironmentComponent {
|
||||
@Output() onSaved = new EventEmitter();
|
||||
|
||||
iconsUrl: string;
|
||||
identityUrl: string;
|
||||
apiUrl: string;
|
||||
webVaultUrl: string;
|
||||
notificationsUrl: string;
|
||||
baseUrl: string;
|
||||
showCustom = false;
|
||||
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected i18nService: I18nService
|
||||
) {
|
||||
const urls = this.environmentService.getUrls();
|
||||
|
||||
this.baseUrl = urls.base || "";
|
||||
this.webVaultUrl = urls.webVault || "";
|
||||
this.apiUrl = urls.api || "";
|
||||
this.identityUrl = urls.identity || "";
|
||||
this.iconsUrl = urls.icons || "";
|
||||
this.notificationsUrl = urls.notifications || "";
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const resUrls = await this.environmentService.setUrls({
|
||||
base: this.baseUrl,
|
||||
api: this.apiUrl,
|
||||
identity: this.identityUrl,
|
||||
webVault: this.webVaultUrl,
|
||||
icons: this.iconsUrl,
|
||||
notifications: this.notificationsUrl,
|
||||
});
|
||||
|
||||
// re-set urls since service can change them, ex: prefixing https://
|
||||
this.baseUrl = resUrls.base;
|
||||
this.apiUrl = resUrls.api;
|
||||
this.identityUrl = resUrls.identity;
|
||||
this.webVaultUrl = resUrls.webVault;
|
||||
this.iconsUrl = resUrls.icons;
|
||||
this.notificationsUrl = resUrls.notifications;
|
||||
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("environmentSaved"));
|
||||
this.saved();
|
||||
}
|
||||
|
||||
toggleCustom() {
|
||||
this.showCustom = !this.showCustom;
|
||||
}
|
||||
|
||||
protected saved() {
|
||||
this.onSaved.emit();
|
||||
}
|
||||
}
|
||||
11
jslib/angular/src/components/icon.component.html
Normal file
11
jslib/angular/src/components/icon.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<div class="icon" aria-hidden="true">
|
||||
<img
|
||||
[src]="image"
|
||||
appFallbackSrc="{{ fallbackImage }}"
|
||||
*ngIf="imageEnabled && image"
|
||||
alt=""
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
/>
|
||||
<i class="bwi bwi-fw bwi-lg {{ icon }}" *ngIf="!imageEnabled || !image"></i>
|
||||
</div>
|
||||
112
jslib/angular/src/components/icon.component.ts
Normal file
112
jslib/angular/src/components/icon.component.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Component, Input, OnChanges } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { CipherType } from "jslib-common/enums/cipherType";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
/**
|
||||
* Provides a mapping from supported card brands to
|
||||
* the filenames of icon that should be present in images/cards folder of clients.
|
||||
*/
|
||||
const cardIcons: Record<string, string> = {
|
||||
Visa: "card-visa",
|
||||
Mastercard: "card-mastercard",
|
||||
Amex: "card-amex",
|
||||
Discover: "card-discover",
|
||||
"Diners Club": "card-diners-club",
|
||||
JCB: "card-jcb",
|
||||
Maestro: "card-maestro",
|
||||
UnionPay: "card-union-pay",
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-icon",
|
||||
templateUrl: "icon.component.html",
|
||||
})
|
||||
export class IconComponent implements OnChanges {
|
||||
@Input() cipher: CipherView;
|
||||
icon: string;
|
||||
image: string;
|
||||
fallbackImage: string;
|
||||
imageEnabled: boolean;
|
||||
|
||||
private iconsUrl: string;
|
||||
|
||||
constructor(environmentService: EnvironmentService, private stateService: StateService) {
|
||||
this.iconsUrl = environmentService.getIconsUrl();
|
||||
}
|
||||
|
||||
async ngOnChanges() {
|
||||
// Components may be re-used when using cdk-virtual-scroll. Which puts the component in a weird state,
|
||||
// to avoid this we reset all state variables.
|
||||
this.image = null;
|
||||
this.fallbackImage = null;
|
||||
this.imageEnabled = !(await this.stateService.getDisableFavicon());
|
||||
this.load();
|
||||
}
|
||||
|
||||
protected load() {
|
||||
switch (this.cipher.type) {
|
||||
case CipherType.Login:
|
||||
this.icon = "bwi-globe";
|
||||
this.setLoginIcon();
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
this.icon = "bwi-sticky-note";
|
||||
break;
|
||||
case CipherType.Card:
|
||||
this.icon = "bwi-credit-card";
|
||||
this.setCardIcon();
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
this.icon = "bwi-id-card";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private setLoginIcon() {
|
||||
if (this.cipher.login.uri) {
|
||||
let hostnameUri = this.cipher.login.uri;
|
||||
let isWebsite = false;
|
||||
|
||||
if (hostnameUri.indexOf("androidapp://") === 0) {
|
||||
this.icon = "bwi-android";
|
||||
this.image = null;
|
||||
} else if (hostnameUri.indexOf("iosapp://") === 0) {
|
||||
this.icon = "bwi-apple";
|
||||
this.image = null;
|
||||
} else if (
|
||||
this.imageEnabled &&
|
||||
hostnameUri.indexOf("://") === -1 &&
|
||||
hostnameUri.indexOf(".") > -1
|
||||
) {
|
||||
hostnameUri = "http://" + hostnameUri;
|
||||
isWebsite = true;
|
||||
} else if (this.imageEnabled) {
|
||||
isWebsite = hostnameUri.indexOf("http") === 0 && hostnameUri.indexOf(".") > -1;
|
||||
}
|
||||
|
||||
if (this.imageEnabled && isWebsite) {
|
||||
try {
|
||||
this.image = this.iconsUrl + "/" + Utils.getHostname(hostnameUri) + "/icon.png";
|
||||
this.fallbackImage = "images/bwi-globe.png";
|
||||
} catch (e) {
|
||||
// Ignore error since the fallback icon will be shown if image is null.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.image = null;
|
||||
}
|
||||
}
|
||||
|
||||
private setCardIcon() {
|
||||
const brand = this.cipher.card.brand;
|
||||
if (this.imageEnabled && brand in cardIcons) {
|
||||
this.icon = "credit-card-icon " + cardIcons[brand];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from "@angular/cdk/a11y";
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ComponentRef,
|
||||
ElementRef,
|
||||
OnDestroy,
|
||||
Type,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from "@angular/core";
|
||||
|
||||
import { ModalService } from "../../services/modal.service";
|
||||
|
||||
import { ModalRef } from "./modal.ref";
|
||||
|
||||
@Component({
|
||||
selector: "app-modal",
|
||||
template: "<ng-template #modalContent></ng-template>",
|
||||
})
|
||||
export class DynamicModalComponent implements AfterViewInit, OnDestroy {
|
||||
componentRef: ComponentRef<any>;
|
||||
|
||||
@ViewChild("modalContent", { read: ViewContainerRef, static: true })
|
||||
modalContentRef: ViewContainerRef;
|
||||
|
||||
childComponentType: Type<any>;
|
||||
setComponentParameters: (component: any) => void;
|
||||
|
||||
private focusTrap: ConfigurableFocusTrap;
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private el: ElementRef<HTMLElement>,
|
||||
private focusTrapFactory: ConfigurableFocusTrapFactory,
|
||||
public modalRef: ModalRef
|
||||
) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.loadChildComponent(this.childComponentType);
|
||||
if (this.setComponentParameters != null) {
|
||||
this.setComponentParameters(this.componentRef.instance);
|
||||
}
|
||||
this.cd.detectChanges();
|
||||
|
||||
this.modalRef.created(this.el.nativeElement);
|
||||
this.focusTrap = this.focusTrapFactory.create(
|
||||
this.el.nativeElement.querySelector(".modal-dialog")
|
||||
);
|
||||
if (this.el.nativeElement.querySelector("[appAutoFocus]") == null) {
|
||||
this.focusTrap.focusFirstTabbableElementWhenReady();
|
||||
}
|
||||
}
|
||||
|
||||
loadChildComponent(componentType: Type<any>) {
|
||||
const componentFactory = this.modalService.resolveComponentFactory(componentType);
|
||||
|
||||
this.modalContentRef.clear();
|
||||
this.componentRef = this.modalContentRef.createComponent(componentFactory);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.componentRef) {
|
||||
this.componentRef.destroy();
|
||||
}
|
||||
this.focusTrap.destroy();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.modalRef.close();
|
||||
}
|
||||
|
||||
getFocus() {
|
||||
const autoFocusEl = this.el.nativeElement.querySelector("[appAutoFocus]") as HTMLElement;
|
||||
autoFocusEl?.focus();
|
||||
}
|
||||
}
|
||||
10
jslib/angular/src/components/modal/modal-injector.ts
Normal file
10
jslib/angular/src/components/modal/modal-injector.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { InjectFlags, InjectionToken, Injector, Type } from "@angular/core";
|
||||
|
||||
export class ModalInjector implements Injector {
|
||||
constructor(private _parentInjector: Injector, private _additionalTokens: WeakMap<any, any>) {}
|
||||
|
||||
get<T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
||||
get(token: any, notFoundValue?: any, flags?: any) {
|
||||
return this._additionalTokens.get(token) ?? this._parentInjector.get<any>(token, notFoundValue);
|
||||
}
|
||||
}
|
||||
50
jslib/angular/src/components/modal/modal.ref.ts
Normal file
50
jslib/angular/src/components/modal/modal.ref.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
export class ModalRef {
|
||||
onCreated: Observable<HTMLElement>; // Modal added to the DOM.
|
||||
onClose: Observable<any>; // Initiated close.
|
||||
onClosed: Observable<any>; // Modal was closed (Remove element from DOM)
|
||||
onShow: Observable<void>; // Start showing modal
|
||||
onShown: Observable<void>; // Modal is fully visible
|
||||
|
||||
private readonly _onCreated = new Subject<HTMLElement>();
|
||||
private readonly _onClose = new Subject<any>();
|
||||
private readonly _onClosed = new Subject<any>();
|
||||
private readonly _onShow = new Subject<void>();
|
||||
private readonly _onShown = new Subject<void>();
|
||||
private lastResult: any;
|
||||
|
||||
constructor() {
|
||||
this.onCreated = this._onCreated.asObservable();
|
||||
this.onClose = this._onClose.asObservable();
|
||||
this.onClosed = this._onClosed.asObservable();
|
||||
this.onShow = this._onShow.asObservable();
|
||||
this.onShown = this._onShow.asObservable();
|
||||
}
|
||||
|
||||
show() {
|
||||
this._onShow.next();
|
||||
}
|
||||
|
||||
shown() {
|
||||
this._onShown.next();
|
||||
}
|
||||
|
||||
close(result?: any) {
|
||||
this.lastResult = result;
|
||||
this._onClose.next(result);
|
||||
}
|
||||
|
||||
closed() {
|
||||
this._onClosed.next(this.lastResult);
|
||||
}
|
||||
|
||||
created(el: HTMLElement) {
|
||||
this._onCreated.next(el);
|
||||
}
|
||||
|
||||
onClosedPromise(): Promise<any> {
|
||||
return this.onClosed.pipe(first()).toPromise();
|
||||
}
|
||||
}
|
||||
41
jslib/angular/src/components/password-reprompt.component.ts
Normal file
41
jslib/angular/src/components/password-reprompt.component.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Directive } from "@angular/core";
|
||||
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { ModalRef } from "./modal/modal.ref";
|
||||
|
||||
/**
|
||||
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||
* See UserVerificationComponent for any other situation where you need to verify the user's identity.
|
||||
*/
|
||||
@Directive()
|
||||
export class PasswordRepromptComponent {
|
||||
showPassword = false;
|
||||
masterPassword = "";
|
||||
|
||||
constructor(
|
||||
private modalRef: ModalRef,
|
||||
private cryptoService: CryptoService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService
|
||||
) {}
|
||||
|
||||
togglePassword() {
|
||||
this.showPassword = !this.showPassword;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null))) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("invalidMasterPassword")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalRef.close(true);
|
||||
}
|
||||
}
|
||||
95
jslib/angular/src/components/toastr.component.ts
Normal file
95
jslib/angular/src/components/toastr.component.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, ModuleWithProviders, NgModule } from "@angular/core";
|
||||
import {
|
||||
DefaultNoComponentGlobalConfig,
|
||||
GlobalConfig,
|
||||
Toast as BaseToast,
|
||||
ToastPackage,
|
||||
ToastrService,
|
||||
TOAST_CONFIG,
|
||||
} from "ngx-toastr";
|
||||
|
||||
@Component({
|
||||
selector: "[toast-component2]",
|
||||
template: `
|
||||
<button
|
||||
*ngIf="options.closeButton"
|
||||
(click)="remove()"
|
||||
type="button"
|
||||
class="toast-close-button"
|
||||
aria-label="Close"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<div class="icon">
|
||||
<i></i>
|
||||
</div>
|
||||
<div>
|
||||
<div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
|
||||
{{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="message && options.enableHtml"
|
||||
role="alertdialog"
|
||||
aria-live="polite"
|
||||
[class]="options.messageClass"
|
||||
[innerHTML]="message"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="message && !options.enableHtml"
|
||||
role="alertdialog"
|
||||
aria-live="polite"
|
||||
[class]="options.messageClass"
|
||||
[attr.aria-label]="message"
|
||||
>
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="options.progressBar">
|
||||
<div class="toast-progress" [style.width]="width + '%'"></div>
|
||||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger("flyInOut", [
|
||||
state("inactive", style({ opacity: 0 })),
|
||||
state("active", style({ opacity: 1 })),
|
||||
state("removed", style({ opacity: 0 })),
|
||||
transition("inactive => active", animate("{{ easeTime }}ms {{ easing }}")),
|
||||
transition("active => removed", animate("{{ easeTime }}ms {{ easing }}")),
|
||||
]),
|
||||
],
|
||||
preserveWhitespaces: false,
|
||||
})
|
||||
export class BitwardenToast extends BaseToast {
|
||||
constructor(protected toastrService: ToastrService, public toastPackage: ToastPackage) {
|
||||
super(toastrService, toastPackage);
|
||||
}
|
||||
}
|
||||
|
||||
export const BitwardenToastGlobalConfig: GlobalConfig = {
|
||||
...DefaultNoComponentGlobalConfig,
|
||||
toastComponent: BitwardenToast,
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
declarations: [BitwardenToast],
|
||||
exports: [BitwardenToast],
|
||||
})
|
||||
export class BitwardenToastModule {
|
||||
static forRoot(config: Partial<GlobalConfig> = {}): ModuleWithProviders<BitwardenToastModule> {
|
||||
return {
|
||||
ngModule: BitwardenToastModule,
|
||||
providers: [
|
||||
{
|
||||
provide: TOAST_CONFIG,
|
||||
useValue: {
|
||||
default: BitwardenToastGlobalConfig,
|
||||
config: config,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<ng-container *ngIf="!usesKeyConnector">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="password"
|
||||
name="MasterPasswordHash"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usesKeyConnector">
|
||||
<div class="form-group">
|
||||
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="requestOTP()"
|
||||
[disabled]="disableRequestOTP"
|
||||
>
|
||||
{{ "sendCode" | i18n }}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "codeSent" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="verificationCode"
|
||||
type="input"
|
||||
name="verificationCode"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
96
jslib/angular/src/components/user-verification.component.ts
Normal file
96
jslib/angular/src/components/user-verification.component.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
import { VerificationType } from "jslib-common/enums/verificationType";
|
||||
import { Verification } from "jslib-common/types/verification";
|
||||
|
||||
/**
|
||||
* Used for general-purpose user verification throughout the app.
|
||||
* Collects the user's master password, or if they are using Key Connector, prompts for an OTP via email.
|
||||
* This is exposed to the parent component via the ControlValueAccessor interface (e.g. bind it to a FormControl).
|
||||
* Use UserVerificationService to verify the user's input.
|
||||
*/
|
||||
@Component({
|
||||
selector: "app-user-verification",
|
||||
templateUrl: "user-verification.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: UserVerificationComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger("sent", [
|
||||
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
usesKeyConnector = false;
|
||||
disableRequestOTP = false;
|
||||
sentCode = false;
|
||||
|
||||
secret = new FormControl("");
|
||||
|
||||
private onChange: (value: Verification) => void;
|
||||
|
||||
constructor(
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private userVerificationService: UserVerificationService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||
this.processChanges(this.secret.value);
|
||||
|
||||
this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret));
|
||||
}
|
||||
|
||||
async requestOTP() {
|
||||
if (this.usesKeyConnector) {
|
||||
this.disableRequestOTP = true;
|
||||
try {
|
||||
await this.userVerificationService.requestOTP();
|
||||
this.sentCode = true;
|
||||
} finally {
|
||||
this.disableRequestOTP = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
this.secret.setValue(obj);
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disableRequestOTP = isDisabled;
|
||||
if (isDisabled) {
|
||||
this.secret.disable();
|
||||
} else {
|
||||
this.secret.enable();
|
||||
}
|
||||
}
|
||||
|
||||
private processChanges(secret: string) {
|
||||
if (this.onChange == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onChange({
|
||||
type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword,
|
||||
secret: secret,
|
||||
});
|
||||
}
|
||||
}
|
||||
23
jslib/angular/src/directives/a11y-title.directive.ts
Normal file
23
jslib/angular/src/directives/a11y-title.directive.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Directive, ElementRef, Input, Renderer2 } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appA11yTitle]",
|
||||
})
|
||||
export class A11yTitleDirective {
|
||||
@Input() set appA11yTitle(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private title: string;
|
||||
|
||||
constructor(private el: ElementRef, private renderer: Renderer2) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.el.nativeElement.hasAttribute("title")) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, "title", this.title);
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute("aria-label")) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, "aria-label", this.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
jslib/angular/src/directives/api-action.directive.ts
Normal file
49
jslib/angular/src/directives/api-action.directive.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Directive, ElementRef, Input, OnChanges } from "@angular/core";
|
||||
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { ErrorResponse } from "jslib-common/models/response/errorResponse";
|
||||
|
||||
import { ValidationService } from "../services/validation.service";
|
||||
|
||||
/**
|
||||
* Provides error handling, in particular for any error returned by the server in an api call.
|
||||
* Attach it to a <form> element and provide the name of the class property that will hold the api call promise.
|
||||
* e.g. <form [appApiAction]="this.formPromise">
|
||||
* Any errors/rejections that occur will be intercepted and displayed as error toasts.
|
||||
*/
|
||||
@Directive({
|
||||
selector: "[appApiAction]",
|
||||
})
|
||||
export class ApiActionDirective implements OnChanges {
|
||||
@Input() appApiAction: Promise<any>;
|
||||
|
||||
constructor(
|
||||
private el: ElementRef,
|
||||
private validationService: ValidationService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.appApiAction == null || this.appApiAction.then == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.nativeElement.loading = true;
|
||||
|
||||
this.appApiAction.then(
|
||||
(response: any) => {
|
||||
this.el.nativeElement.loading = false;
|
||||
},
|
||||
(e: any) => {
|
||||
this.el.nativeElement.loading = false;
|
||||
|
||||
if ((e as ErrorResponse).captchaRequired) {
|
||||
this.logService.error("Captcha required error response: " + e.getSingleMessage());
|
||||
return;
|
||||
}
|
||||
this.logService?.error(`Received API exception: ${e}`);
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
27
jslib/angular/src/directives/autofocus.directive.ts
Normal file
27
jslib/angular/src/directives/autofocus.directive.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Directive, ElementRef, Input, NgZone } from "@angular/core";
|
||||
import { take } from "rxjs/operators";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Directive({
|
||||
selector: "[appAutofocus]",
|
||||
})
|
||||
export class AutofocusDirective {
|
||||
@Input() set appAutofocus(condition: boolean | string) {
|
||||
this.autofocus = condition === "" || condition === true;
|
||||
}
|
||||
|
||||
private autofocus: boolean;
|
||||
|
||||
constructor(private el: ElementRef, private ngZone: NgZone) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (!Utils.isMobileBrowser && this.autofocus) {
|
||||
if (this.ngZone.isStable) {
|
||||
this.el.nativeElement.focus();
|
||||
} else {
|
||||
this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
jslib/angular/src/directives/blur-click.directive.ts
Normal file
12
jslib/angular/src/directives/blur-click.directive.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Directive, ElementRef, HostListener } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appBlurClick]",
|
||||
})
|
||||
export class BlurClickDirective {
|
||||
constructor(private el: ElementRef) {}
|
||||
|
||||
@HostListener("click") onClick() {
|
||||
this.el.nativeElement.blur();
|
||||
}
|
||||
}
|
||||
59
jslib/angular/src/directives/box-row.directive.ts
Normal file
59
jslib/angular/src/directives/box-row.directive.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Directive, ElementRef, HostListener, OnInit } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appBoxRow]",
|
||||
})
|
||||
export class BoxRowDirective implements OnInit {
|
||||
el: HTMLElement = null;
|
||||
formEls: Element[];
|
||||
|
||||
constructor(elRef: ElementRef) {
|
||||
this.el = elRef.nativeElement;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.formEls = Array.from(
|
||||
this.el.querySelectorAll('input:not([type="hidden"]), select, textarea')
|
||||
);
|
||||
this.formEls.forEach((formEl) => {
|
||||
formEl.addEventListener(
|
||||
"focus",
|
||||
() => {
|
||||
this.el.classList.add("active");
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
formEl.addEventListener(
|
||||
"blur",
|
||||
() => {
|
||||
this.el.classList.remove("active");
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener("click", ["$event"]) onClick(event: Event) {
|
||||
const target = event.target as HTMLElement;
|
||||
if (
|
||||
target !== this.el &&
|
||||
!target.classList.contains("progress") &&
|
||||
!target.classList.contains("progress-bar")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.formEls.length > 0) {
|
||||
const formEl = this.formEls[0] as HTMLElement;
|
||||
if (formEl.tagName.toLowerCase() === "input") {
|
||||
const inputEl = formEl as HTMLInputElement;
|
||||
if (inputEl.type != null && inputEl.type.toLowerCase() === "checkbox") {
|
||||
inputEl.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
formEl.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
jslib/angular/src/directives/fallback-src.directive.ts
Normal file
14
jslib/angular/src/directives/fallback-src.directive.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Directive, ElementRef, HostListener, Input } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appFallbackSrc]",
|
||||
})
|
||||
export class FallbackSrcDirective {
|
||||
@Input("appFallbackSrc") appFallbackSrc: string;
|
||||
|
||||
constructor(private el: ElementRef) {}
|
||||
|
||||
@HostListener("error") onError() {
|
||||
this.el.nativeElement.src = this.appFallbackSrc;
|
||||
}
|
||||
}
|
||||
10
jslib/angular/src/directives/stop-click.directive.ts
Normal file
10
jslib/angular/src/directives/stop-click.directive.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Directive, HostListener } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appStopClick]",
|
||||
})
|
||||
export class StopClickDirective {
|
||||
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
10
jslib/angular/src/directives/stop-prop.directive.ts
Normal file
10
jslib/angular/src/directives/stop-prop.directive.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Directive, HostListener } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[appStopProp]",
|
||||
})
|
||||
export class StopPropDirective {
|
||||
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
14
jslib/angular/src/pipes/i18n.pipe.ts
Normal file
14
jslib/angular/src/pipes/i18n.pipe.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Pipe({
|
||||
name: "i18n",
|
||||
})
|
||||
export class I18nPipe implements PipeTransform {
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
transform(id: string, p1?: string, p2?: string, p3?: string): string {
|
||||
return this.i18nService.t(id, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
41
jslib/angular/src/pipes/search-ciphers.pipe.ts
Normal file
41
jslib/angular/src/pipes/search-ciphers.pipe.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
@Pipe({
|
||||
name: "searchCiphers",
|
||||
})
|
||||
export class SearchCiphersPipe implements PipeTransform {
|
||||
transform(ciphers: CipherView[], searchText: string, deleted = false): CipherView[] {
|
||||
if (ciphers == null || ciphers.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (searchText == null || searchText.length < 2) {
|
||||
return ciphers.filter((c) => {
|
||||
return deleted !== c.isDeleted;
|
||||
});
|
||||
}
|
||||
|
||||
searchText = searchText.trim().toLowerCase();
|
||||
return ciphers.filter((c) => {
|
||||
if (deleted !== c.isDeleted) {
|
||||
return false;
|
||||
}
|
||||
if (c.name != null && c.name.toLowerCase().indexOf(searchText) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (searchText.length >= 8 && c.id.startsWith(searchText)) {
|
||||
return true;
|
||||
}
|
||||
if (c.subTitle != null && c.subTitle.toLowerCase().indexOf(searchText) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (c.login && c.login.uri != null && c.login.uri.toLowerCase().indexOf(searchText) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
170
jslib/angular/src/scss/bwicons/fonts/bwi-font.svg
Normal file
170
jslib/angular/src/scss/bwicons/fonts/bwi-font.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 262 KiB |
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.ttf
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2
Normal file
BIN
jslib/angular/src/scss/bwicons/fonts/bwi-font.woff2
Normal file
Binary file not shown.
250
jslib/angular/src/scss/bwicons/styles/style.scss
Normal file
250
jslib/angular/src/scss/bwicons/styles/style.scss
Normal file
@@ -0,0 +1,250 @@
|
||||
$icomoon-font-family: "bwi-font" !default;
|
||||
$icomoon-font-path: "~@bitwarden/jslib-angular/src/scss/bwicons/fonts/" !default;
|
||||
|
||||
// New font sheet? Update the font-face information below
|
||||
@font-face {
|
||||
font-family: "#{$icomoon-font-family}";
|
||||
src: url($icomoon-font-path + "bwi-font.svg") format("svg"),
|
||||
url($icomoon-font-path + "bwi-font.ttf") format("truetype"),
|
||||
url($icomoon-font-path + "bwi-font.woff") format("woff"),
|
||||
url($icomoon-font-path + "bwi-font.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
// Base Class
|
||||
.bwi {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: "#{$icomoon-font-family}" !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
/* Better Font Rendering */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
// Fixed Width Icons
|
||||
.bwi-fw {
|
||||
width: calc(18em / 14);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Sizing Changes
|
||||
.bwi-sm {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.bwi-lg {
|
||||
font-size: calc(4em / 3);
|
||||
line-height: calc(3em / 4);
|
||||
vertical-align: -15%;
|
||||
}
|
||||
|
||||
.bwi-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.bwi-3x {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.bwi-4x {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
// Spin Animations
|
||||
.bwi-spin {
|
||||
animation: bwi-spin 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes bwi-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
// List Icons
|
||||
.bwi-ul {
|
||||
padding-left: 0;
|
||||
margin-left: calc(30em / 14);
|
||||
list-style-type: none;
|
||||
> li {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.bwi-li {
|
||||
position: absolute;
|
||||
left: calc(-30em / 14);
|
||||
width: calc(30em / 14);
|
||||
top: calc(2em / 14);
|
||||
text-align: center;
|
||||
&.bwi-lg {
|
||||
left: calc(-30em / 14) + calc(4em / 14);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotation
|
||||
.bwi-rotate-270 {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
// For new icons - add their glyph name and value to the map below
|
||||
$icons: (
|
||||
"save-changes": "\e988",
|
||||
"browser": "\e985",
|
||||
"mobile": "\e986",
|
||||
"cli": "\e987",
|
||||
"providers": "\e983",
|
||||
"vault": "\e984",
|
||||
"folder-closed-f": "\e982",
|
||||
"rocket": "\e9ee",
|
||||
"ellipsis-h": "\e9ef",
|
||||
"ellipsis-v": "\e9f0",
|
||||
"safari": "\e974",
|
||||
"opera": "\e975",
|
||||
"firefox": "\e976",
|
||||
"edge": "\e977",
|
||||
"chrome": "\e978",
|
||||
"star-f": "\e979",
|
||||
"arrow-circle-up": "\e97a",
|
||||
"arrow-circle-right": "\e97b",
|
||||
"arrow-circle-left": "\e97c",
|
||||
"arrow-circle-down": "\e97d",
|
||||
"undo": "\e97e",
|
||||
"bolt": "\e97f",
|
||||
"puzzle": "\e980",
|
||||
"rss": "\e973",
|
||||
"dbl-angle-left": "\e970",
|
||||
"dbl-angle-right": "\e971",
|
||||
"hamburger": "\e972",
|
||||
"bw-folder-open-f": "\e93e",
|
||||
"desktop": "\e96a",
|
||||
"angle-left": "\e96b",
|
||||
"user": "\e900",
|
||||
"user-f": "\e901",
|
||||
"key": "\e902",
|
||||
"share-square": "\e903",
|
||||
"hashtag": "\e904",
|
||||
"clone": "\e905",
|
||||
"list-alt": "\e906",
|
||||
"id-card": "\e907",
|
||||
"credit-card": "\e908",
|
||||
"globe": "\e909",
|
||||
"sticky-note": "\e90a",
|
||||
"folder": "\e90b",
|
||||
"lock": "\e90c",
|
||||
"lock-f": "\e90d",
|
||||
"generate": "\e90e",
|
||||
"generate-f": "\e90f",
|
||||
"cog": "\e910",
|
||||
"cog-f": "\e911",
|
||||
"check-circle": "\e912",
|
||||
"eye": "\e913",
|
||||
"pencil-square": "\e914",
|
||||
"bookmark": "\e915",
|
||||
"files": "\e916",
|
||||
"trash": "\e917",
|
||||
"plus": "\e918",
|
||||
"star": "\e919",
|
||||
"list": "\e91a",
|
||||
"angle-right": "\e91b",
|
||||
"external-link": "\e91c",
|
||||
"refresh": "\e91d",
|
||||
"search": "\e91f",
|
||||
"filter": "\e920",
|
||||
"plus-circle": "\e921",
|
||||
"user-circle": "\e922",
|
||||
"question-circle": "\e923",
|
||||
"cogs": "\e924",
|
||||
"minus-circle": "\e925",
|
||||
"send": "\e926",
|
||||
"send-f": "\e927",
|
||||
"download": "\e928",
|
||||
"pencil": "\e929",
|
||||
"sign-out": "\e92a",
|
||||
"share": "\e92b",
|
||||
"clock": "\e92c",
|
||||
"angle-down": "\e92d",
|
||||
"caret-down": "\e92e",
|
||||
"square": "\e92f",
|
||||
"collection": "\e930",
|
||||
"bank": "\e931",
|
||||
"shield": "\e932",
|
||||
"stop": "\e933",
|
||||
"plus-square": "\e934",
|
||||
"save": "\e935",
|
||||
"sign-in": "\e936",
|
||||
"spinner": "\e937",
|
||||
"dollar": "\e939",
|
||||
"check": "\e93a",
|
||||
"check-square": "\e93b",
|
||||
"minus-square": "\e93c",
|
||||
"close": "\e93d",
|
||||
"share-arrow": "\e96c",
|
||||
"paperclip": "\e93f",
|
||||
"bitcoin": "\e940",
|
||||
"cut": "\e941",
|
||||
"frown": "\e942",
|
||||
"folder-open": "\e943",
|
||||
"bug": "\e946",
|
||||
"chain-broken": "\e947",
|
||||
"dashboard": "\e948",
|
||||
"envelope": "\e949",
|
||||
"exclamation-circle": "\e94a",
|
||||
"exclamation-triangle": "\e94b",
|
||||
"caret-right": "\e94c",
|
||||
"file-pdf": "\e94e",
|
||||
"file-text": "\e94f",
|
||||
"info-circle": "\e952",
|
||||
"lightbulb": "\e953",
|
||||
"link": "\e954",
|
||||
"linux": "\e956",
|
||||
"long-arrow-right": "\e957",
|
||||
"money": "\e958",
|
||||
"play": "\e959",
|
||||
"reddit": "\e95a",
|
||||
"refresh-tab": "\e95b",
|
||||
"sitemap": "\e95c",
|
||||
"sliders": "\e95d",
|
||||
"tag": "\e95e",
|
||||
"thumb-tack": "\e95f",
|
||||
"thumbs-up": "\e960",
|
||||
"unlock": "\e962",
|
||||
"users": "\e963",
|
||||
"wrench": "\e965",
|
||||
"ban": "\e967",
|
||||
"camera": "\e968",
|
||||
"chevron-up": "\e969",
|
||||
"eye-slash": "\e96d",
|
||||
"file": "\e96e",
|
||||
"paste": "\e96f",
|
||||
"github": "\e950",
|
||||
"facebook": "\e94d",
|
||||
"paypal": "\e938",
|
||||
"google": "\e951",
|
||||
"linkedin": "\e955",
|
||||
"discourse": "\e91e",
|
||||
"twitter": "\e961",
|
||||
"youtube": "\e966",
|
||||
"windows": "\e964",
|
||||
"apple": "\e945",
|
||||
"android": "\e944",
|
||||
"error": "\e981",
|
||||
"numbered-list": "\e989",
|
||||
);
|
||||
|
||||
@each $name, $glyph in $icons {
|
||||
.bwi-#{$name}:before {
|
||||
content: $glyph;
|
||||
}
|
||||
}
|
||||
44
jslib/angular/src/scss/icons.scss
Normal file
44
jslib/angular/src/scss/icons.scss
Normal file
@@ -0,0 +1,44 @@
|
||||
$card-icons-base: "~@bitwarden/jslib-angular/src/images/cards/";
|
||||
$card-icons: (
|
||||
"visa": $card-icons-base + "visa-light.png",
|
||||
"amex": $card-icons-base + "amex-light.png",
|
||||
"diners-club": $card-icons-base + "diners_club-light.png",
|
||||
"discover": $card-icons-base + "discover-light.png",
|
||||
"jcb": $card-icons-base + "jcb-light.png",
|
||||
"maestro": $card-icons-base + "maestro-light.png",
|
||||
"mastercard": $card-icons-base + "mastercard-light.png",
|
||||
"union-pay": $card-icons-base + "union_pay-light.png",
|
||||
);
|
||||
|
||||
$card-icons-dark: (
|
||||
"visa": $card-icons-base + "visa-dark.png",
|
||||
"amex": $card-icons-base + "amex-dark.png",
|
||||
"diners-club": $card-icons-base + "diners_club-dark.png",
|
||||
"discover": $card-icons-base + "discover-dark.png",
|
||||
"jcb": $card-icons-base + "jcb-dark.png",
|
||||
"maestro": $card-icons-base + "maestro-dark.png",
|
||||
"mastercard": $card-icons-base + "mastercard-dark.png",
|
||||
"union-pay": $card-icons-base + "union_pay-dark.png",
|
||||
);
|
||||
|
||||
.credit-card-icon {
|
||||
display: block; // Resolves the parent container being slighly to big
|
||||
height: 19px;
|
||||
width: 24px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@each $name, $url in $card-icons {
|
||||
.card-#{$name} {
|
||||
background-image: url("#{$url}");
|
||||
}
|
||||
}
|
||||
|
||||
@each $theme in $dark-icon-themes {
|
||||
@each $name, $url in $card-icons-dark {
|
||||
.#{$theme} .card-#{$name} {
|
||||
background-image: url("#{$url}");
|
||||
}
|
||||
}
|
||||
}
|
||||
89
jslib/angular/src/scss/webfonts.css
Normal file
89
jslib/angular/src/scss/webfonts.css
Normal file
@@ -0,0 +1,89 @@
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-italic-300.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-italic-400.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-italic-600.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-italic-700.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-italic-800.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-normal-300.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-normal-400.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-normal-600.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-normal-700.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: auto;
|
||||
src: url(webfonts/Open_Sans-normal-800.woff) format("woff");
|
||||
unicode-range: U+0-10FFFF;
|
||||
}
|
||||
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-300.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-400.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-600.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-700.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-italic-800.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-300.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-400.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-600.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-700.woff
Normal file
Binary file not shown.
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff
Normal file
BIN
jslib/angular/src/scss/webfonts/Open_Sans-normal-800.woff
Normal file
Binary file not shown.
45
jslib/angular/src/services/auth-guard.service.ts
Normal file
45
jslib/angular/src/services/auth-guard.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
|
||||
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuardService implements CanActivate {
|
||||
constructor(
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private router: Router,
|
||||
private messagingService: MessagingService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private stateService: StateService
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||
if (!isAuthed) {
|
||||
this.messagingService.send("authBlocked");
|
||||
return false;
|
||||
}
|
||||
|
||||
const locked = await this.vaultTimeoutService.isLocked();
|
||||
if (locked) {
|
||||
if (routerState != null) {
|
||||
this.messagingService.send("lockedUrl", { url: routerState.url });
|
||||
}
|
||||
this.router.navigate(["lock"], { queryParams: { promptBiometric: true } });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!routerState.url.includes("remove-password") &&
|
||||
(await this.keyConnectorService.getConvertAccountRequired())
|
||||
) {
|
||||
this.router.navigate(["/remove-password"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
6
jslib/angular/src/services/broadcaster.service.ts
Normal file
6
jslib/angular/src/services/broadcaster.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { BroadcasterService as BaseBroadcasterService } from "jslib-common/services/broadcaster.service";
|
||||
|
||||
@Injectable()
|
||||
export class BroadcasterService extends BaseBroadcasterService {}
|
||||
492
jslib/angular/src/services/jslib-services.module.ts
Normal file
492
jslib/angular/src/services/jslib-services.module.ts
Normal file
@@ -0,0 +1,492 @@
|
||||
import { Injector, LOCALE_ID, NgModule } from "@angular/core";
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "jslib-common/abstractions/audit.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CipherService as CipherServiceAbstraction } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService as CollectionServiceAbstraction } from "jslib-common/abstractions/collection.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||
import { ExportService as ExportServiceAbstraction } from "jslib-common/abstractions/export.service";
|
||||
import { FileUploadService as FileUploadServiceAbstraction } from "jslib-common/abstractions/fileUpload.service";
|
||||
import { FolderService as FolderServiceAbstraction } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from "jslib-common/abstractions/organization.service";
|
||||
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService as PolicyServiceAbstraction } from "jslib-common/abstractions/policy.service";
|
||||
import { ProviderService as ProviderServiceAbstraction } from "jslib-common/abstractions/provider.service";
|
||||
import { SearchService as SearchServiceAbstraction } from "jslib-common/abstractions/search.service";
|
||||
import { SendService as SendServiceAbstraction } from "jslib-common/abstractions/send.service";
|
||||
import { SettingsService as SettingsServiceAbstraction } from "jslib-common/abstractions/settings.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||
import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.service";
|
||||
import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "jslib-common/abstractions/usernameGeneration.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
import { StateFactory } from "jslib-common/factories/stateFactory";
|
||||
import { Account } from "jslib-common/models/domain/account";
|
||||
import { GlobalState } from "jslib-common/models/domain/globalState";
|
||||
import { ApiService } from "jslib-common/services/api.service";
|
||||
import { AppIdService } from "jslib-common/services/appId.service";
|
||||
import { AuditService } from "jslib-common/services/audit.service";
|
||||
import { AuthService } from "jslib-common/services/auth.service";
|
||||
import { CipherService } from "jslib-common/services/cipher.service";
|
||||
import { CollectionService } from "jslib-common/services/collection.service";
|
||||
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
||||
import { CryptoService } from "jslib-common/services/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/services/environment.service";
|
||||
import { EventService } from "jslib-common/services/event.service";
|
||||
import { ExportService } from "jslib-common/services/export.service";
|
||||
import { FileUploadService } from "jslib-common/services/fileUpload.service";
|
||||
import { FolderService } from "jslib-common/services/folder.service";
|
||||
import { KeyConnectorService } from "jslib-common/services/keyConnector.service";
|
||||
import { NotificationsService } from "jslib-common/services/notifications.service";
|
||||
import { OrganizationService } from "jslib-common/services/organization.service";
|
||||
import { PasswordGenerationService } from "jslib-common/services/passwordGeneration.service";
|
||||
import { PolicyService } from "jslib-common/services/policy.service";
|
||||
import { ProviderService } from "jslib-common/services/provider.service";
|
||||
import { SearchService } from "jslib-common/services/search.service";
|
||||
import { SendService } from "jslib-common/services/send.service";
|
||||
import { SettingsService } from "jslib-common/services/settings.service";
|
||||
import { StateService } from "jslib-common/services/state.service";
|
||||
import { StateMigrationService } from "jslib-common/services/stateMigration.service";
|
||||
import { SyncService } from "jslib-common/services/sync.service";
|
||||
import { TokenService } from "jslib-common/services/token.service";
|
||||
import { TotpService } from "jslib-common/services/totp.service";
|
||||
import { TwoFactorService } from "jslib-common/services/twoFactor.service";
|
||||
import { UserVerificationService } from "jslib-common/services/userVerification.service";
|
||||
import { UsernameGenerationService } from "jslib-common/services/usernameGeneration.service";
|
||||
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||
import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service";
|
||||
|
||||
import { AuthGuardService } from "./auth-guard.service";
|
||||
import { BroadcasterService } from "./broadcaster.service";
|
||||
import { LockGuardService } from "./lock-guard.service";
|
||||
import { ModalService } from "./modal.service";
|
||||
import { PasswordRepromptService } from "./passwordReprompt.service";
|
||||
import { UnauthGuardService } from "./unauth-guard.service";
|
||||
import { ValidationService } from "./validation.service";
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
providers: [
|
||||
{ provide: "WINDOW", useValue: window },
|
||||
{
|
||||
provide: LOCALE_ID,
|
||||
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
|
||||
deps: [I18nServiceAbstraction],
|
||||
},
|
||||
ValidationService,
|
||||
AuthGuardService,
|
||||
UnauthGuardService,
|
||||
LockGuardService,
|
||||
ModalService,
|
||||
{
|
||||
provide: AppIdServiceAbstraction,
|
||||
useClass: AppIdService,
|
||||
deps: [StorageServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AuditServiceAbstraction,
|
||||
useClass: AuditService,
|
||||
deps: [CryptoFunctionServiceAbstraction, ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AuthServiceAbstraction,
|
||||
useClass: AuthService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
LogService,
|
||||
KeyConnectorServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
TwoFactorServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: CipherServiceAbstraction,
|
||||
useFactory: (
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction,
|
||||
apiService: ApiServiceAbstraction,
|
||||
fileUploadService: FileUploadServiceAbstraction,
|
||||
i18nService: I18nServiceAbstraction,
|
||||
injector: Injector,
|
||||
logService: LogService,
|
||||
stateService: StateServiceAbstraction
|
||||
) =>
|
||||
new CipherService(
|
||||
cryptoService,
|
||||
settingsService,
|
||||
apiService,
|
||||
fileUploadService,
|
||||
i18nService,
|
||||
() => injector.get(SearchServiceAbstraction),
|
||||
logService,
|
||||
stateService
|
||||
),
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
SettingsServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
FileUploadServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
Injector, // TODO: Get rid of this circular dependency!
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: FolderServiceAbstraction,
|
||||
useClass: FolderService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: LogService, useFactory: () => new ConsoleLogService(false) },
|
||||
{
|
||||
provide: CollectionServiceAbstraction,
|
||||
useClass: CollectionService,
|
||||
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: EnvironmentServiceAbstraction,
|
||||
useClass: EnvironmentService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: TotpServiceAbstraction,
|
||||
useClass: TotpService,
|
||||
deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction],
|
||||
},
|
||||
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: CryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: PasswordGenerationServiceAbstraction,
|
||||
useClass: PasswordGenerationService,
|
||||
deps: [CryptoServiceAbstraction, PolicyServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: UsernameGenerationServiceAbstraction,
|
||||
useClass: UsernameGenerationService,
|
||||
deps: [CryptoServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: ApiServiceAbstraction,
|
||||
useFactory: (
|
||||
tokenService: TokenServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
appIdService: AppIdServiceAbstraction
|
||||
) =>
|
||||
new ApiService(
|
||||
tokenService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
appIdService,
|
||||
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
||||
),
|
||||
deps: [
|
||||
TokenServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: FileUploadServiceAbstraction,
|
||||
useClass: FileUploadService,
|
||||
deps: [LogService, ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: SyncServiceAbstraction,
|
||||
useFactory: (
|
||||
apiService: ApiServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction,
|
||||
folderService: FolderServiceAbstraction,
|
||||
cipherService: CipherServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
policyService: PolicyServiceAbstraction,
|
||||
sendService: SendServiceAbstraction,
|
||||
logService: LogService,
|
||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||
stateService: StateServiceAbstraction,
|
||||
organizationService: OrganizationServiceAbstraction,
|
||||
providerService: ProviderServiceAbstraction
|
||||
) =>
|
||||
new SyncService(
|
||||
apiService,
|
||||
settingsService,
|
||||
folderService,
|
||||
cipherService,
|
||||
cryptoService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
policyService,
|
||||
sendService,
|
||||
logService,
|
||||
keyConnectorService,
|
||||
stateService,
|
||||
organizationService,
|
||||
providerService,
|
||||
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
||||
),
|
||||
deps: [
|
||||
ApiServiceAbstraction,
|
||||
SettingsServiceAbstraction,
|
||||
FolderServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
PolicyServiceAbstraction,
|
||||
SendServiceAbstraction,
|
||||
LogService,
|
||||
KeyConnectorServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
OrganizationServiceAbstraction,
|
||||
ProviderServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
|
||||
{
|
||||
provide: SettingsServiceAbstraction,
|
||||
useClass: SettingsService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: VaultTimeoutServiceAbstraction,
|
||||
useFactory: (
|
||||
cipherService: CipherServiceAbstraction,
|
||||
folderService: FolderServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
searchService: SearchServiceAbstraction,
|
||||
tokenService: TokenServiceAbstraction,
|
||||
policyService: PolicyServiceAbstraction,
|
||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||
stateService: StateServiceAbstraction
|
||||
) =>
|
||||
new VaultTimeoutService(
|
||||
cipherService,
|
||||
folderService,
|
||||
collectionService,
|
||||
cryptoService,
|
||||
platformUtilsService,
|
||||
messagingService,
|
||||
searchService,
|
||||
tokenService,
|
||||
policyService,
|
||||
keyConnectorService,
|
||||
stateService,
|
||||
null,
|
||||
async (userId?: string) =>
|
||||
messagingService.send("logout", { expired: false, userId: userId })
|
||||
),
|
||||
deps: [
|
||||
CipherServiceAbstraction,
|
||||
FolderServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
SearchServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
PolicyServiceAbstraction,
|
||||
KeyConnectorServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction,
|
||||
logService: LogService,
|
||||
stateMigrationService: StateMigrationServiceAbstraction
|
||||
) =>
|
||||
new StateService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
logService,
|
||||
stateMigrationService,
|
||||
new StateFactory(GlobalState, Account)
|
||||
),
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
"SECURE_STORAGE",
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction
|
||||
) =>
|
||||
new StateMigrationService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
new StateFactory(GlobalState, Account)
|
||||
),
|
||||
deps: [StorageServiceAbstraction, "SECURE_STORAGE"],
|
||||
},
|
||||
{
|
||||
provide: ExportServiceAbstraction,
|
||||
useClass: ExportService,
|
||||
deps: [
|
||||
FolderServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SearchServiceAbstraction,
|
||||
useClass: SearchService,
|
||||
deps: [CipherServiceAbstraction, LogService, I18nServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: NotificationsServiceAbstraction,
|
||||
useFactory: (
|
||||
syncService: SyncServiceAbstraction,
|
||||
appIdService: AppIdServiceAbstraction,
|
||||
apiService: ApiServiceAbstraction,
|
||||
vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
logService: LogService,
|
||||
stateService: StateServiceAbstraction
|
||||
) =>
|
||||
new NotificationsService(
|
||||
syncService,
|
||||
appIdService,
|
||||
apiService,
|
||||
vaultTimeoutService,
|
||||
environmentService,
|
||||
async () => messagingService.send("logout", { expired: true }),
|
||||
logService,
|
||||
stateService
|
||||
),
|
||||
deps: [
|
||||
SyncServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
VaultTimeoutServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: CryptoFunctionServiceAbstraction,
|
||||
useClass: WebCryptoFunctionService,
|
||||
deps: ["WINDOW"],
|
||||
},
|
||||
{
|
||||
provide: EventServiceAbstraction,
|
||||
useClass: EventService,
|
||||
deps: [
|
||||
ApiServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
LogService,
|
||||
OrganizationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: PolicyServiceAbstraction,
|
||||
useClass: PolicyService,
|
||||
deps: [StateServiceAbstraction, OrganizationServiceAbstraction, ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: SendServiceAbstraction,
|
||||
useClass: SendService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
FileUploadServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: KeyConnectorServiceAbstraction,
|
||||
useClass: KeyConnectorService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
LogService,
|
||||
OrganizationServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: UserVerificationServiceAbstraction,
|
||||
useClass: UserVerificationService,
|
||||
deps: [CryptoServiceAbstraction, I18nServiceAbstraction, ApiServiceAbstraction],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
provide: OrganizationServiceAbstraction,
|
||||
useClass: OrganizationService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: ProviderServiceAbstraction,
|
||||
useClass: ProviderService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: TwoFactorServiceAbstraction,
|
||||
useClass: TwoFactorService,
|
||||
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
29
jslib/angular/src/services/lock-guard.service.ts
Normal file
29
jslib/angular/src/services/lock-guard.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { CanActivate, Router } from "@angular/router";
|
||||
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
@Injectable()
|
||||
export class LockGuardService implements CanActivate {
|
||||
protected homepage = "vault";
|
||||
protected loginpage = "login";
|
||||
constructor(
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private router: Router,
|
||||
private stateService: StateService
|
||||
) {}
|
||||
|
||||
async canActivate() {
|
||||
if (await this.vaultTimeoutService.isLocked()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const redirectUrl = (await this.stateService.getIsAuthenticated())
|
||||
? [this.homepage]
|
||||
: [this.loginpage];
|
||||
|
||||
this.router.navigate(redirectUrl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
180
jslib/angular/src/services/modal.service.ts
Normal file
180
jslib/angular/src/services/modal.service.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
ApplicationRef,
|
||||
ComponentFactory,
|
||||
ComponentFactoryResolver,
|
||||
ComponentRef,
|
||||
EmbeddedViewRef,
|
||||
Injectable,
|
||||
Injector,
|
||||
Type,
|
||||
ViewContainerRef,
|
||||
} from "@angular/core";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { DynamicModalComponent } from "../components/modal/dynamic-modal.component";
|
||||
import { ModalInjector } from "../components/modal/modal-injector";
|
||||
import { ModalRef } from "../components/modal/modal.ref";
|
||||
|
||||
export class ModalConfig<D = any> {
|
||||
data?: D;
|
||||
allowMultipleModals = false;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ModalService {
|
||||
protected modalList: ComponentRef<DynamicModalComponent>[] = [];
|
||||
|
||||
// Lazy loaded modules are not available in componentFactoryResolver,
|
||||
// therefore modules needs to manually initialize their resolvers.
|
||||
private factoryResolvers: Map<Type<any>, ComponentFactoryResolver> = new Map();
|
||||
|
||||
constructor(
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private applicationRef: ApplicationRef,
|
||||
private injector: Injector
|
||||
) {
|
||||
document.addEventListener("keyup", (event) => {
|
||||
if (event.key === "Escape" && this.modalCount > 0) {
|
||||
this.topModal.instance.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get modalCount() {
|
||||
return this.modalList.length;
|
||||
}
|
||||
|
||||
private get topModal() {
|
||||
return this.modalList[this.modalCount - 1];
|
||||
}
|
||||
|
||||
async openViewRef<T>(
|
||||
componentType: Type<T>,
|
||||
viewContainerRef: ViewContainerRef,
|
||||
setComponentParameters: (component: T) => void = null
|
||||
): Promise<[ModalRef, T]> {
|
||||
const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false);
|
||||
modalComponentRef.instance.setComponentParameters = setComponentParameters;
|
||||
|
||||
viewContainerRef.insert(modalComponentRef.hostView);
|
||||
|
||||
await modalRef.onCreated.pipe(first()).toPromise();
|
||||
|
||||
return [modalRef, modalComponentRef.instance.componentRef.instance];
|
||||
}
|
||||
|
||||
open(componentType: Type<any>, config?: ModalConfig) {
|
||||
if (!(config?.allowMultipleModals ?? false) && this.modalCount > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const [modalRef, _] = this.openInternal(componentType, config, true);
|
||||
|
||||
return modalRef;
|
||||
}
|
||||
|
||||
registerComponentFactoryResolver<T>(
|
||||
componentType: Type<T>,
|
||||
componentFactoryResolver: ComponentFactoryResolver
|
||||
): void {
|
||||
this.factoryResolvers.set(componentType, componentFactoryResolver);
|
||||
}
|
||||
|
||||
resolveComponentFactory<T>(componentType: Type<T>): ComponentFactory<T> {
|
||||
if (this.factoryResolvers.has(componentType)) {
|
||||
return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType);
|
||||
}
|
||||
|
||||
return this.componentFactoryResolver.resolveComponentFactory(componentType);
|
||||
}
|
||||
|
||||
protected openInternal(
|
||||
componentType: Type<any>,
|
||||
config?: ModalConfig,
|
||||
attachToDom?: boolean
|
||||
): [ModalRef, ComponentRef<DynamicModalComponent>] {
|
||||
const [modalRef, componentRef] = this.createModalComponent(config);
|
||||
componentRef.instance.childComponentType = componentType;
|
||||
|
||||
if (attachToDom) {
|
||||
this.applicationRef.attachView(componentRef.hostView);
|
||||
const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
|
||||
document.body.appendChild(domElem);
|
||||
}
|
||||
|
||||
modalRef.onClosed.pipe(first()).subscribe(() => {
|
||||
if (attachToDom) {
|
||||
this.applicationRef.detachView(componentRef.hostView);
|
||||
}
|
||||
componentRef.destroy();
|
||||
|
||||
this.modalList.pop();
|
||||
if (this.modalCount > 0) {
|
||||
this.topModal.instance.getFocus();
|
||||
}
|
||||
});
|
||||
|
||||
this.setupHandlers(modalRef);
|
||||
|
||||
this.modalList.push(componentRef);
|
||||
|
||||
return [modalRef, componentRef];
|
||||
}
|
||||
|
||||
protected setupHandlers(modalRef: ModalRef) {
|
||||
let backdrop: HTMLElement = null;
|
||||
|
||||
// Add backdrop, setup [data-dismiss] handler.
|
||||
modalRef.onCreated.pipe(first()).subscribe((el) => {
|
||||
document.body.classList.add("modal-open");
|
||||
|
||||
const modalEl: HTMLElement = el.querySelector(".modal");
|
||||
const dialogEl = modalEl.querySelector(".modal-dialog") as HTMLElement;
|
||||
|
||||
backdrop = document.createElement("div");
|
||||
backdrop.className = "modal-backdrop fade";
|
||||
backdrop.style.zIndex = `${this.modalCount}040`;
|
||||
modalEl.prepend(backdrop);
|
||||
|
||||
dialogEl.addEventListener("click", (e: Event) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
dialogEl.style.zIndex = `${this.modalCount}050`;
|
||||
|
||||
const modals = Array.from(
|
||||
el.querySelectorAll('.modal-backdrop, .modal *[data-dismiss="modal"]')
|
||||
);
|
||||
for (const closeElement of modals) {
|
||||
closeElement.addEventListener("click", () => {
|
||||
modalRef.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// onClose is used in Web to hook into bootstrap. On other projects we pipe it directly to closed.
|
||||
modalRef.onClose.pipe(first()).subscribe(() => {
|
||||
modalRef.closed();
|
||||
|
||||
if (this.modalCount === 0) {
|
||||
document.body.classList.remove("modal-open");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected createModalComponent(
|
||||
config: ModalConfig
|
||||
): [ModalRef, ComponentRef<DynamicModalComponent>] {
|
||||
const modalRef = new ModalRef();
|
||||
|
||||
const map = new WeakMap();
|
||||
map.set(ModalConfig, config);
|
||||
map.set(ModalRef, modalRef);
|
||||
|
||||
const componentFactory =
|
||||
this.componentFactoryResolver.resolveComponentFactory(DynamicModalComponent);
|
||||
const componentRef = componentFactory.create(new ModalInjector(this.injector, map));
|
||||
|
||||
return [modalRef, componentRef];
|
||||
}
|
||||
}
|
||||
45
jslib/angular/src/services/passwordReprompt.service.ts
Normal file
45
jslib/angular/src/services/passwordReprompt.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
|
||||
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||
|
||||
import { ModalService } from "./modal.service";
|
||||
|
||||
/**
|
||||
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||
* See UserVerificationService for any other situation where you need to verify the user's identity.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
||||
protected component = PasswordRepromptComponent;
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private keyConnectorService: KeyConnectorService
|
||||
) {}
|
||||
|
||||
protectedFields() {
|
||||
return ["TOTP", "Password", "H_Field", "Card Number", "Security Code"];
|
||||
}
|
||||
|
||||
async showPasswordPrompt() {
|
||||
if (!(await this.enabled())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ref = this.modalService.open(this.component, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = await ref.onClosedPromise();
|
||||
return result === true;
|
||||
}
|
||||
|
||||
async enabled() {
|
||||
return !(await this.keyConnectorService.getUsesKeyConnector());
|
||||
}
|
||||
}
|
||||
29
jslib/angular/src/services/unauth-guard.service.ts
Normal file
29
jslib/angular/src/services/unauth-guard.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { CanActivate, Router } from "@angular/router";
|
||||
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
@Injectable()
|
||||
export class UnauthGuardService implements CanActivate {
|
||||
protected homepage = "vault";
|
||||
constructor(
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private router: Router,
|
||||
private stateService: StateService
|
||||
) {}
|
||||
|
||||
async canActivate() {
|
||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||
if (isAuthed) {
|
||||
const locked = await this.vaultTimeoutService.isLocked();
|
||||
if (locked) {
|
||||
this.router.navigate(["lock"]);
|
||||
} else {
|
||||
this.router.navigate([this.homepage]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
38
jslib/angular/src/services/validation.service.ts
Normal file
38
jslib/angular/src/services/validation.service.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { ErrorResponse } from "jslib-common/models/response/errorResponse";
|
||||
|
||||
@Injectable()
|
||||
export class ValidationService {
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
showError(data: any): string[] {
|
||||
const defaultErrorMessage = this.i18nService.t("unexpectedError");
|
||||
let errors: string[] = [];
|
||||
|
||||
if (data != null && typeof data === "string") {
|
||||
errors.push(data);
|
||||
} else if (data == null || typeof data !== "object") {
|
||||
errors.push(defaultErrorMessage);
|
||||
} else if (data.validationErrors != null) {
|
||||
errors = errors.concat((data as ErrorResponse).getAllMessages());
|
||||
} else {
|
||||
errors.push(data.message ? data.message : defaultErrorMessage);
|
||||
}
|
||||
|
||||
if (errors.length === 1) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors[0]);
|
||||
} else if (errors.length > 1) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), errors, {
|
||||
timeout: 5000 * errors.length,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user