setup services, accounts components

This commit is contained in:
Kyle Spearrin 2018-06-05 15:02:53 -04:00
parent 28197970bd
commit a4ff241574
25 changed files with 898 additions and 3 deletions

2
jslib

@ -1 +1 @@
Subproject commit 66b3dbae177a795d6b5168f7cdbba2f98f309272
Subproject commit 476d21e9f07f648784e92e6e7ec4ae37910e2449

5
locales/en/messages.json Normal file
View File

@ -0,0 +1,5 @@
{
"hello": {
"message": "hello world"
}
}

5
locales/es/messages.json Normal file
View File

@ -0,0 +1,5 @@
{
"hello": {
"message": "hola mundo"
}
}

View File

@ -0,0 +1,30 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a routerLink="/login">{{'cancel' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'passwordHint' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
</button>
</div>
</header>
<content>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{'emailAddress' | i18n}}</label>
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus
appInputVerbatim="false">
</div>
</div>
<div class="box-footer">
{{'enterEmailToGetHint' | i18n}}
</div>
</div>
</content>
</form>

View File

@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { HintComponent as BaseHintComponent } from 'jslib/angular/components/hint.component';
@Component({
selector: 'app-hint',
templateUrl: 'hint.component.html',
})
export class HintComponent extends BaseHintComponent {
constructor(router: Router, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
apiService: ApiService) {
super(router, analytics, toasterService, i18nService, apiService);
}
}

View File

@ -0,0 +1,40 @@
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<div class="content">
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
<div class="box last">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{'emailAddress' | i18n}}</label>
<input id="email" type="text" name="Email" [(ngModel)]="email" required
[appAutofocus]="email === ''">
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''">
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
<i class="fa fa-lg"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
</div>
</div>
<div class="buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
<b [hidden]="form.loading"><i class="fa fa-sign-in"></i> {{'logIn' | i18n}}</b>
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading"></i>
</button>
<a routerLink="/register" class="btn block">
<i class="fa fa-pencil-square-o"></i> {{'createAccount' | i18n}}
</a>
</div>
<div class="sub-options">
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
</div>
</div>
</form>

View File

@ -0,0 +1,24 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component';
@Component({
selector: 'app-login',
templateUrl: 'login.component.html',
})
export class LoginComponent extends BaseLoginComponent {
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, private syncService: SyncService) {
super(authService, router, analytics, toasterService, i18nService);
this.successRoute = '/vault';
}
}

View File

@ -0,0 +1,71 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a routerLink="/home">{{'cancel' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'createAccount' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
</button>
</div>
</header>
<content>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{'emailAddress' | i18n}}</label>
<input id="email" type="text" name="Email" [(ngModel)]="email" required
[appAutofocus]="email === ''" inputmode="email" appInputVerbatim="false">
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
class="monospaced" [(ngModel)]="masterPassword" required [appAutofocus]="email !== ''"
appInputVerbatim>
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
<i class="fa fa-lg"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
</div>
<div class="box-footer">
{{'masterPassDesc' | i18n}}
</div>
</div>
<div class="box last">
<div class="box-content">
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword"
required appInputVerbatim>
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
<i class="fa fa-lg"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
<div class="box-content-row" appBoxRow>
<label for="hint">{{'masterPassHint' | i18n}}</label>
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
</div>
</div>
<div class="box-footer">
{{'masterPassHintDesc' | i18n}}
</div>
</div>
</content>
</form>

View File

@ -0,0 +1,25 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { RegisterComponent as BaseRegisterComponent } from 'jslib/angular/components/register.component';
@Component({
selector: 'app-register',
templateUrl: 'register.component.html',
})
export class RegisterComponent extends BaseRegisterComponent {
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, cryptoService: CryptoService,
apiService: ApiService) {
super(authService, router, analytics, toasterService, i18nService, cryptoService, apiService);
}
}

View File

@ -0,0 +1,24 @@
<header>
<div class="left">
<a routerLink="/2fa">{{'close' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'twoStepOptions' | i18n}}</span>
</div>
<div class="right"></div>
</header>
<content>
<div class="box">
<div class="box-content">
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
(click)="choose(p)">
<span class="text">{{p.name}}</span>
<span class="detail">{{p.description}}</span>
</a>
<a href="#" appStopClick class="box-content-row" (click)="recover()">
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
</a>
</div>
</div>
</content>

View File

@ -0,0 +1,31 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import {
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
} from 'jslib/angular/components/two-factor-options.component';
@Component({
selector: 'app-two-factor-options',
templateUrl: 'two-factor-options.component.html',
})
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window);
}
choose(p: any) {
super.choose(p);
this.authService.selectedTwoFactorProviderType = p.type;
this.router.navigate(['2fa']);
}
}

View File

@ -0,0 +1,108 @@
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a routerLink="/login">{{'back' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{title}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading"
*ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
selectedProviderType !== providerType.OrganizationDuo">
<span [hidden]="form.loading">{{'continue' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading"></i>
</button>
</div>
</header>
<content>
<ng-container *ngIf="selectedProviderType === providerType.Authenticator ||
selectedProviderType === providerType.Email">
<div class="content text-center">
<span *ngIf="selectedProviderType === providerType.Authenticator">
{{'enterVerificationCodeApp' | i18n}}
</span>
<span *ngIf="selectedProviderType === providerType.Email">
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
</span>
</div>
<div class="box first">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="code">{{'verificationCode' | i18n}}</label>
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus
inputmode="tel" appInputVerbatim>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="remember">{{'rememberMe' | i18n}}</label>
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
<div class="content text-center">
<p>{{'insertYubiKey' | i18n}}</p>
<img src="../../images/two-factor/yubikey.jpg" class="img-rounded img-responsive" alt="">
</div>
<div class="box first">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus
appInputVerbatim>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="remember">{{'rememberMe' | i18n}}</label>
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.U2f">
<div class="content text-center">
<span *ngIf="!u2fReady"><i class="fa fa-spinner fa-spin"></i></span>
<div *ngIf="u2fReady">
<p>{{'insertU2f' | i18n}}</p>
<img src="../../images/two-factor/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
</div>
</div>
<div class="box first">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="remember">{{'rememberMe' | i18n}}</label>
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
selectedProviderType === providerType.OrganizationDuo">
<div id="duo-frame" *ngIf="!showNewWindowMessage"><iframe id="duo_iframe"></iframe></div>
<div *ngIf="showNewWindowMessage" class="content text-center">{{'twoStepNewWindowMessage' | i18n}}</div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="remember">{{'rememberMe' | i18n}}</label>
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
</div>
</div>
</div>
</ng-container>
<div class="content text-center" *ngIf="selectedProviderType == null">
<p>{{'noTwoStepProviders' | i18n}}</p>
<p>{{'noTwoStepProviders2' | i18n}}</p>
</div>
<div class="content no-vpad text-center" *ngIf="selectedProviderType != null">
<p>
<a href="#" appStopClick (click)="anotherMethod()">{{'useAnotherTwoStepMethod' | i18n}}</a>
</p>
<p *ngIf="selectedProviderType === providerType.Email">
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise">
{{'sendVerificationCodeEmailAgain' | i18n}}
</a>
</p>
</div>
</content>
</form>
<iframe id="u2f_iframe" hidden></iframe>

View File

@ -0,0 +1,50 @@
import {
ChangeDetectorRef,
Component,
NgZone,
OnDestroy,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
const BroadcasterSubscriptionId = 'TwoFactorComponent';
@Component({
selector: 'app-two-factor',
templateUrl: 'two-factor.component.html',
})
export class TwoFactorComponent extends BaseTwoFactorComponent {
showNewWindowMessage = false;
constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, apiService: ApiService,
platformUtilsService: PlatformUtilsService, syncService: SyncService,
environmentService: EnvironmentService, private ngZone: NgZone,
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef) {
super(authService, router, analytics, toasterService, i18nService, apiService,
platformUtilsService, window, environmentService);
this.successRoute = '/vault';
}
anotherMethod() {
this.router.navigate(['2fa-options']);
}
}

View File

@ -4,10 +4,19 @@ import {
Routes,
} from '@angular/router';
import { HintComponent } from './accounts/hint.component';
import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { VaultComponent } from './vault/vault.component';
const routes: Routes = [
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: '2fa', component: TwoFactorComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'hint', component: HintComponent },
{
path: 'vault',
component: VaultComponent,

View File

@ -1,6 +1,10 @@
import 'core-js';
import 'zone.js/dist/zone';
import { ToasterModule } from 'angular2-toaster';
import { Angulartics2Module } from 'angulartics2';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { AppRoutingModule } from './app-routing.module';
import { NgModule } from '@angular/core';
@ -8,19 +12,59 @@ import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServicesModule } from './services.module';
import { AppComponent } from './app.component';
import { HintComponent } from './accounts/hint.component';
import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { VaultComponent } from './vault/vault.component';
import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive';
import { AutofocusDirective } from 'jslib/angular/directives/autofocus.directive';
import { BlurClickDirective } from 'jslib/angular/directives/blur-click.directive';
import { BoxRowDirective } from 'jslib/angular/directives/box-row.directive';
import { FallbackSrcDirective } from 'jslib/angular/directives/fallback-src.directive';
import { InputVerbatimDirective } from 'jslib/angular/directives/input-verbatim.directive';
import { StopClickDirective } from 'jslib/angular/directives/stop-click.directive';
import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive';
import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe';
import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
AppRoutingModule,
ServicesModule,
Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], {
pageTracking: {
clearQueryParams: true,
},
}),
ToasterModule,
],
declarations: [
ApiActionDirective,
AppComponent,
AutofocusDirective,
BlurClickDirective,
BoxRowDirective,
FallbackSrcDirective,
HintComponent,
I18nPipe,
InputVerbatimDirective,
LoginComponent,
RegisterComponent,
SearchCiphersPipe,
StopClickDirective,
StopPropDirective,
TwoFactorComponent,
VaultComponent,
],
entryComponents: [

167
src/app/services.module.ts Normal file
View File

@ -0,0 +1,167 @@
import {
APP_INITIALIZER,
NgModule,
} from '@angular/core';
import { ToasterModule } from 'angular2-toaster';
import { I18nService } from '../services/i18n.service';
import { WebMessagingService } from '../services/webMessaging.service';
import { WebPlatformUtilsService } from '../services/webPlatformUtils.service';
import { WebStorageService } from '../services/webStorage.service';
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { ValidationService } from 'jslib/angular/services/validation.service';
import { Analytics } from 'jslib/misc/analytics';
import { ApiService } from 'jslib/services/api.service';
import { AppIdService } from 'jslib/services/appId.service';
import { AuditService } from 'jslib/services/audit.service';
import { AuthService } from 'jslib/services/auth.service';
import { CipherService } from 'jslib/services/cipher.service';
import { CollectionService } from 'jslib/services/collection.service';
import { ConstantsService } from 'jslib/services/constants.service';
import { ContainerService } from 'jslib/services/container.service';
import { CryptoService } from 'jslib/services/crypto.service';
import { EnvironmentService } from 'jslib/services/environment.service';
import { ExportService } from 'jslib/services/export.service';
import { FolderService } from 'jslib/services/folder.service';
import { LockService } from 'jslib/services/lock.service';
import { PasswordGenerationService } from 'jslib/services/passwordGeneration.service';
import { SettingsService } from 'jslib/services/settings.service';
import { StateService } from 'jslib/services/state.service';
import { SyncService } from 'jslib/services/sync.service';
import { TokenService } from 'jslib/services/token.service';
import { TotpService } from 'jslib/services/totp.service';
import { UserService } from 'jslib/services/user.service';
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
import { ApiService as ApiServiceAbstraction } from 'jslib/abstractions/api.service';
import { AppIdService as AppIdServiceAbstraction } from 'jslib/abstractions/appId.service';
import { AuditService as AuditServiceAbstraction } from 'jslib/abstractions/audit.service';
import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.service';
import { CipherService as CipherServiceAbstraction } from 'jslib/abstractions/cipher.service';
import { CollectionService as CollectionServiceAbstraction } from 'jslib/abstractions/collection.service';
import { CryptoService as CryptoServiceAbstraction } from 'jslib/abstractions/crypto.service';
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib/abstractions/cryptoFunction.service';
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib/abstractions/environment.service';
import { ExportService as ExportServiceAbstraction } from 'jslib/abstractions/export.service';
import { FolderService as FolderServiceAbstraction } from 'jslib/abstractions/folder.service';
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
import { LockService as LockServiceAbstraction } from 'jslib/abstractions/lock.service';
import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service';
import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service';
import {
PasswordGenerationService as PasswordGenerationServiceAbstraction,
} from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib/abstractions/platformUtils.service';
import { SettingsService as SettingsServiceAbstraction } from 'jslib/abstractions/settings.service';
import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service';
import { StorageService as StorageServiceAbstraction } from 'jslib/abstractions/storage.service';
import { SyncService as SyncServiceAbstraction } from 'jslib/abstractions/sync.service';
import { TokenService as TokenServiceAbstraction } from 'jslib/abstractions/token.service';
import { TotpService as TotpServiceAbstraction } from 'jslib/abstractions/totp.service';
import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.service';
const i18nService = new I18nService(window.navigator.language, 'locales');
const stateService = new StateService();
const broadcasterService = new BroadcasterService();
const messagingService = new WebMessagingService();
const platformUtilsService = new WebPlatformUtilsService(messagingService);
const storageService: StorageServiceAbstraction = new WebStorageService();
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new WebCryptoFunctionService(window,
platformUtilsService);
const cryptoService = new CryptoService(storageService, storageService, cryptoFunctionService);
const tokenService = new TokenService(storageService);
const appIdService = new AppIdService(storageService);
const apiService = new ApiService(tokenService, platformUtilsService,
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
const environmentService = new EnvironmentService(apiService, storageService);
const userService = new UserService(tokenService, storageService);
const settingsService = new SettingsService(userService, storageService);
const cipherService = new CipherService(cryptoService, userService, settingsService,
apiService, storageService, i18nService, platformUtilsService);
const folderService = new FolderService(cryptoService, userService,
() => i18nService.t('noneFolder'), apiService, storageService, i18nService);
const collectionService = new CollectionService(cryptoService, userService, storageService, i18nService);
const lockService = new LockService(cipherService, folderService, collectionService,
cryptoService, platformUtilsService, storageService, messagingService, null);
const syncService = new SyncService(userService, apiService, settingsService,
folderService, cipherService, cryptoService, collectionService, storageService, messagingService,
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
const passwordGenerationService = new PasswordGenerationService(cryptoService, storageService);
const totpService = new TotpService(storageService, cryptoFunctionService);
const containerService = new ContainerService(cryptoService, platformUtilsService);
const authService = new AuthService(cryptoService, apiService,
userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService);
const exportService = new ExportService(folderService, cipherService);
const auditService = new AuditService(cryptoFunctionService);
const analytics = new Analytics(window, () => platformUtilsService.isDev(),
platformUtilsService, storageService, appIdService);
containerService.attachToWindow(window);
environmentService.setUrlsFromStorage().then(() => {
return syncService.fullSync(true);
});
export function initFactory(): Function {
return async () => {
lockService.init(true);
const locale = await storageService.get<string>(ConstantsService.localeKey);
await i18nService.init(locale);
await authService.init();
const htmlEl = window.document.documentElement;
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
htmlEl.classList.add('locale_' + i18nService.translationLocale);
let theme = await storageService.get<string>(ConstantsService.themeKey);
if (theme == null) {
theme = 'light';
}
htmlEl.classList.add('theme_' + theme);
stateService.save(ConstantsService.disableFaviconKey,
await storageService.get<boolean>(ConstantsService.disableFaviconKey));
};
}
@NgModule({
imports: [
ToasterModule,
],
declarations: [],
providers: [
ValidationService,
AuthGuardService,
{ provide: AuditServiceAbstraction, useValue: auditService },
{ provide: AuthServiceAbstraction, useValue: authService },
{ provide: CipherServiceAbstraction, useValue: cipherService },
{ provide: FolderServiceAbstraction, useValue: folderService },
{ provide: CollectionServiceAbstraction, useValue: collectionService },
{ provide: EnvironmentServiceAbstraction, useValue: environmentService },
{ provide: TotpServiceAbstraction, useValue: totpService },
{ provide: TokenServiceAbstraction, useValue: tokenService },
{ provide: I18nServiceAbstraction, useValue: i18nService },
{ provide: CryptoServiceAbstraction, useValue: cryptoService },
{ provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService },
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
{ provide: ApiServiceAbstraction, useValue: apiService },
{ provide: SyncServiceAbstraction, useValue: syncService },
{ provide: UserServiceAbstraction, useValue: userService },
{ provide: MessagingServiceAbstraction, useValue: messagingService },
{ provide: BroadcasterService, useValue: broadcasterService },
{ provide: SettingsServiceAbstraction, useValue: settingsService },
{ provide: LockServiceAbstraction, useValue: lockService },
{ provide: StorageServiceAbstraction, useValue: storageService },
{ provide: StateServiceAbstraction, useValue: stateService },
{ provide: ExportServiceAbstraction, useValue: exportService },
{
provide: APP_INITIALIZER,
useFactory: initFactory,
deps: [],
multi: true,
},
],
})
export class ServicesModule {
}

View File

@ -1 +1 @@
<b>The vault!!</b>
<b>The vault!! {{ 'hello' | i18n }}</b>

View File

@ -0,0 +1,5 @@
{
"hello": {
"message": "hello world"
}
}

View File

@ -0,0 +1,5 @@
{
"hello": {
"message": "hola mundo"
}
}

View File

@ -0,0 +1,16 @@
import { I18nService as BaseI18nService } from 'jslib/services/i18n.service';
export class I18nService extends BaseI18nService {
constructor(systemLanguage: string, localesDirectory: string) {
super(systemLanguage, localesDirectory, async (formattedLocale: string) => {
const filePath = this.localesDirectory + '/' + formattedLocale + '/messages.json';
const localesResult = await fetch(filePath);
const locales = await localesResult.json();
return locales;
});
this.supportedTranslationLocales = [
'en', 'es',
];
}
}

View File

@ -0,0 +1,7 @@
import { MessagingService } from 'jslib/abstractions/messaging.service';
export class WebMessagingService implements MessagingService {
send(subscriber: string, arg: any = {}) {
//
}
}

View File

@ -0,0 +1,181 @@
import { DeviceType } from 'jslib/enums/deviceType';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { AnalyticsIds } from 'jslib/misc/analytics';
const DialogPromiseExpiration = 600000; // 10 minutes
export class WebPlatformUtilsService implements PlatformUtilsService {
identityClientId: string = 'web';
private showDialogResolves = new Map<number, { resolve: (value: boolean) => void, date: Date }>();
private deviceCache: DeviceType = null;
private analyticsIdCache: string = null;
constructor(private messagingService: MessagingService) { }
getDevice(): DeviceType {
if (this.deviceCache) {
return this.deviceCache;
}
if (navigator.userAgent.indexOf(' Firefox/') !== -1 || navigator.userAgent.indexOf(' Gecko/') !== -1) {
this.deviceCache = DeviceType.Firefox;
} else if (navigator.userAgent.indexOf(' OPR/') >= 0) {
this.deviceCache = DeviceType.Opera;
} else if (navigator.userAgent.indexOf(' Edge/') !== -1) {
this.deviceCache = DeviceType.Edge;
} else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) {
this.deviceCache = DeviceType.Vivaldi;
} else if (navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
this.deviceCache = DeviceType.Safari;
} else if ((window as any).chrome && navigator.userAgent.indexOf(' Chrome/') !== -1) {
this.deviceCache = DeviceType.Chrome;
}
return this.deviceCache;
}
getDeviceString(): string {
return DeviceType[this.getDevice()].toLowerCase();
}
isFirefox(): boolean {
return this.getDevice() === DeviceType.Firefox;
}
isChrome(): boolean {
return this.getDevice() === DeviceType.Chrome;
}
isEdge(): boolean {
return this.getDevice() === DeviceType.Edge;
}
isOpera(): boolean {
return this.getDevice() === DeviceType.Opera;
}
isVivaldi(): boolean {
return this.getDevice() === DeviceType.Vivaldi;
}
isSafari(): boolean {
return this.getDevice() === DeviceType.Safari;
}
isMacAppStore(): boolean {
return false;
}
analyticsId(): string {
if (this.analyticsIdCache) {
return this.analyticsIdCache;
}
this.analyticsIdCache = (AnalyticsIds as any)[this.getDevice()];
return this.analyticsIdCache;
}
getDomain(uriString: string): string {
return uriString;
}
isViewOpen(): boolean {
return false;
}
launchUri(uri: string, options?: any): void {
//
}
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
//
}
getApplicationVersion(): string {
return '1.2.3';
}
supportsU2f(win: Window): boolean {
if (win != null && (win as any).u2f != null) {
return true;
}
return this.isChrome() || this.isOpera();
}
supportsDuo(): boolean {
return true;
}
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string, global?: any): void {
throw new Error('showToast not implemented');
}
showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string) {
const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
this.messagingService.send('showDialog', {
text: text,
title: title,
confirmText: confirmText,
cancelText: cancelText,
type: type,
dialogId: dialogId,
});
return new Promise<boolean>((resolve) => {
this.showDialogResolves.set(dialogId, { resolve: resolve, date: new Date() });
});
}
isDev(): boolean {
return process.env.ENV === 'development';
}
copyToClipboard(text: string, options?: any): void {
const doc = options ? options.doc : window.document;
if ((window as any).clipboardData && (window as any).clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
(window as any).clipboardData.setData('Text', text);
} else if (doc.queryCommandSupported && doc.queryCommandSupported('copy')) {
const textarea = doc.createElement('textarea');
textarea.textContent = text;
// Prevent scrolling to bottom of page in MS Edge.
textarea.style.position = 'fixed';
doc.body.appendChild(textarea);
textarea.select();
try {
// Security exception may be thrown by some browsers.
doc.execCommand('copy');
} catch (e) {
// tslint:disable-next-line
console.warn('Copy to clipboard failed.', e);
} finally {
doc.body.removeChild(textarea);
}
}
}
resolveDialogPromise(dialogId: number, confirmed: boolean) {
if (this.showDialogResolves.has(dialogId)) {
const resolveObj = this.showDialogResolves.get(dialogId);
resolveObj.resolve(confirmed);
this.showDialogResolves.delete(dialogId);
}
// Clean up old promises
const deleteIds: number[] = [];
this.showDialogResolves.forEach((val, key) => {
const age = new Date().getTime() - val.date.getTime();
if (age > DialogPromiseExpiration) {
deleteIds.push(key);
}
});
deleteIds.forEach((id) => {
this.showDialogResolves.delete(id);
});
}
}

View File

@ -0,0 +1,24 @@
import { StorageService } from 'jslib/abstractions/storage.service';
export class WebStorageService implements StorageService {
private store: any = {};
get<T>(key: string): Promise<T> {
const val = this.store[key];
if (val == null) {
return Promise.resolve(null);
}
return Promise.resolve(val as T);
}
save(key: string, obj: any): Promise<any> {
this.store[key] = obj;
return Promise.resolve();
}
remove(key: string): Promise<any> {
delete this.store[key];
return Promise.resolve();
}
}

View File

@ -29,5 +29,6 @@
"jslib/src/angular/dummy.module.ts",
"jslib/src/services/nodeApi.service.ts",
"jslib/src/services/lowdbStorage.service.ts",
"jslib/src/misc/nodeUtils.ts",
]
}

View File

@ -97,6 +97,7 @@ const plugins = [
{ from: './src/browserconfig.xml' },
{ from: './src/app-id.json' },
{ from: './src/images', to: 'images' },
{ from: './src/locales', to: 'locales' },
]),
extractCss,
new webpack.DefinePlugin({