1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 18:53:29 +00:00

move new app into popup folder

This commit is contained in:
Kyle Spearrin
2018-04-10 21:54:20 -04:00
parent 1fed135b31
commit 67ab9b1d3e
169 changed files with 35 additions and 63 deletions

View File

@@ -0,0 +1,59 @@
<form #form class="modal-content" (ngSubmit)="submit()">
<header>
<div class="left">
<a routerLink="/home">{{'close' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'appName' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'save' | i18n}}</span>
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading"></i>
</button>
</div>
</header>
<content>
<div class="box">
<div class="box-header">
{{'selfHostedEnvironment' | i18n}}
</div>
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="baseUrl">{{'baseUrl' | i18n}}</label>
<input id="baseUrl" type="text" name="BaseUrl" [(ngModel)]="baseUrl"
placeholder="ex. https://bitwarden.company.com">
</div>
</div>
<div class="box-footer">
{{'selfHostedEnvironmentFooter' | i18n}}
</div>
</div>
<div class="box">
<div class="box-header">
{{'customEnvironment' | i18n}}
</div>
<div class="box-content" [hidden]="!showCustom">
<div class="box-content-row" appBoxRow>
<label for="webVaultUrl">{{'webVaultUrl' | i18n}}</label>
<input id="webVaultUrl" type="text" name="WebVaultUrl" [(ngModel)]="webVaultUrl">
</div>
<div class="box-content-row" appBoxRow>
<label for="apiUrl">{{'apiUrl' | i18n}}</label>
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl">
</div>
<div class="box-content-row" appBoxRow>
<label for="identityUrl">{{'identityUrl' | i18n}}</label>
<input id="identityUrl" type="text" name="IdentityUrl" [(ngModel)]="identityUrl">
</div>
<div class="box-content-row" appBoxRow>
<label for="iconsUrl">{{'iconsUrl' | i18n}}</label>
<input id="iconsUrl" type="text" name="IconsUrl" [(ngModel)]="iconsUrl">
</div>
</div>
<div class="box-footer" [hidden]="!showCustom">
{{'customEnvironmentFooter' | i18n}}
</div>
</div>
</content>
</form>

View File

@@ -0,0 +1,28 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib/angular/components/environment.component';
@Component({
selector: 'app-environment',
templateUrl: 'environment.component.html',
})
export class EnvironmentComponent extends BaseEnvironmentComponent {
constructor(analytics: Angulartics2, toasterService: ToasterService,
environmentService: EnvironmentService, i18nService: I18nService,
private router: Router) {
super(analytics, toasterService, environmentService, i18nService);
this.showCustom = true;
}
saved() {
super.saved();
this.router.navigate(['']);
}
}

View File

@@ -0,0 +1,29 @@
<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-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>
</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,15 @@
<div class="home-page">
<a routerLink="/environment" class="settings-icon">
<i class="fa fa-cog fa-lg"></i><span>&nbsp;{{'settings' | i18n}}</span>
</a>
<img src="../images/logo@2x.png" alt="bitwarden" class="img-responsive" />
<p>{{'loginOrCreateNewAccount' | i18n}}</p>
<div class="bottom-buttons">
<a class="btn btn-lg btn-primary btn-block" routerLink="/register">
<strong>{{'createAccount' | i18n}}</strong>
</a>
<a class="btn btn-lg btn-link btn-block" routerLink="/login">
{{'login' | i18n}}
</a>
</div>
</div>

View File

@@ -0,0 +1,16 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { I18nService } from 'jslib/abstractions/i18n.service';
@Component({
selector: 'app-home',
templateUrl: 'home.component.html',
})
export class HomeComponent {
constructor(private router: Router, i18nService: I18nService,
analytics: Angulartics2, toasterService: ToasterService) { }
}

View File

@@ -0,0 +1,34 @@
<form (ngSubmit)="submit()">
<header>
<div class="left"></div>
<div class="center">
<span class="title">{{'verifyMasterPassword' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick>{{'submit' | i18n}}</button>
</div>
</header>
<content>
<div class="box">
<div class="box-content">
<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>
</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>
<p class="text-center">
<a href="#" appStopClick (click)="logOut()">{{'logOut' | i18n}}</a>
</p>
</content>
</form>

View File

@@ -0,0 +1,28 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { UserService } from 'jslib/abstractions/user.service';
import { LockComponent as BaseLockComponent } from 'jslib/angular/components/lock.component';
@Component({
selector: 'app-lock',
templateUrl: 'lock.component.html',
})
export class LockComponent extends BaseLockComponent {
constructor(router: Router, analytics: Angulartics2,
toasterService: ToasterService, i18nService: I18nService,
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
userService: UserService, cryptoService: CryptoService) {
super(router, analytics, toasterService, i18nService, platformUtilsService,
messagingService, userService, cryptoService);
this.successRoute = '/tabs/current';
}
}

View File

@@ -0,0 +1,44 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a routerLink="/home">{{'cancel' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'appName' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'login' | i18n}}</span>
<i class="fa fa-spinner 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 === ''">
</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>
<p class="text-center">
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
</p>
</content>
</form>

View File

@@ -0,0 +1,28 @@
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, syncService: SyncService) {
super(authService, router, analytics, toasterService, i18nService, syncService);
this.successRoute = '/tabs/vault';
}
settings() {
this.router.navigate(['environment']);
}
}

View File

@@ -0,0 +1,70 @@
<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-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 === ''">
</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(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>
</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-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>
</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/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>
</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">{{'loading' | i18n}}</span>
<div *ngIf="u2fReady">
<p>{{'insertU2f' | i18n}}</p>
<img src="../images/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 no-vpad 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,114 @@
import {
ChangeDetectorRef,
Component,
NgZone,
OnDestroy,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { BrowserApi } from '../../browser/browserApi';
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, syncService, window, environmentService);
this.successRoute = '/tabs/vault';
}
async ngOnInit() {
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case '2faPageResponse':
if (message.type === 'duo') {
this.token = message.data.sigValue;
this.submitWithTab(message.webExtSender.tab);
}
default:
break;
}
this.changeDetectorRef.detectChanges();
})
});
this.showNewWindowMessage = this.platformUtilsService.isSafari();
await super.ngOnInit();
if (this.selectedProviderType == null) {
return;
}
var isDuo = this.selectedProviderType == TwoFactorProviderType.Duo ||
this.selectedProviderType == TwoFactorProviderType.OrganizationDuo;
if (!this.platformUtilsService.isSafari() || !isDuo) {
return;
}
const params = this.authService.twoFactorProviders.get(this.selectedProviderType);
const tab = BrowserApi.createNewTab(BrowserApi.getAssetUrl('2fa/index.html'));
const tabToSend = BrowserApi.makeTabObject(tab);
window.setTimeout(() => {
BrowserApi.tabSendMessage(tabToSend, {
command: '2faPageData',
data: {
type: 'duo',
host: params.Host,
signature: params.Signature,
}
});
}, 500);
}
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
super.ngOnDestroy();
}
anotherMethod() {
this.router.navigate(['2fa-options']);
}
async submitWithTab(sendSuccessToTab: any) {
await super.submit();
if (sendSuccessToTab != null) {
window.setTimeout(() => {
BrowserApi.tabSendMessage(sendSuccessToTab, {
command: '2faPageData',
data: { type: 'success' }
});
}, 1000);
}
}
}

View File

@@ -0,0 +1,131 @@
import {
trigger,
animate,
style,
group,
query,
transition,
} from '@angular/animations';
const queryShown = query(':enter, :leave', [
style({ position: 'fixed', width: '100%', height: '100%' }),
], { optional: true });
// ref: https://github.com/angular/angular/issues/15477
const queryChildRoute = query('router-outlet ~ *', [
style({}),
animate(1, style({})),
], { optional: true });
const speed = '0.4s';
export function queryTranslate(direction: string, axis: string, from: number, to: number, zIndex: number = 1000) {
return query(':' + direction, [
style({ transform: 'translate' + axis + '(' + from + '%)', zIndex: zIndex, boxShadow: '0 3px 2px -2px gray' }),
animate(speed + ' ease-in-out', style({ transform: 'translate' + axis + '(' + to + '%)' })),
], { optional: true });
}
export function queryTranslateX(direction: string, from: number, to: number, zIndex: number = 1000) {
return queryTranslate(direction, 'X', from, to, zIndex);
}
export function queryTranslateY(direction: string, from: number, to: number, zIndex: number = 1000) {
return queryTranslate(direction, 'Y', from, to, zIndex);
}
const inSlideLeft = [
queryShown,
group([
queryTranslateX('enter', 100, 0),
queryTranslateX('leave', 0, -100),
queryChildRoute,
]),
];
const outSlideRight = [
queryShown,
group([
queryTranslateX('enter', -100, 0),
queryTranslateX('leave', 0, 100),
]),
];
const inSlideUp = [
queryShown,
group([
queryTranslateY('enter', 100, 0, 1010),
queryTranslateY('leave', 0, 0),
queryChildRoute,
]),
];
const outSlideDown = [
queryShown,
group([
queryTranslateY('enter', 0, 0),
queryTranslateY('leave', 0, 100, 1010),
]),
];
const inSlideDown = [
queryShown,
group([
queryTranslateY('enter', -100, 0, 1010),
queryTranslateY('leave', 0, 0),
queryChildRoute,
]),
];
const outSlideUp = [
queryShown,
group([
queryTranslateY('enter', 0, 0),
queryTranslateY('leave', 0, -100, 1010),
]),
];
export const routerTransition = trigger('routerTransition', [
transition('void => home', inSlideLeft),
transition('void => tabs', inSlideLeft),
transition('home => login', inSlideLeft),
transition('home => environment', inSlideUp),
transition('login => home', outSlideRight),
transition('login => hint', inSlideUp),
transition('login => tabs, login => 2fa', inSlideLeft),
transition('hint => login', outSlideDown),
transition('environment => home', outSlideDown),
transition('2fa => login', outSlideRight),
transition('2fa => 2fa-options', inSlideUp),
transition('2fa-options => 2fa', outSlideDown),
transition('2fa => tabs', inSlideLeft),
transition('tabs => ciphers', inSlideLeft),
transition('ciphers => tabs', outSlideRight),
transition('tabs => view-cipher, ciphers => view-cipher', inSlideUp),
transition('view-cipher => tabs, view-cipher => ciphers', outSlideDown),
transition('view-cipher => edit-cipher', inSlideUp),
transition('edit-cipher => view-cipher', outSlideDown),
transition('tabs => add-cipher, ciphers => add-cipher', inSlideUp),
transition('add-cipher => tabs, add-cipher => ciphers', outSlideDown),
transition('generator => generator-history', inSlideLeft),
transition('generator-history => generator', outSlideRight),
transition('add-cipher => generator, edit-cipher => generator, tabs => generator', inSlideUp),
transition('generator => add-cipher, generator => edit-cipher, generator => tabs', outSlideDown),
transition('tabs => export', inSlideUp),
transition('export => tabs', outSlideDown),
transition('tabs => lock', inSlideDown),
transition('lock => tabs', outSlideUp),
]);

View File

@@ -0,0 +1,176 @@
import { NgModule } from '@angular/core';
import {
RouterModule,
Routes,
} from '@angular/router';
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
import { LaunchGuardService } from './services/launch-guard.service';
import { EnvironmentComponent } from './accounts/environment.component';
import { HintComponent } from './accounts/hint.component';
import { HomeComponent } from './accounts/home.component';
import { LockComponent } from './accounts/lock.component';
import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { SettingsComponent } from './settings/settings.component';
import { TabsComponent } from './tabs.component';
import { ExportComponent } from './tools/export.component';
import { PasswordGeneratorComponent } from './tools/password-generator.component';
import { PasswordGeneratorHistoryComponent } from './tools/password-generator-history.component';
import { ToolsComponent } from './tools/tools.component';
import { AddEditComponent } from './vault/add-edit.component';
import { CiphersComponent } from './vault/ciphers.component';
import { CurrentTabComponent } from './vault/current-tab.component';
import { GroupingsComponent } from './vault/groupings.component';
import { ViewComponent } from './vault/view.component';
const routes: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full',
},
{
path: 'vault',
redirectTo: '/tabs/vault',
pathMatch: 'full',
},
{
path: 'home',
component: HomeComponent,
canActivate: [LaunchGuardService],
data: { state: 'home' },
},
{
path: 'login',
component: LoginComponent,
canActivate: [LaunchGuardService],
data: { state: 'login' },
},
{
path: 'lock',
component: LockComponent,
data: { state: 'lock' },
},
{
path: '2fa',
component: TwoFactorComponent,
canActivate: [LaunchGuardService],
data: { state: '2fa' },
},
{
path: '2fa-options',
component: TwoFactorOptionsComponent,
canActivate: [LaunchGuardService],
data: { state: '2fa-options' },
},
{
path: 'register',
component: RegisterComponent,
canActivate: [LaunchGuardService],
data: { state: 'register' },
},
{
path: 'hint',
component: HintComponent,
canActivate: [LaunchGuardService],
data: { state: 'hint' },
},
{
path: 'environment',
component: EnvironmentComponent,
canActivate: [LaunchGuardService],
data: { state: 'environment' },
},
{
path: 'ciphers',
component: CiphersComponent,
canActivate: [AuthGuardService],
data: { state: 'ciphers' },
},
{
path: 'view-cipher',
component: ViewComponent,
canActivate: [AuthGuardService],
data: { state: 'view-cipher' },
},
{
path: 'add-cipher',
component: AddEditComponent,
canActivate: [AuthGuardService],
data: { state: 'add-cipher' },
},
{
path: 'edit-cipher',
component: AddEditComponent,
canActivate: [AuthGuardService],
data: { state: 'edit-cipher' },
},
{
path: 'generator',
component: PasswordGeneratorComponent,
canActivate: [AuthGuardService],
data: { state: 'generator' },
},
{
path: 'generator-history',
component: PasswordGeneratorHistoryComponent,
canActivate: [AuthGuardService],
data: { state: 'generator-history' },
},
{
path: 'export',
component: ExportComponent,
canActivate: [AuthGuardService],
data: { state: 'export' },
},
{
path: 'tabs',
component: TabsComponent,
data: { state: 'tabs' },
children: [
{
path: '',
redirectTo: '/tabs/vault',
pathMatch: 'full',
},
{
path: 'current',
component: CurrentTabComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_current' },
},
{
path: 'vault',
component: GroupingsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_vault' },
},
{
path: 'tools',
component: ToolsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_tools' },
},
{
path: 'settings',
component: SettingsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_settings' },
},
]
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true,
/*enableTracing: true,*/
})],
exports: [RouterModule],
})
export class AppRoutingModule { }

141
src/popup/app.component.ts Normal file
View File

@@ -0,0 +1,141 @@
import {
ToasterConfig,
ToasterContainerComponent,
} from 'angular2-toaster';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import swal from 'sweetalert';
import {
ChangeDetectorRef,
Component,
NgZone,
OnInit,
} from '@angular/core';
import {
NavigationEnd,
Router,
RouterOutlet,
} from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { BrowserApi } from '../browser/browserApi';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { ConstantsService } from 'jslib/services/constants.service';
import { routerTransition } from './app-routing.animations';
@Component({
selector: 'app-root',
styles: [],
animations: [routerTransition],
template: `
<toaster-container [toasterconfig]="toasterConfig"></toaster-container>
<main [@routerTransition]="getState(o)">
<router-outlet #o="outlet"></router-outlet>
</main>`,
})
export class AppComponent implements OnInit {
toasterConfig: ToasterConfig = new ToasterConfig({
showCloseButton: false,
mouseoverTimerStop: true,
animation: 'slideUp',
limit: 2,
positionClass: 'toast-bottom-full-width',
newestOnTop: false
});
private lastActivity: number = null;
private previousUrl: string = '';
constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, private analytics: Angulartics2,
private toasterService: ToasterService, private storageService: StorageService,
private broadcasterService: BroadcasterService, private authService: AuthService,
private i18nService: I18nService, private router: Router,
private stateService: StateService, private messagingService: MessagingService,
private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone) { }
ngOnInit() {
window.onmousemove = () => this.recordActivity();
window.onmousedown = () => this.recordActivity();
window.ontouchstart = () => this.recordActivity();
window.onclick = () => this.recordActivity();
window.onscroll = () => this.recordActivity();
window.onkeypress = () => this.recordActivity();
(window as any).bitwardenPopupMainMessageListener = async (msg: any, sender: any, sendResponse: any) => {
if (msg.command === 'doneLoggingOut') {
this.ngZone.run(async () => {
this.authService.logOut(() => {
this.analytics.eventTrack.next({ action: 'Logged Out' });
if (msg.expired) {
this.toasterService.popAsync('warning', this.i18nService.t('loggedOut'),
this.i18nService.t('loginExpired'));
}
this.router.navigate(['home']);
});
this.changeDetectorRef.detectChanges();
});
} else if (msg.command === 'showDialog') {
const buttons = [msg.confirmText == null ? this.i18nService.t('ok') : msg.confirmText];
if (msg.cancelText != null) {
buttons.unshift(msg.cancelText);
}
const confirmed = await swal({
title: msg.title,
text: msg.text,
buttons: buttons,
icon: msg.type,
});
this.messagingService.send('showDialogResolve', {
dialogId: msg.dialogId,
confirmed: confirmed,
});
} else {
msg.webExtSender = sender;
this.broadcasterService.send(msg);
}
};
BrowserApi.messageListener((window as any).bitwardenPopupMainMessageListener);
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
const url = event.urlAfterRedirects || event.url || '';
if (url.startsWith('/tabs/') && this.previousUrl.startsWith('/tabs/')) {
this.stateService.remove('GroupingsComponent');
this.stateService.remove('CiphersComponent');
}
if (url.startsWith('/tabs/')) {
this.stateService.remove('addEditCipher');
}
this.previousUrl = url;
}
});
}
getState(outlet: RouterOutlet) {
return outlet.activatedRouteData.state;
}
private async recordActivity() {
const now = (new Date()).getTime();
if (this.lastActivity != null && now - this.lastActivity < 250) {
return;
}
this.lastActivity = now;
this.storageService.save(ConstantsService.lastActiveKey, now);
}
}

110
src/popup/app.module.ts Normal file
View File

@@ -0,0 +1,110 @@
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 { ServicesModule } from './services/services.module';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { EnvironmentComponent } from './accounts/environment.component';
import { HintComponent } from './accounts/hint.component';
import { HomeComponent } from './accounts/home.component';
import { LockComponent } from './accounts/lock.component';
import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { SettingsComponent } from './settings/settings.component';
import { TabsComponent } from './tabs.component';
import { ExportComponent } from './tools/export.component';
import { PasswordGeneratorComponent } from './tools/password-generator.component';
import { PasswordGeneratorHistoryComponent } from './tools/password-generator-history.component';
import { ToolsComponent } from './tools/tools.component';
import { AddEditComponent } from './vault/add-edit.component';
import { CiphersComponent } from './vault/ciphers.component';
import { CurrentTabComponent } from './vault/current-tab.component';
import { GroupingsComponent } from './vault/groupings.component';
import { ViewComponent } from './vault/view.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 { 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';
import { ActionButtonsComponent } from './components/action-buttons.component';
import { CiphersListComponent } from './components/ciphers-list.component';
import { PopOutComponent } from './components/pop-out.component';
import { IconComponent } from 'jslib/angular/components/icon.component';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
AppRoutingModule,
ServicesModule,
Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], {
pageTracking: {
clearQueryParams: true,
},
}),
ToasterModule,
],
declarations: [
ActionButtonsComponent,
AddEditComponent,
ApiActionDirective,
AppComponent,
AutofocusDirective,
BlurClickDirective,
BoxRowDirective,
CiphersComponent,
CiphersListComponent,
CurrentTabComponent,
EnvironmentComponent,
ExportComponent,
FallbackSrcDirective,
GroupingsComponent,
HomeComponent,
HintComponent,
I18nPipe,
IconComponent,
LockComponent,
LoginComponent,
PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PopOutComponent,
RegisterComponent,
SearchCiphersPipe,
SettingsComponent,
StopClickDirective,
StopPropDirective,
TabsComponent,
ToolsComponent,
TwoFactorOptionsComponent,
TwoFactorComponent,
ViewComponent,
],
entryComponents: [
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule { }

View File

@@ -1,43 +0,0 @@
angular
.module('bit.accounts')
.controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, popupUtilsService,
$analytics, i18nService, $timeout) {
$timeout(function () {
popupUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('email').focus();
}, 500);
$scope.i18n = i18nService;
$scope.model = {};
$scope.submitPromise = null;
$scope.submit = function (model) {
if (!model.email) {
toastr.error(i18nService.emailRequired, i18nService.errorsOccurred);
return;
}
if (model.email.indexOf('@') === -1) {
toastr.error(i18nService.invalidEmail, i18nService.errorsOccurred);
return;
}
var request = new PasswordHintRequest(model.email);
$scope.submitPromise = hintPromise(request);
$scope.submitPromise.then(function () {
$analytics.eventTrack('Requested Hint');
toastr.success(i18nService.masterPassSent);
$state.go('login');
});
};
function hintPromise(request) {
return $q(function (resolve, reject) {
apiService.postPasswordHint(request).then(function () {
resolve();
}, function (error) {
reject(error);
});
});
}
});

View File

@@ -1,58 +0,0 @@
angular
.module('bit.accounts')
.controller('accountsLoginController', function ($scope, $state, $stateParams, authService, userService, toastr,
popupUtilsService, $analytics, i18nService, $timeout) {
$timeout(function () {
popupUtilsService.initListSectionItemListeners(document, angular);
if ($stateParams.email) {
document.getElementById('master-password').focus();
}
else {
document.getElementById('email').focus();
}
}, 500);
$scope.i18n = i18nService;
$scope.model = {
email: $stateParams.email
};
$scope.loginPromise = null;
$scope.login = function (model) {
if (!model.email) {
toastr.error(i18nService.emailRequired, i18nService.errorsOccurred);
return;
}
if (model.email.indexOf('@') === -1) {
toastr.error(i18nService.invalidEmail, i18nService.errorsOccurred);
return;
}
if (!model.masterPassword) {
toastr.error(i18nService.masterPassRequired, i18nService.errorsOccurred);
return;
}
$scope.loginPromise = authService.logIn(model.email, model.masterPassword);
$scope.loginPromise.then(function (response) {
if (response.twoFactor) {
$analytics.eventTrack('Logged In To Two-step');
$state.go('twoFactor', {
animation: 'in-slide-left',
email: model.email,
masterPassword: model.masterPassword,
providers: response.twoFactorProviders,
provider: null
});
}
else {
$analytics.eventTrack('Logged In');
$state.go('tabs.vault', {
animation: 'in-slide-left',
syncOnLoad: true
});
}
});
};
});

View File

@@ -1,215 +0,0 @@
angular
.module('bit.accounts')
.controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, platformUtilsService,
$analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService,
environmentService, SweetAlert, popupUtilsService) {
$timeout(function () {
popupUtilsService.initListSectionItemListeners(document, angular);
}, 500);
$scope.i18n = i18nService;
$scope.showNewWindowMessage = platformUtilsService.isSafari();
var customWebVaultUrl = null;
if (environmentService.baseUrl) {
customWebVaultUrl = environmentService.baseUrl;
}
else if (environmentService.webVaultUrl) {
customWebVaultUrl = environmentService.webVaultUrl;
}
var u2f = new $window.U2f($window, customWebVaultUrl, function (data) {
$timeout(function () {
$scope.login(data);
});
}, function (error) {
$timeout(function () {
toastr.error(error, i18nService.errorsOccurred);
});
}, function (info) {
$timeout(function () {
if (info === 'ready') {
$scope.u2fReady = true;
}
});
});
var constants = constantsService;
var email = authService.email;
var masterPasswordHash = authService.masterPasswordHash;
var providers = authService.twoFactorProviders;
if (!email || !masterPasswordHash || !providers) {
$state.go('login');
return;
}
$scope.providerType = $stateParams.provider || $stateParams.provider === 0 ? $stateParams.provider :
authService.getDefaultTwoFactorProvider(platformUtilsService.supportsU2f($window));
$scope.twoFactorEmail = null;
$scope.token = null;
$scope.constantsProvider = constants.twoFactorProvider;
$scope.u2fReady = false;
$scope.remember = { checked: false };
init();
$scope.loginPromise = null;
$scope.login = function (token, sendSuccessToTab) {
if (!token) {
toastr.error(i18nService.verificationCodeRequired, i18nService.errorsOccurred);
return;
}
if ($scope.providerType === constants.twoFactorProvider.u2f) {
if (u2f) {
u2f.stop();
}
else {
return;
}
}
else if ($scope.providerType === constants.twoFactorProvider.email ||
$scope.providerType === constants.twoFactorProvider.authenticator) {
token = token.replace(' ', '');
}
$scope.loginPromise = authService.logInTwoFactor($scope.providerType, token, $scope.remember.checked);
$scope.loginPromise.then(function () {
$analytics.eventTrack('Logged In From Two-step');
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true }).then(function () {
if (sendSuccessToTab) {
$timeout(function () {
BrowserApi.tabSendMessage(sendSuccessToTab, {
command: '2faPageData',
data: { type: 'success' }
});
}, 1000);
}
});
}, function () {
u2f.start();
});
};
$scope.sendEmail = function (doToast) {
if ($scope.providerType !== constants.twoFactorProvider.email) {
return;
}
var request = new TwoFactorEmailRequest(email, masterPasswordHash);
apiService.postTwoFactorEmail(request, function () {
if (doToast) {
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
}
}, function () {
toastr.error('Could not send verification email.');
});
};
$scope.anotherMethod = function () {
$state.go('twoFactorMethods', {
animation: 'in-slide-up',
provider: $scope.providerType
});
};
$scope.back = function () {
$state.go('login', {
animation: 'out-slide-right'
});
};
$scope.$on('$destroy', function () {
u2f.stop();
u2f.cleanup();
u2f = null;
});
$scope.$on('2faPageResponse', (event, details) => {
if (details.type === 'duo') {
$scope.login(details.data.sigValue, details.tab);
}
});
function init() {
u2f.stop();
u2f.cleanup();
$timeout(function () {
var codeInput = document.getElementById('code');
if (codeInput) {
codeInput.focus();
}
var params = providers.get($scope.providerType);
if ($scope.providerType === constants.twoFactorProvider.duo ||
$scope.providerType === constants.twoFactorProvider.organizationDuo) {
if (platformUtilsService.isSafari()) {
var tab = BrowserApi.createNewTab(BrowserApi.getAssetUrl('2fa/index.html'));
var tabToSend = BrowserApi.makeTabObject(tab);
$timeout(() => {
BrowserApi.tabSendMessage(tabToSend, {
command: '2faPageData',
data: {
type: 'duo',
host: params.Host,
signature: params.Signature
}
});
}, 500);
}
else {
$window.Duo.init({
host: params.Host,
sig_request: params.Signature,
submit_callback: function (theForm) {
var sigElement = theForm.querySelector('input[name="sig_response"]');
if (sigElement) {
$scope.login(sigElement.value);
}
}
});
}
}
else if ($scope.providerType === constants.twoFactorProvider.u2f) {
var challenges = JSON.parse(params.Challenges);
u2f.init({
appId: challenges[0].appId,
challenge: challenges[0].challenge,
keys: [{
version: challenges[0].version,
keyHandle: challenges[0].keyHandle
}]
});
}
else if ($scope.providerType === constants.twoFactorProvider.email) {
$scope.twoFactorEmail = params.Email;
if (!platformUtilsService.isSafari() && BrowserApi.isPopupOpen() &&
!popupUtilsService.inSidebar($window) && !popupUtilsService.inTab($window) &&
!popupUtilsService.inPopout($window)) {
SweetAlert.swal({
title: i18nService.twoStepLogin,
text: i18nService.popup2faCloseMessage,
showCancelButton: true,
confirmButtonText: i18nService.yes,
cancelButtonText: i18nService.no
}, function (confirmed) {
if (confirmed) {
BrowserApi.createNewTab('/popup/index.html?uilocation=tab#!/login', true);
return;
}
else if (providers.size > 1) {
$scope.sendEmail(false);
}
});
}
else if (providers.size > 1) {
$scope.sendEmail(false);
}
}
}, 500);
}
});

View File

@@ -1,2 +0,0 @@
angular
.module('bit.accounts', ['toastr', 'ngAnimate']);

View File

@@ -1,64 +0,0 @@
angular
.module('bit.accounts')
.controller(
'accountsRegisterController',
function ($scope, $state, cryptoService, toastr, $q, apiService, popupUtilsService, $analytics,
i18nService, $timeout) {
$timeout(function () {
popupUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('email').focus();
}, 500);
$scope.i18n = i18nService;
$scope.model = {};
$scope.submitPromise = null;
$scope.submit = function (model) {
if (!model.email) {
toastr.error(i18nService.emailRequired, i18nService.errorsOccurred);
return;
}
if (model.email.indexOf('@') === -1) {
toastr.error(i18nService.invalidEmail, i18nService.errorsOccurred);
return;
}
if (!model.masterPassword) {
toastr.error(i18nService.masterPassRequired, i18nService.errorsOccurred);
return;
}
if (model.masterPassword.length < 8) {
toastr.error(i18nService.masterPassLength, i18nService.errorsOccurred);
return;
}
if (model.masterPassword !== model.masterPasswordRetype) {
toastr.error(i18nService.masterPassDoesntMatch, i18nService.errorsOccurred);
return;
}
var email = model.email.toLowerCase();
var key = cryptoService.makeKey(model.masterPassword, email);
$scope.submitPromise = registerPromise(key, model.masterPassword, email, model.hint);
$scope.submitPromise.then(function () {
$analytics.eventTrack('Registered');
toastr.success(i18nService.newAccountCreated);
$state.go('login', { email: email, animation: 'in-slide-left' });
});
};
function registerPromise(key, masterPassword, email, hint) {
var deferred = $q.defer();
var encKey;
cryptoService.makeEncKey(key).then(function (theEncKey) {
encKey = theEncKey;
return cryptoService.hashPassword(masterPassword, key);
}).then(function (hashedPassword) {
var request = new RegisterRequest(email, hashedPassword, hint, encKey.encryptedString);
apiService.postRegister(request).then(function () {
deferred.resolve();
}, function (error) {
deferred.reject(error);
});
});
return deferred.promise;
}
});

View File

@@ -1,59 +0,0 @@
angular
.module('bit.accounts')
.controller('accountsTwoFactorMethodsController', function ($scope, $state, $stateParams, constantsService,
utilsService, i18nService, $analytics, platformUtilsService, authService, $window) {
$scope.i18n = i18nService;
var constants = constantsService;
var providers = authService.twoFactorProviders;
var provider = $stateParams.provider;
$scope.providers = [];
if (providers.has(constants.twoFactorProvider.organizationDuo)) {
add(constants.twoFactorProvider.organizationDuo);
}
if (providers.has(constants.twoFactorProvider.authenticator)) {
add(constants.twoFactorProvider.authenticator);
}
if (providers.has(constants.twoFactorProvider.yubikey)) {
add(constants.twoFactorProvider.yubikey);
}
if (providers.has(constants.twoFactorProvider.email)) {
add(constants.twoFactorProvider.email);
}
if (providers.has(constants.twoFactorProvider.duo)) {
add(constants.twoFactorProvider.duo);
}
if (providers.has(constants.twoFactorProvider.u2f) && platformUtilsService.supportsU2f($window)) {
add(constants.twoFactorProvider.u2f);
}
$scope.choose = function (p) {
$state.go('twoFactor', {
animation: 'out-slide-down',
provider: p.type
});
};
$scope.cancel = function () {
$state.go('twoFactor', {
animation: 'out-slide-down',
provider: provider
});
};
$scope.recover = function () {
$analytics.eventTrack('Selected Recover');
BrowserApi.createNewTab('https://help.bitwarden.com/article/lost-two-step-device/');
};
function add(type) {
for (var i = 0; i < constants.twoFactorProviderInfo.length; i++) {
if (constants.twoFactorProviderInfo[i].type === type) {
$scope.providers.push(constants.twoFactorProviderInfo[i]);
}
}
}
});

View File

@@ -1,28 +0,0 @@
<form name="theForm" ng-submit="submit(model)" bit-form="submitPromise">
<div class="header">
<div class="left">
<a ui-sref="login({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.submit}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{i18n.passwordHint}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-envelope fa-lg fa-fw"></i>
<label for="email" class="sr-only">{{i18n.emailAddress}}</label>
<input id="email" type="text" name="Email" placeholder="{{i18n.emailAddress}}" ng-model="model.email">
</div>
</div>
<div class="list-section-footer">
{{i18n.enterEmailToGetHint}}
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,34 +0,0 @@
<form name="theForm" ng-submit="login(model)" bit-form="loginPromise">
<div class="header">
<div class="left">
<a ui-sref="home({animation: 'out-slide-down'})">{{i18n.cancel}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.login}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{i18n.appName}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-envelope fa-lg fa-fw"></i>
<label for="email" class="sr-only">{{i18n.emailAddress}}</label>
<input id="email" type="text" name="Email" placeholder="{{i18n.emailAddress}}" ng-model="model.email">
</div>
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">{{i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword" placeholder="{{i18n.masterPass}}"
ng-model="model.masterPassword">
</div>
</div>
</div>
</div>
<p class="text-center text-accent">
<a ui-sref="hint({animation: 'in-slide-left'})">{{i18n.getMasterPasswordHint}}</a>
</p>
</div>
</form>

View File

@@ -1,170 +0,0 @@
<form name="theForm" ng-submit="login(token)" bit-form="loginPromise"
ng-if="providerType === constantsProvider.authenticator || providerType === constantsProvider.email">
<div class="header">
<div class="left">
<a href="#" ng-click="back()"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.continue}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{i18n.verificationCode}}</div>
</div>
<div class="content">
<div class="two-factor-key-page">
<p ng-if="providerType === constantsProvider.authenticator">
{{i18n.enterVerificationCodeApp}}
</p>
<p ng-if="providerType === constantsProvider.email">
{{i18n.enterVerificationCodeEmail}} <b>{{twoFactorEmail}}</b>.
</p>
</div>
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="code" class="sr-only">{{i18n.verificationCode}}</label>
<input id="code" type="text" name="Code" placeholder="{{i18n.verificationCode}}" ng-model="token"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="remember">{{i18n.rememberMe}}</label>
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
</div>
</div>
</div>
</div>
<p class="text-center text-accent" ng-if="providerType === constantsProvider.email">
<a href="#" stop-click ng-click="sendEmail(true)">{{i18n.sendVerificationCodeEmailAgain}}</a>
</p>
<p class="text-center text-accent">
<a href="#" stop-click ng-click="anotherMethod()">{{i18n.useAnotherTwoStepMethod}}</a>
</p>
</div>
</form>
<form name="theForm" bit-form="loginPromise" ng-if="providerType === constantsProvider.duo ||
providerType === constantsProvider.organizationDuo" autocomplete="off">
<div class="header">
<div class="left">
<a ui-sref="login({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="title">
Duo
<span ng-if="providerType === constantsProvider.organizationDuo">({{i18n.organization}})</span>
</div>
</div>
<div class="content">
<div id="duoFrameWrapper" ng-if="!showNewWindowMessage">
<iframe id="duo_iframe"></iframe>
</div>
<div ng-if="showNewWindowMessage" class="two-factor-key-page">
<p>{{i18n.twoStepNewWindowMessage}}</p>
</div>
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="remember">{{i18n.rememberMe}}</label>
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
</div>
</div>
</div>
</div>
<p class="text-accent text-center">
<a href="#" stop-click ng-click="anotherMethod()">{{i18n.useAnotherTwoStepMethod}}</a>
</p>
</div>
</form>
<form name="theForm" ng-submit="login(token)" bit-form="loginPromise" ng-if="providerType === constantsProvider.yubikey"
autocomplete="off">
<div class="header">
<div class="left">
<a ui-sref="login({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.continue}}</button>
<i class="fa fa-spinner fa-lg fa-spin" ng-show="theForm.$loading"></i>
</div>
<div class="title">YubiKey</div>
</div>
<div class="content">
<div class="two-factor-key-page">
<p>{{i18n.insertYubiKey}}</p>
<img src="../../../images/yubikey.jpg" alt="" class="img-rounded img-responsive" />
</div>
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="code" class="sr-only">{{i18n.verificationCode}}</label>
<input id="code" type="password" name="Code" ng-model="token">
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="remember">{{i18n.rememberMe}}</label>
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
</div>
</div>
</div>
</div>
<p class="text-center text-accent">
<a href="#" stop-click ng-click="anotherMethod()">{{i18n.useAnotherTwoStepMethod}}</a>
</p>
</div>
</form>
<form name="theForm" bit-form="loginPromise" ng-if="providerType === constantsProvider.u2f" autocomplete="off">
<div class="header">
<div class="left">
<a ui-sref="login({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="right">
<i class="fa fa-spinner fa-lg fa-spin" ng-show="theForm.$loading"></i>
</div>
<div class="title">FIDO U2F</div>
</div>
<div class="content">
<div class="two-factor-key-page">
<iframe id="u2f_iframe" class="hide"></iframe>
<p ng-if="!u2fReady">Loading...</p>
<div ng-if="u2fReady">
<p>{{i18n.insertU2f}}</p>
<img src="../../../images/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
</div>
</div>
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="remember">{{i18n.rememberMe}}</label>
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
</div>
</div>
</div>
</div>
<p class="text-accent text-center">
<a href="#" stop-click ng-click="anotherMethod()">{{i18n.useAnotherTwoStepMethod}}</a>
</p>
</div>
</form>
<div ng-if="providerType === null">
<div class="header">
<div class="left">
<a ui-sref="login({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
</div>
<div class="title">{{i18n.loginUnavailable}}</div>
</div>
<div class="content">
<div class="two-factor-key-page">
<p>{{i18n.noTwoStepProviders}}</p>
<p>{{i18n.noTwoStepProviders2}}</p>
</div>
<p class="text-accent text-center">
<a href="#" stop-click ng-click="anotherMethod()">{{i18n.useAnotherTwoStepMethod}}</a>
</p>
</div>
</div>

View File

@@ -1,53 +0,0 @@
<form name="theForm" ng-submit="submit(model)" bit-form="submitPromise">
<div class="header">
<div class="left">
<a ui-sref="home({animation: 'out-slide-down'})">{{i18n.cancel}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.submit}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{i18n.createAccount}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-envelope fa-lg fa-fw"></i>
<label for="email" class="sr-only">{{i18n.emailAddress}}</label>
<input id="email" type="text" name="Email" placeholder="{{i18n.emailAddress}}" ng-model="model.email">
</div>
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">{{i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword"
placeholder="{{i18n.masterPass}}" ng-model="model.masterPassword">
</div>
</div>
<div class="list-section-footer">
{{i18n.masterPassDesc}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password-retype" class="sr-only">{{i18n.reTypeMasterPass}}</label>
<input id="master-password-retype" type="password" name="MasterPasswordRetype"
placeholder="{{i18n.reTypeMasterPass}}" ng-model="model.masterPasswordRetype">
</div>
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lightbulb-o fa-lg fa-fw"></i>
<label for="hint" class="sr-only">{{i18n.masterPassHint}}</label>
<input id="hint" type="text" name="Hint" placeholder="{{i18n.masterPassHint}}"
ng-model="model.hint">
</div>
</div>
<div class="list-section-footer">
{{i18n.masterPassHintDesc}}
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,25 +0,0 @@
<div class="header">
<div class="left">
<a href="#" ng-click="cancel()" stop-click>{{i18n.cancel}}</a>
</div>
<div class="title">{{i18n.twoStepOptions}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item wrap" href="#" stop-click ng-click="choose(provider)"
ng-repeat="provider in providers | orderBy: 'displayOrder'">
<span class="text">{{provider.name}}</span>
<span class="detail">{{provider.description}}</span>
</a>
<a class="list-section-item wrap" href="#" stop-click ng-click="recover()">
<span class="text">{{i18n.recoveryCodeTitle}}</span>
<span class="detail">
{{i18n.recoveryCodeDesc}}
</span>
</a>
</div>
</div>
</div>
</div>

View File

@@ -1 +0,0 @@
declare module '*.html';

View File

@@ -1,224 +0,0 @@
require('clipboard');
require('angular');
require('angular-animate');
const uiRouter = require('@uirouter/angularjs').default;
require('angular-toastr');
require('ngclipboard');
require('sweetalert');
require('angular-sweetalert');
require('angulartics');
require('angulartics-google-analytics');
require('ng-infinite-scroll');
require('../../scripts/duo.js');
require('../less/libs.less');
require('../less/popup.less');
import DirectivesModule from './directives/directives.module';
import ComponentsModule from './components/components.module';
import ToolsModule from './tools/tools.module';
import ServicesModule from './services/services.module';
import LockModule from './lock/lock.module';
import CurrentModule from './current/current.module';
import GlobalModule from './global/global.module';
import SettingsModule from './settings/settings.module';
import { BrowserApi } from '../../browser/browserApi';
window.BrowserApi = BrowserApi;
import { U2f } from '../../scripts/u2f';
window.U2f = U2f;
import { Analytics } from '../../../jslib/src/misc/analytics';
if (BrowserApi.getBackgroundPage()) {
new Analytics(window, () => BrowserApi.gaFilter(), null, null, null, () => {
const bgPage = BrowserApi.getBackgroundPage();
if (!bgPage || !bgPage.bitwardenMain) {
throw 'Cannot resolve background page main.';
}
return bgPage.bitwardenMain;
});
}
// Model imports
import { Attachment } from '../../../jslib/src/models/domain/attachment';
import { Card } from '../../../jslib/src/models/domain/card';
import { Cipher } from '../../../jslib/src/models/domain/cipher';
import { CipherString } from '../../../jslib/src/models/domain/cipherString';
import { Field } from '../../../jslib/src/models/domain/field';
import { Folder } from '../../../jslib/src/models/domain/folder';
import { Identity } from '../../../jslib/src/models/domain/identity';
import { Login } from '../../../jslib/src/models/domain/login';
import { SecureNote } from '../../../jslib/src/models/domain/secureNote';
window.Attachment = Attachment;
window.Card = Card;
window.Cipher = Cipher;
window.CipherString = CipherString;
window.Field = Field;
window.Folder = Folder;
window.Identity = Identity;
window.Login = Login;
window.SecureNote = SecureNote;
import { AttachmentData } from '../../../jslib/src/models/data/attachmentData';
import { CardData } from '../../../jslib/src/models/data/cardData';
import { CipherData } from '../../../jslib/src/models/data/cipherData';
import { CollectionData } from '../../../jslib/src/models/data/collectionData';
import { FieldData } from '../../../jslib/src/models/data/fieldData';
import { FolderData } from '../../../jslib/src/models/data/folderData';
import { IdentityData } from '../../../jslib/src/models/data/identityData';
import { LoginData } from '../../../jslib/src/models/data/loginData';
import { SecureNoteData } from '../../../jslib/src/models/data/secureNoteData';
window.AttachmentData = AttachmentData;
window.CardData = CardData;
window.CipherData = CipherData;
window.CollectionData = CollectionData;
window.FieldData = FieldData;
window.FolderData = FolderData;
window.IdentityData = IdentityData;
window.LoginData = LoginData;
window.SecureNoteData = SecureNoteData;
import { CipherRequest } from '../../../jslib/src/models/request/cipherRequest';
import { DeviceRequest } from '../../../jslib/src/models/request/deviceRequest';
import { DeviceTokenRequest } from '../../../jslib/src/models/request/deviceTokenRequest';
import { FolderRequest } from '../../../jslib/src/models/request/folderRequest';
import { PasswordHintRequest } from '../../../jslib/src/models/request/passwordHintRequest';
import { RegisterRequest } from '../../../jslib/src/models/request/registerRequest';
import { TokenRequest } from '../../../jslib/src/models/request/tokenRequest';
import { TwoFactorEmailRequest } from '../../../jslib/src/models/request/twoFactorEmailRequest';
window.CipherRequest = CipherRequest;
window.DeviceRequest = DeviceRequest;
window.DeviceTokenRequest = DeviceTokenRequest;
window.FolderRequest = FolderRequest;
window.PasswordHintRequest = PasswordHintRequest;
window.RegisterRequest = RegisterRequest;
window.TokenRequest = TokenRequest;
window.TwoFactorEmailRequest = TwoFactorEmailRequest;
import { AttachmentResponse } from '../../../jslib/src/models/response/attachmentResponse';
import { CipherResponse } from '../../../jslib/src/models/response/cipherResponse';
import { CollectionResponse } from '../../../jslib/src/models/response/collectionResponse';
import { DeviceResponse } from '../../../jslib/src/models/response/deviceResponse';
import { DomainsResponse } from '../../../jslib/src/models/response/domainsResponse';
import { ErrorResponse } from '../../../jslib/src/models/response/errorResponse';
import { FolderResponse } from '../../../jslib/src/models/response/folderResponse';
import { GlobalDomainResponse } from '../../../jslib/src/models/response/globalDomainResponse';
import { IdentityTokenResponse } from '../../../jslib/src/models/response/identityTokenResponse';
import { KeysResponse } from '../../../jslib/src/models/response/keysResponse';
import { ListResponse } from '../../../jslib/src/models/response/listResponse';
import { ProfileOrganizationResponse } from '../../../jslib/src/models/response/profileOrganizationResponse';
import { ProfileResponse } from '../../../jslib/src/models/response/profileResponse';
import { SyncResponse } from '../../../jslib/src/models/response/syncResponse';
window.AttachmentResponse = AttachmentResponse;
window.CipherResponse = CipherResponse;
window.CollectionResponse = CollectionResponse;
window.DeviceResponse = DeviceResponse;
window.DomainsResponse = DomainsResponse;
window.ErrorResponse = ErrorResponse;
window.FolderResponse = FolderResponse;
window.GlobalDomainResponse = GlobalDomainResponse;
window.IdentityTokenResponse = IdentityTokenResponse;
window.KeysResponse = KeysResponse;
window.ListResponse = ListResponse;
window.ProfileOrganizationResponse = ProfileOrganizationResponse;
window.ProfileResponse = ProfileResponse;
window.SyncResponse = SyncResponse;
angular
.module('bit', [
uiRouter,
'ngAnimate',
'toastr',
'angulartics',
'angulartics.google.analytics',
DirectivesModule,
ComponentsModule,
ServicesModule,
GlobalModule,
'bit.accounts',
CurrentModule,
'bit.vault',
SettingsModule,
ToolsModule,
LockModule
]);
require('./config');
require('./accounts/accountsModule.js');
require('./accounts/accountsLoginController.js');
require('./accounts/accountsLoginTwoFactorController.js');
require('./accounts/accountsTwoFactorMethodsController.js');
require('./accounts/accountsHintController.js');
require('./accounts/accountsRegisterController.js');
require('./vault/vaultModule.js');
require('./vault/vaultController.js');
require('./vault/vaultViewGroupingController.js');
require('./vault/vaultAddCipherController.js');
require('./vault/vaultEditCipherController.js');
require('./vault/vaultViewCipherController.js');
require('./vault/vaultAttachmentsController.js');
// $$ngIsClass fix issue with "class constructors must be invoked with |new|" on Firefox ESR
// ref: https://github.com/angular/angular.js/issues/14240
import { ActionButtonsController } from './components/action-buttons.component';
ActionButtonsController.$$ngIsClass = true;
import { CipherItemsController } from './components/cipher-items.component';
CipherItemsController.$$ngIsClass = true;
import { IconController } from './components/icon.component';
IconController.$$ngIsClass = true;
import { PopOutController } from './components/pop-out.component';
PopOutController.$$ngIsClass = true;
import { CurrentController } from './current/current.component';
CurrentController.$$ngIsClass = true;
import { LockController } from './lock/lock.component';
LockController.$$ngIsClass = true;
import { ExportController } from './tools/export.component';
ExportController.$$ngIsClass = true;
import { PasswordGeneratorController } from './tools/password-generator.component';
PasswordGeneratorController.$$ngIsClass = true;
import { PasswordGeneratorHistoryController } from './tools/password-generator-history.component';
PasswordGeneratorHistoryController.$$ngIsClass = true;
import { ToolsController } from './tools/tools.component';
ToolsController.$$ngIsClass = true;
import { AddFolderController } from './settings/folders/add-folder.component';
AddFolderController.$$ngIsClass = true;
import { EditFolderController } from './settings/folders/edit-folder.component';
EditFolderController.$$ngIsClass = true;
import { FoldersController } from './settings/folders/folders.component';
FoldersController.$$ngIsClass = true;
import { AboutController } from './settings/about.component';
AboutController.$$ngIsClass = true;
import { CreditsController } from './settings/credits.component';
CreditsController.$$ngIsClass = true;
import { EnvironmentController } from './settings/environment.component';
EnvironmentController.$$ngIsClass = true;
import { OptionsController } from './settings/options.component';
OptionsController.$$ngIsClass = true;
import { HelpController } from './settings/help.component';
HelpController.$$ngIsClass = true;
import { PremiumController } from './settings/premium.component';
PremiumController.$$ngIsClass = true;
import { SettingsController } from './settings/settings.component';
SettingsController.$$ngIsClass = true;
import { SyncController } from './settings/sync.component';
SyncController.$$ngIsClass = true;
import { BaseController } from './global/base.controller';
BaseController.$$ngIsClass = true;
import { MainController } from './global/main.controller';
MainController.$$ngIsClass = true;
import { PrivateModeController } from './global/private-mode.controller';
PrivateModeController.$$ngIsClass = true;
import { TabsController } from './global/tabs.controller';
TabsController.$$ngIsClass = true;
// Bootstrap the angular application
angular.element(function () {
angular.bootstrap(document, ['bit']);
});

View File

@@ -1,60 +0,0 @@
<div class="action-buttons">
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.login">
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.launchWebsite}}" ng-click="$ctrl.launch()"
ng-if="!$ctrl.showView" ng-class="{disabled: !$ctrl.cipher.login.uri}">
<i class="fa fa-lg fa-share-square-o"></i>
</span>
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{::$ctrl.i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.copyUsername}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.username, 'Username')"
data-clipboard-text="{{$ctrl.cipher.login.username}}" ng-class="{disabled: !$ctrl.cipher.login.username}">
<i class="fa fa-lg fa-user"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.copyPassword}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.password, 'Password')"
data-clipboard-text="{{$ctrl.cipher.login.password}}" ng-class="{disabled: !$ctrl.cipher.login.password}">
<i class="fa fa-lg fa-key"></i>
</span>
</div>
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.card">
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{::$ctrl.i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.copyNumber}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.number, 'Card Number')"
data-clipboard-text="{{$ctrl.cipher.card.number}}" ng-class="{disabled: !$ctrl.cipher.card.number}">
<i class="fa fa-lg fa-hashtag"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.copySecurityCode}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.securityCode, 'Security Code')"
data-clipboard-text="{{$ctrl.cipher.card.code}}" ng-class="{disabled: !$ctrl.cipher.card.code}">
<i class="fa fa-lg fa-key"></i>
</span>
</div>
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.identity">
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{::$ctrl.i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
</div>
<div ng-if="$ctrl.cipher.type === $ctrl.constants.cipherType.secureNote">
<span class="btn-list" ng-click="$ctrl.onView($ctrl.cipher)" stop-prop stop-click title="{{::$ctrl.i18n.view}}"
ng-if="$ctrl.showView">
<i class="fa fa-lg fa-eye"></i>
</span>
<span class="btn-list" stop-prop stop-click title="{{::$ctrl.i18n.copyNote}}" ngclipboard
ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.note, 'Note')"
data-clipboard-text="{{$ctrl.cipher.notes}}" ng-class="{disabled: !$ctrl.cipher.notes}">
<i class="fa fa-lg fa-clipboard"></i>
</span>
</div>
</div>

View File

@@ -1,57 +0,0 @@
import * as template from './action-buttons.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { ConstantsService } from 'jslib/services/constants.service';
import { PopupUtilsService } from '../services/popupUtils.service';
export class ActionButtonsController implements ng.IController {
onView: Function;
cipher: any;
showView: boolean;
i18n: any;
constants: ConstantsService;
constructor(private i18nService: any, private $analytics: any, private constantsService: ConstantsService,
private toastr: any, private $timeout: ng.ITimeoutService, private $window: ng.IWindowService) {
this.i18n = i18nService;
this.constants = constantsService;
}
launch() {
const self = this;
this.$timeout(() => {
if (self.cipher.login.uri.startsWith('http://') || self.cipher.login.uri.startsWith('https://')) {
self.$analytics.eventTrack('Launched Website From Listing');
BrowserApi.createNewTab(self.cipher.login.uri);
if (PopupUtilsService.inPopup(self.$window)) {
BrowserApi.closePopup(self.$window);
}
}
});
}
clipboardError(e: any) {
this.toastr.info(this.i18nService.browserNotSupportClipboard);
}
clipboardSuccess(e: any, type: string, aType: string) {
e.clearSelection();
this.$analytics.eventTrack('Copied ' + aType);
this.toastr.info(type + this.i18nService.valueCopied);
}
}
ActionButtonsController.$inject = ['i18nService', '$analytics', 'constantsService', 'toastr', '$timeout', '$window'];
export const ActionButtonsComponent = {
bindings: {
cipher: '<',
showView: '<',
onView: '&',
},
controller: ActionButtonsController,
template: template,
};

View File

@@ -1,11 +0,0 @@
<a href="#" stop-click ng-click="$ctrl.select(cipher)" class="list-section-item condensed"
title="{{::$ctrl.selectionTitle}}" ng-repeat="cipher in $ctrl.ciphers track by $index">
<action-buttons cipher="cipher" show-view="true" on-view="$ctrl.view(cipher)"></action-buttons>
<icon cipher="cipher"></icon>
<span class="text">
{{cipher.name}}
<i class="fa fa-share-alt text-muted" ng-if="::cipher.organizationId" title="{{::$ctrl.i18n.shared}}"></i>
<i class="fa fa-paperclip text-muted" ng-if="cipher.attachments" title="{{::$ctrl.i18n.attachments}}"></i>
</span>
<span class="detail">{{cipher.subTitle}}</span>
</a>

View File

@@ -1,33 +0,0 @@
import * as template from './cipher-items.component.html';
export class CipherItemsController implements ng.IController {
onSelected: Function;
onView: Function;
i18n: any;
constructor(private i18nService: any) {
this.i18n = i18nService;
}
view(cipher: any) {
return this.onView({ cipher: cipher });
}
select(cipher: any) {
return this.onSelected({ cipher: cipher });
}
}
CipherItemsController.$inject = ['i18nService'];
export const CipherItemsComponent = {
bindings: {
ciphers: '<',
selectionTitle: '<',
onSelected: '&',
onView: '&',
},
controller: CipherItemsController,
template: template,
};

View File

@@ -1,13 +0,0 @@
import * as angular from 'angular';
import { ActionButtonsComponent } from './action-buttons.component';
import { CipherItemsComponent } from './cipher-items.component';
import { IconComponent } from './icon.component';
import { PopOutComponent } from './pop-out.component';
export default angular
.module('bit.components', [])
.component('cipherItems', CipherItemsComponent)
.component('icon', IconComponent)
.component('actionButtons', ActionButtonsComponent)
.component('popOut', PopOutComponent)
.name;

View File

@@ -1,4 +0,0 @@
<div class="icon">
<img ng-src="{{$ctrl.image}}" fallback-src="{{$ctrl.fallbackImage}}" ng-if="$ctrl.imageEnabled && $ctrl.image" alt="" />
<i class="fa fa-fw fa-lg {{$ctrl.icon}}" ng-if="!$ctrl.imageEnabled || !$ctrl.image"></i>
</div>

View File

@@ -1,90 +0,0 @@
import * as template from './icon.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { CipherType } from 'jslib/enums/cipherType';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
export class IconController implements ng.IController {
cipher: any;
icon: string;
image: string;
fallbackImage: string;
imageEnabled: boolean;
private iconsUrl: string;
constructor(private stateService: any, private environmentService: EnvironmentService) {
this.imageEnabled = stateService.getState('faviconEnabled');
this.iconsUrl = environmentService.iconsUrl;
if (!this.iconsUrl) {
if (environmentService.baseUrl) {
this.iconsUrl = environmentService.baseUrl + '/icons';
} else {
this.iconsUrl = 'https://icons.bitwarden.com';
}
}
}
$onChanges() {
switch (this.cipher.type) {
case CipherType.Login:
this.icon = 'fa-globe';
this.setLoginIcon();
break;
case CipherType.SecureNote:
this.icon = 'fa-sticky-note-o';
break;
case CipherType.Card:
this.icon = 'fa-credit-card';
break;
case CipherType.Identity:
this.icon = 'fa-id-card-o';
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 = 'fa-android';
this.image = null;
} else if (hostnameUri.indexOf('iosapp://') === 0) {
this.icon = 'fa-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 {
const url = new URL(hostnameUri);
this.image = this.iconsUrl + '/' + url.hostname + '/icon.png';
this.fallbackImage = BrowserApi.getAssetUrl('images/fa-globe.png');
} catch (e) { }
}
} else {
this.image = null;
}
}
}
IconController.$inject = ['stateService', 'environmentService'];
export const IconComponent = {
bindings: {
cipher: '<',
},
controller: IconController,
template: template,
};

View File

@@ -1,3 +0,0 @@
<a href="" ng-click="$ctrl.expand()" title="{{::$ctrl.i18n.popOutNewWindow}}">
<i class="fa fa-external-link fa-rotate-270 fa-lg"></i>
</a>

View File

@@ -1,282 +0,0 @@
angular
.module('bit')
.config(function ($stateProvider, $urlRouterProvider, $compileProvider, $sceDelegateProvider, toastrConfig) {
$compileProvider.imgSrcSanitizationWhitelist(
/^\s*((https?|ftp|file|blob):|data:image\/|(moz|chrome|ms-browser)-extension)/);
angular.extend(toastrConfig, {
closeButton: true,
progressBar: true,
showMethod: 'slideDown',
positionClass: 'toast-bottom-center'
});
$urlRouterProvider.otherwise(function ($injector, $location) {
var $state = $injector.get('$state');
if (!BrowserApi.getBackgroundPage()) {
$state.go('privateMode');
return;
}
var userService = $injector.get('userService');
var cryptoService = $injector.get('cryptoService');
var key;
cryptoService.getKey().then(function (theKey) {
key = theKey;
return userService.isAuthenticated();
}).then(function (isAuthenticated) {
if (isAuthenticated) {
if (!key) {
$state.go('lock');
}
else {
$state.go('tabs.current');
}
}
else {
$state.go('home');
}
});
});
$stateProvider
.state('splash', {
url: '/splash',
controller: 'baseController',
template: require('./global/splash.html'),
data: { authorize: false },
params: { animation: null }
})
.state('privateMode', {
url: '/private-mode',
controller: 'privateModeController',
template: require('./global/private-mode.html'),
data: { authorize: false },
params: { animation: null }
})
.state('home', {
url: '/home',
controller: 'baseController',
template: require('./global/home.html'),
data: { authorize: false },
params: { animation: null }
})
.state('login', {
url: '/login',
controller: 'accountsLoginController',
template: require('./accounts/views/accountsLogin.html'),
data: { authorize: false },
params: { animation: null, email: null }
})
.state('hint', {
url: '/hint',
controller: 'accountsHintController',
template: require('./accounts/views/accountsHint.html'),
data: { authorize: false },
params: { animation: null }
})
.state('twoFactor', {
url: '/two-factor',
controller: 'accountsLoginTwoFactorController',
template: require('./accounts/views/accountsLoginTwoFactor.html'),
data: { authorize: false },
params: { animation: null, provider: null }
})
.state('twoFactorMethods', {
url: '/two-factor-methods',
controller: 'accountsTwoFactorMethodsController',
template: require('./accounts/views/accountsTwoFactorMethods.html'),
data: { authorize: false },
params: { animation: null, provider: null }
})
.state('register', {
url: '/register',
controller: 'accountsRegisterController',
template: require('./accounts/views/accountsRegister.html'),
data: { authorize: false },
params: { animation: null }
})
.state('tabs', {
url: '/tab',
abstract: true,
template: require('./global/tabs.html'),
data: { authorize: true },
params: { animation: null }
})
.state('tabs.current', {
url: '/current',
component: 'current'
})
.state('tabs.vault', {
url: '/vault',
template: require('./vault/views/vault.html'),
controller: 'vaultController',
params: { syncOnLoad: false, searchText: null }
})
.state('tabs.settings', {
url: '/settings',
component: 'settings',
})
.state('tabs.tools', {
url: '/tools',
component: 'tools'
})
.state('viewGrouping', {
url: '/view-grouping?folderId&collectionId',
template: require('./vault/views/vaultViewGrouping.html'),
controller: 'vaultViewGroupingController',
data: { authorize: true },
params: { animation: null, from: 'vault' }
})
.state('viewCipher', {
url: '/view-cipher?cipherId',
template: require('./vault/views/vaultViewCipher.html'),
controller: 'vaultViewCipherController',
data: { authorize: true },
params: { animation: null, from: 'vault' }
})
.state('addCipher', {
url: '/add-cipher',
template: require('./vault/views/vaultAddCipher.html'),
controller: 'vaultAddCipherController',
data: { authorize: true },
params: { animation: null, name: null, uri: null, folderId: null, cipher: null, from: 'vault' }
})
.state('editCipher', {
url: '/edit-cipher?cipherId',
template: require('./vault/views/vaultEditCipher.html'),
controller: 'vaultEditCipherController',
data: { authorize: true },
params: { animation: null, fromView: true, cipher: null, from: 'vault' }
})
.state('attachments', {
url: '/attachments?id',
template: require('./vault/views/vaultAttachments.html'),
controller: 'vaultAttachmentsController',
data: { authorize: true },
params: { animation: null, fromView: true, from: 'vault' }
})
.state('passwordGenerator', {
url: '/password-generator',
component: 'passwordGenerator',
data: { authorize: true },
params: { animation: null, addState: null, editState: null }
})
.state('passwordGeneratorHistory', {
url: '/password-generator-history',
component: 'passwordGeneratorHistory',
data: { authorize: true },
params: { animation: null, addState: null, editState: null }
})
.state('export', {
url: '/export',
component: 'export',
data: { authorize: true },
params: { animation: null }
})
.state('about', {
url: '/about',
component: 'about',
data: { authorize: true },
params: { animation: null }
})
.state('credits', {
url: '/credits',
component: 'credits',
data: { authorize: true },
params: { animation: null }
})
.state('options', {
url: '/options',
component: 'options',
data: { authorize: true },
params: { animation: null }
})
.state('help', {
url: '/help',
component: 'help',
data: { authorize: true },
params: { animation: null }
})
.state('sync', {
url: '/sync',
component: 'sync',
data: { authorize: true },
params: { animation: null }
})
.state('premium', {
url: '/premium',
component: 'premium',
data: { authorize: true },
params: { animation: null }
})
.state('folders', {
url: '/folders',
abstract: true,
data: { authorize: true },
params: { animation: null }
})
.state('folders.list', {
url: '',
component: 'folders',
})
.state('folders.add', {
url: '/add',
component: 'addFolder',
})
.state('folders.edit', {
url: '/{folderId}/edit',
component: 'editFolder',
})
.state('environment', {
url: '/environment',
component: 'environment',
data: { authorize: false },
params: { animation: null }
})
.state('lock', {
url: '/lock',
component: 'lock',
data: { authorize: true },
params: { animation: null }
});
})
.run(function ($trace, $transitions, userService, $state, constantsService, stateService,
storageService, messagingService) {
stateService.init();
$transitions.onStart({}, function (trans) {
const $state = trans.router.stateService;
const toState = trans.to();
if ($state.current.name.indexOf('tabs.') > -1 && toState.name.indexOf('tabs.') > -1) {
stateService.removeState('vault');
stateService.removeState('viewGrouping');
}
const userService = trans.injector().get('userService');
if (!userService) {
return;
}
userService.isAuthenticated().then((isAuthenticated) => {
if (isAuthenticated) {
storageService.save(constantsService.lastActiveKey, (new Date()).getTime());
}
else if (toState.data && toState.data.authorize) {
event.preventDefault();
messagingService.send('logout');
}
});
});
});

View File

@@ -1,63 +0,0 @@
<div class="header header-search">
<pop-out ng-if="$ctrl.showPopout" class="left"></pop-out>
<div class="left" ng-if="$ctrl.inSidebar">
<a href="" ng-click="$ctrl.refresh()" title="{{::$ctrl.i18n.refresh}}"><i class="fa fa-refresh fa-lg"></i></a>
</div>
<div class="search" ng-style="{'visibility': $ctrl.disableSearch ? 'hidden' : 'visible'}">
<input type="search" placeholder="{{$ctrl.i18n.searchVault}}" id="search" ng-model="$ctrl.searchText" ng-change="$ctrl.searchVault()" />
<i class="fa fa-search"></i>
</div>
<div class="right">
<a href="" ng-click="$ctrl.addCipher()" title="{{::$ctrl.i18n.addItem}}"><i class="fa fa-plus fa-lg"></i></a>
</div>
</div>
<div class="content content-tabs">
<div class="list" ng-if="$ctrl.loaded">
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.typeLogins}}
</div>
<div class="list-section-items" ng-class="{'list-no-selection': !$ctrl.loginCiphers.length}">
<cipher-items ng-if="$ctrl.loginCiphers.length"
ciphers="$ctrl.loginCiphers"
on-view="$ctrl.viewCipher(cipher)"
on-selected="$ctrl.fillCipher(cipher)"
selection-title="$ctrl.i18n.autoFill">
</cipher-items>
<div class="list-section-item" ng-if="!$ctrl.loginCiphers.length">
<p>{{$ctrl.i18n.autoFillInfo}}</p>
<button ng-click="$ctrl.addCipher()" class="btn btn-link btn-block">{{$ctrl.i18n.addLogin}}</button>
</div>
</div>
</div>
<div class="list-section" ng-if="$ctrl.cardCiphers.length">
<div class="list-section-header">
{{$ctrl.i18n.cards}}
</div>
<div class="list-section-items">
<cipher-items ng-if="$ctrl.cardCiphers.length"
ciphers="$ctrl.cardCiphers"
on-view="$ctrl.viewCipher(cipher)"
on-selected="$ctrl.fillCipher(cipher)"
selection-title="$ctrl.i18n.autoFill">
</cipher-items>
</div>
</div>
<div class="list-section" ng-if="$ctrl.identityCiphers.length">
<div class="list-section-header">
{{$ctrl.i18n.identities}}
</div>
<div class="list-section-items">
<cipher-items ng-if="$ctrl.identityCiphers.length"
ciphers="$ctrl.identityCiphers"
on-view="$ctrl.viewCipher(cipher)"
on-selected="$ctrl.fillCipher(cipher)"
selection-title="$ctrl.i18n.autoFill">
</cipher-items>
</div>
</div>
</div>
<div class="page-loading" ng-if="!$ctrl.loaded">
<i class="fa fa-lg fa-spinner fa-spin"></i>
</div>
</div>

View File

@@ -1,185 +0,0 @@
import * as template from './current.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { CipherType } from 'jslib/enums/cipherType';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { UtilsService } from 'jslib/abstractions/utils.service';
import { AutofillService } from '../../../services/abstractions/autofill.service';
import { PopupUtilsService } from '../services/popupUtils.service';
export class CurrentController {
i18n: any;
pageDetails: any = [];
loaded: boolean = false;
cardCiphers: any = [];
identityCiphers: any = [];
loginCiphers: any = [];
url: string;
domain: string;
canAutofill: boolean = false;
searchText: string = null;
inSidebar: boolean = false;
showPopout: boolean = true;
disableSearch: boolean = false;
constructor($scope: any, private cipherService: CipherService, private platformUtilsService: PlatformUtilsService,
private utilsService: UtilsService, private toastr: any, private $window: ng.IWindowService,
private $state: any, private $timeout: ng.ITimeoutService, private autofillService: AutofillService,
private $analytics: any, private i18nService: any, private $filter: ng.IFilterService) {
this.i18n = i18nService;
this.inSidebar = PopupUtilsService.inSidebar($window);
this.showPopout = !this.inSidebar && !platformUtilsService.isSafari();
this.disableSearch = platformUtilsService.isEdge();
$scope.$on('syncCompleted', (event: any, successfully: boolean) => {
if (this.loaded) {
$timeout(this.loadVault.bind(this), 500);
}
});
$scope.$on('collectPageDetailsResponse', (event: any, details: any) => {
this.pageDetails.push(details);
});
}
$onInit() {
this.$timeout(() => {
document.getElementById('search').focus();
}, 50);
this.loadVault();
}
async refresh() {
await this.loadVault();
}
addCipher() {
this.$state.go('addCipher', {
animation: 'in-slide-up',
name: this.domain,
uri: this.url,
from: 'current',
});
}
viewCipher(cipher: any) {
this.$state.go('viewCipher', {
cipherId: cipher.id,
animation: 'in-slide-up',
from: 'current',
});
}
fillCipher(cipher: any) {
if (!this.canAutofill) {
this.$analytics.eventTrack('Autofilled Error');
this.toastr.error(this.i18nService.autofillError);
}
this.autofillService.doAutoFill({
cipher: cipher,
pageDetails: this.pageDetails,
fromBackground: false,
doc: this.$window.document,
}).then((totpCode: string) => {
this.$analytics.eventTrack('Autofilled');
if (totpCode && this.platformUtilsService.isFirefox()) {
this.utilsService.copyToClipboard(totpCode, document);
}
if (PopupUtilsService.inPopup(this.$window)) {
BrowserApi.closePopup(this.$window);
}
}).catch(() => {
this.$analytics.eventTrack('Autofilled Error');
this.toastr.error(this.i18nService.autofillError);
});
}
searchVault() {
this.$state.go('tabs.vault', {
searchText: this.searchText,
});
}
private async loadVault() {
const tab = await BrowserApi.getTabFromCurrentWindow();
if (tab) {
this.url = tab.url;
} else {
this.$timeout(() => {
this.loaded = true;
});
return;
}
this.domain = this.platformUtilsService.getDomain(this.url);
BrowserApi.tabSendMessage(tab, {
command: 'collectPageDetails',
tab: tab,
sender: 'currentController',
}).then(() => {
this.canAutofill = true;
});
const otherTypes = [
CipherType.Card,
CipherType.Identity,
];
const ciphers = await this.cipherService.getAllDecryptedForUrl(this.url, otherTypes);
const loginCiphers: any = [];
const cardCiphers: any = [];
const identityCiphers: any = [];
const sortedCiphers = this.$filter('orderBy')(ciphers,
[this.sortUriMatch, this.sortLastUsed, 'name', 'subTitle']);
sortedCiphers.forEach((cipher: any) => {
switch (cipher.type) {
case CipherType.Login:
loginCiphers.push(cipher);
break;
case CipherType.Card:
cardCiphers.push(cipher);
break;
case CipherType.Identity:
identityCiphers.push(cipher);
break;
default:
break;
}
});
this.$timeout(() => {
this.loginCiphers = loginCiphers;
this.cardCiphers = cardCiphers;
this.identityCiphers = identityCiphers;
this.loaded = true;
});
}
private sortUriMatch(cipher: any) {
// exact matches should sort earlier.
return cipher.login && cipher.login.uri && this.url && this.url.startsWith(cipher.login.uri) ? 0 : 1;
}
private sortLastUsed(cipher: any) {
return cipher.localData && cipher.localData.lastUsedDate ? -1 * cipher.localData.lastUsedDate : 0;
}
}
CurrentController.$inject = ['$scope', 'cipherService', 'platformUtilsService', 'utilsService', 'toastr', '$window',
'$state', '$timeout', 'autofillService', '$analytics', 'i18nService', '$filter'];
export const CurrentComponent = {
bindings: {},
controller: CurrentController,
template: template,
};

View File

@@ -1,9 +0,0 @@
import * as angular from 'angular';
import { CurrentComponent } from './current.component';
export default angular
.module('bit.current', ['toastr', 'ngclipboard'])
.component('current', CurrentComponent)
.name;

View File

@@ -1,30 +0,0 @@
import { ValidationService } from '../services/validation.service';
export function BitFormDirective($rootScope: ng.IRootScopeService, validationService: ValidationService) {
return {
require: 'form',
restrict: 'A',
link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, formCtrl: ng.IFormController) => {
const watchPromise = attrs.bitForm || null;
if (watchPromise) {
scope.$watch(watchPromise, formSubmitted.bind(null, formCtrl, scope));
}
},
};
function formSubmitted(form: any, scope: ng.IScope, promise: any) {
if (!promise || !promise.then) {
return;
}
// start loading
form.$loading = true;
promise.then((response: any) => {
form.$loading = false;
}, (reason: any) => {
form.$loading = false;
validationService.showError(reason);
});
}
}

View File

@@ -1,16 +0,0 @@
import * as angular from 'angular';
import { BitFormDirective } from './bit-form.directive';
import { FallbackSrcDirective } from './fallback-src.directive';
import { StopClickDirective } from './stop-click.directive';
import { StopPropDirective } from './stop-prop.directive';
export default angular
.module('bit.directives', [])
.directive('fallbackSrc', FallbackSrcDirective)
.directive('stopClick', StopClickDirective)
.directive('stopProp', StopPropDirective)
.directive('bitForm', BitFormDirective)
.name;

View File

@@ -1,7 +0,0 @@
export function FallbackSrcDirective() {
return (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
element[0].addEventListener('error', (e: any) => {
e.target.src = attrs.fallbackSrc;
});
};
}

View File

@@ -1,8 +0,0 @@
export function StopClickDirective() {
// ref: https://stackoverflow.com/a/14165848/1090359
return (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
element[0].addEventListener('click', (e) => {
e.preventDefault();
});
};
}

View File

@@ -1,7 +0,0 @@
export function StopPropDirective() {
return (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
element[0].addEventListener('click', (e) => {
e.stopPropagation();
});
};
}

View File

@@ -1,7 +0,0 @@
export class BaseController implements ng.IController {
constructor($scope: any, i18nService: any) {
$scope.i18n = i18nService;
}
}
BaseController.$inject = ['$scope', 'i18nService'];

View File

@@ -1 +0,0 @@
<div ui-view></div>

View File

@@ -1,15 +0,0 @@
import * as angular from 'angular';
import { BaseController } from './base.controller';
import { MainController } from './main.controller';
import { PrivateModeController } from './private-mode.controller';
import { TabsController } from './tabs.controller';
export default angular
.module('bit.global', ['ngAnimate'])
.controller('mainController', MainController)
.controller('baseController', BaseController)
.controller('tabsController', TabsController)
.controller('privateModeController', PrivateModeController)
.name;

View File

@@ -1,17 +0,0 @@
<div class="home-page">
<a ui-sref="environment({animation: 'in-slide-up'})" class="settings-icon">
<i class="fa fa-cog fa-lg"></i><span>&nbsp;{{i18n.settings}}</span>
</a>
<img src="../../images/logo@2x.png" alt="bitwarden" />
<p>{{i18n.loginOrCreateNewAccount}}</p>
<div class="bottom-buttons">
<a class="btn btn-lg btn-primary btn-block" ui-sref="register({animation: 'in-slide-up'})"
analytics-on="click" analytics-event="Clicked Create Account">
<b>{{i18n.createAccount}}</b>
</a>
<a class="btn btn-lg btn-link btn-block" ui-sref="login({animation: 'in-slide-up'})"
analytics-on="click" analytics-event="Clicked Log In">
{{i18n.login}}
</a>
</div>
</div>

View File

@@ -1,61 +0,0 @@
import { BrowserApi } from '../../../browser/browserApi';
import { AuthService } from 'jslib/abstractions/auth.service';
import { UtilsService } from 'jslib/abstractions/utils.service';
export class MainController implements ng.IController {
smBody: boolean;
xsBody: boolean;
animation: string;
constructor($scope: any, $transitions: any, $state: any, authService: AuthService, toastr: any,
i18nService: any, $analytics: any, utilsService: UtilsService, $window: ng.IWindowService) {
this.animation = '';
this.xsBody = $window.screen.availHeight < 600;
this.smBody = !this.xsBody && $window.screen.availHeight <= 800;
$transitions.onSuccess({}, (transition: any) => {
const toParams = transition.params('to');
if (toParams.animation) {
this.animation = toParams.animation;
} else {
this.animation = '';
}
});
$window.bitwardenPopupMainMessageListener = (msg: any, sender: any, sendResponse: any) => {
if (msg.command === 'syncCompleted') {
$scope.$broadcast('syncCompleted', msg.successfully);
} else if (msg.command === 'syncStarted') {
$scope.$broadcast('syncStarted');
} else if (msg.command === 'doneLoggingOut') {
authService.logOut(() => {
$analytics.eventTrack('Logged Out');
if (msg.expired) {
toastr.warning(i18nService.loginExpired, i18nService.loggedOut);
}
$state.go('home');
});
} else if (msg.command === 'collectPageDetailsResponse' &&
msg.sender === 'currentController') {
$scope.$broadcast('collectPageDetailsResponse', {
frameId: sender.frameId,
tab: msg.tab,
details: msg.details,
});
} else if (msg.command === '2faPageResponse') {
$scope.$broadcast('2faPageResponse', {
type: msg.type,
data: msg.data,
tab: sender.tab,
});
}
};
BrowserApi.messageListener($window.bitwardenPopupMainMessageListener);
}
}
MainController.$inject = ['$scope', '$transitions', '$state', 'authService', 'toastr', 'i18nService', '$analytics',
'utilsService', '$window'];

View File

@@ -1,13 +0,0 @@
import { BrowserApi } from '../../../browser/browserApi';
export class PrivateModeController implements ng.IController {
constructor($scope: any) {
$scope.privateModeMessage = chrome.i18n.getMessage('privateModeMessage');
$scope.learnMoreMessage = chrome.i18n.getMessage('learnMore');
$scope.learnMore = () => {
BrowserApi.createNewTab('https://help.bitwarden.com/article/extension-wont-load-in-private-mode/');
};
}
}
PrivateModeController.$inject = ['$scope'];

View File

@@ -1,6 +0,0 @@
<div class="content text-center">
<p>{{privateModeMessage}}</p>
<button type="button" class="btn btn-lg btn-link btn-block" ng-click="learnMore()">
{{learnMoreMessage}}
</button>
</div>

View File

@@ -1,5 +0,0 @@
<div class="content">
<div class="splash-page">
<img src="../../images/logo@3x.png" alt="bitwarden" />
</div>
</div>

View File

@@ -1,8 +0,0 @@
export class TabsController implements ng.IController {
constructor($scope: any, $state: any, i18nService: any) {
$scope.$state = $state;
$scope.i18n = i18nService;
}
}
TabsController.$inject = ['$scope', '$state', 'i18nService'];

View File

@@ -1,17 +0,0 @@
<div ui-view></div>
<div class="tabs" ng-controller="tabsController">
<ul>
<li ng-class="{active: $state.includes('tabs.current')}">
<a ui-sref="tabs.current"><i class="fa fa-folder fa-2x"></i>{{i18n.tab}}</a>
</li>
<li ng-class="{active: $state.includes('tabs.vault')}">
<a ui-sref="tabs.vault"><i class="fa fa-lock fa-2x"></i>{{i18n.myVault}}</a>
</li>
<li ng-class="{active: $state.includes('tabs.tools')}">
<a ui-sref="tabs.tools"><i class="fa fa-wrench fa-2x"></i>{{i18n.tools}}</a>
</li>
<li ng-class="{active: $state.includes('tabs.settings')}">
<a ui-sref="tabs.settings"><i class="fa fa-cogs fa-2x"></i>{{i18n.settings}}</a>
</li>
</ul>
</div>

View File

@@ -1,25 +0,0 @@
<form name="theForm" ng-submit="$ctrl.submit()">
<div class="header">
<div class="right">
<button type="submit" class="btn btn-link">{{$ctrl.i18n.submit}}</button>
</div>
<div class="title">{{$ctrl.i18n.verifyMasterPassword}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">{{$ctrl.i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword"
placeholder="{{$ctrl.i18n.masterPass}}" ng-model="$ctrl.masterPassword">
</div>
</div>
</div>
</div>
<p class="text-center text-accent">
<a ng-click="$ctrl.logOut()" href="">{{$ctrl.i18n.logOut}}</a>
</p>
</div>
</form>

View File

@@ -1,69 +0,0 @@
import * as angular from 'angular';
import * as template from './lock.component.html';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { UserService } from 'jslib/abstractions/user.service';
import { PopupUtilsService } from '../services/popupUtils.service';
export class LockController {
i18n: any;
masterPassword: string;
constructor(public $state: any, public i18nService: any, private $timeout: ng.ITimeoutService,
public cryptoService: CryptoService, public toastr: any, public userService: UserService,
public messagingService: MessagingService, public SweetAlert: any) {
this.i18n = i18nService;
}
$onInit() {
this.$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('master-password').focus();
}, 500);
}
logOut() {
this.SweetAlert.swal({
title: this.i18nService.logOut,
text: this.i18nService.logOutConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
if (confirmed) {
this.messagingService.send('logout');
}
});
}
async submit() {
if (this.masterPassword == null || this.masterPassword === '') {
this.toastr.error(this.i18nService.invalidMasterPassword, this.i18nService.errorsOccurred);
return;
}
const email = await this.userService.getEmail();
const key = this.cryptoService.makeKey(this.masterPassword, email);
const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) {
await this.cryptoService.setKey(key);
this.messagingService.send('unlocked');
this.$state.go('tabs.current');
} else {
this.toastr.error(this.i18nService.invalidMasterPassword, this.i18nService.errorsOccurred);
}
}
}
LockController.$inject = ['$state', 'i18nService', '$timeout', 'cryptoService', 'toastr', 'userService',
'messagingService', 'SweetAlert'];
export const LockComponent = {
bindings: {},
controller: LockController,
template: template,
};

View File

@@ -1,9 +0,0 @@
import * as angular from 'angular';
import { LockComponent } from './lock.component';
export default angular
.module('bit.lock', ['ngAnimate', 'toastr'])
.component('lock', LockComponent)
.name;

View File

@@ -1,55 +0,0 @@
import { BrowserApi } from '../../../browser/browserApi';
import { ConstantsService } from 'jslib/services/constants.service';
import { ApiService } from 'jslib/abstractions/api.service';
import { AppIdService } from 'jslib/abstractions/appId.service';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LockService } from 'jslib/abstractions/lock.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { SettingsService } from 'jslib/abstractions/settings.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { TokenService } from 'jslib/abstractions/token.service';
import { TotpService } from 'jslib/abstractions/totp.service';
import { UserService } from 'jslib/abstractions/user.service';
import { UtilsService } from 'jslib/abstractions/utils.service';
import { AutofillService } from '../../../services/abstractions/autofill.service';
function getBackgroundService<T>(service: string) {
return (): T => {
const page = BrowserApi.getBackgroundPage();
return page ? page.bitwardenMain[service] as T : null;
};
}
export const storageService = getBackgroundService<StorageService>('storageService');
export const tokenService = getBackgroundService<TokenService>('tokenService');
export const cryptoService = getBackgroundService<CryptoService>('cryptoService');
export const userService = getBackgroundService<UserService>('userService');
export const apiService = getBackgroundService<ApiService>('apiService');
export const folderService = getBackgroundService<FolderService>('folderService');
export const cipherService = getBackgroundService<CipherService>('cipherService');
export const syncService = getBackgroundService<SyncService>('syncService');
export const autofillService = getBackgroundService<AutofillService>('autofillService');
export const passwordGenerationService = getBackgroundService<PasswordGenerationService>('passwordGenerationService');
export const platformUtilsService = getBackgroundService<PlatformUtilsService>('platformUtilsService');
export const utilsService = getBackgroundService<UtilsService>('utilsService');
export const appIdService = getBackgroundService<AppIdService>('appIdService');
export const i18nService = getBackgroundService<any>('i18nService');
export const i18n2Service = getBackgroundService<I18nService>('i18n2Service');
export const constantsService = getBackgroundService<ConstantsService>('constantsService');
export const settingsService = getBackgroundService<SettingsService>('settingsService');
export const lockService = getBackgroundService<LockService>('lockService');
export const totpService = getBackgroundService<TotpService>('totpService');
export const environmentService = getBackgroundService<EnvironmentService>('environmentService');
export const collectionService = getBackgroundService<CollectionService>('collectionService');
export const auditService = getBackgroundService<CollectionService>('auditService');

View File

@@ -1,120 +0,0 @@
export class PopupUtilsService {
static initListSectionItemListeners(doc: Document, angular: any): void {
if (!doc) {
throw new Error('doc parameter required');
}
const sectionItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"])');
const sectionFormItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"]) input, ' +
'.list-section-item:not([data-bw-events="1"]) select, ' +
'.list-section-item:not([data-bw-events="1"]) textarea');
sectionItems.forEach((item) => {
(item as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('click', (e) => {
if (e.defaultPrevented) {
return;
}
const el = e.target as HTMLElement;
// Some elements will already focus properly
if (el.tagName != null) {
switch (el.tagName.toLowerCase()) {
case 'label': case 'input': case 'textarea': case 'select':
return;
default:
break;
}
}
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
const textFilter = 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])';
const text = cell.querySelectorAll(textFilter + ', textarea');
const checkbox = cell.querySelectorAll('input[type="checkbox"]');
const select = cell.querySelectorAll('select');
if (text.length > 0) {
(text[0] as HTMLElement).focus();
} else if (select.length > 0) {
(select[0] as HTMLElement).focus();
} else if (checkbox.length > 0) {
const cb = checkbox[0] as HTMLInputElement;
cb.checked = !cb.checked;
if (angular) {
angular.element(checkbox[0]).triggerHandler('click');
}
}
}, false);
});
sectionFormItems.forEach((item) => {
const itemCell = item.closest('.list-section-item');
(itemCell as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('focus', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.add('active');
}, false);
item.addEventListener('blur', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.remove('active');
}, false);
});
}
static inSidebar(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=sidebar') > -1;
}
static inTab(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=tab') > -1;
}
static inPopout(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=popout') > -1;
}
static inPopup(theWindow: Window): boolean {
return theWindow.location.search === '' || theWindow.location.search.indexOf('uilocation=') === -1 ||
theWindow.location.search.indexOf('uilocation=popup') > -1;
}
initListSectionItemListeners(doc: Document, angular: any): void {
PopupUtilsService.initListSectionItemListeners(doc, angular);
}
inSidebar(theWindow: Window): boolean {
return PopupUtilsService.inSidebar(theWindow);
}
inTab(theWindow: Window): boolean {
return PopupUtilsService.inTab(theWindow);
}
inPopout(theWindow: Window): boolean {
return PopupUtilsService.inPopout(theWindow);
}
inPopup(theWindow: Window): boolean {
return PopupUtilsService.inPopup(theWindow);
}
}

View File

@@ -1,51 +0,0 @@
import * as angular from 'angular';
import * as backgroundServices from './background.service';
import { PopupUtilsService } from './popupUtils.service';
import { StateService } from './state.service';
import { ValidationService } from './validation.service';
import { AuthService } from 'jslib/services/auth.service';
import BrowserMessagingService from '../../../services/browserMessaging.service';
const messagingService = new BrowserMessagingService();
const authService = new AuthService(backgroundServices.cryptoService(), backgroundServices.apiService(),
backgroundServices.userService(), backgroundServices.tokenService(), backgroundServices.appIdService(),
backgroundServices.i18n2Service(), backgroundServices.platformUtilsService(),
backgroundServices.constantsService(), messagingService);
if (backgroundServices.i18n2Service()) {
authService.init();
}
export default angular
.module('bit.services', ['toastr'])
.service('stateService', StateService)
.service('validationService', ValidationService)
.service('popupUtilsService', PopupUtilsService)
.factory('authService', () => authService)
.factory('messagingService', () => messagingService)
.factory('storageService', backgroundServices.storageService)
.factory('tokenService', backgroundServices.tokenService)
.factory('cryptoService', backgroundServices.cryptoService)
.factory('userService', backgroundServices.userService)
.factory('apiService', backgroundServices.apiService)
.factory('folderService', backgroundServices.folderService)
.factory('cipherService', backgroundServices.cipherService)
.factory('syncService', backgroundServices.syncService)
.factory('autofillService', backgroundServices.autofillService)
.factory('passwordGenerationService', backgroundServices.passwordGenerationService)
.factory('platformUtilsService', backgroundServices.platformUtilsService)
.factory('utilsService', backgroundServices.utilsService)
.factory('appIdService', backgroundServices.appIdService)
.factory('i18nService', backgroundServices.i18nService)
.factory('constantsService', backgroundServices.constantsService)
.factory('settingsService', backgroundServices.settingsService)
.factory('lockService', backgroundServices.lockService)
.factory('totpService', backgroundServices.totpService)
.factory('environmentService', backgroundServices.environmentService)
.factory('collectionService', backgroundServices.collectionService)
.factory('auditService', backgroundServices.auditService)
.name;

View File

@@ -1,39 +0,0 @@
import { ConstantsService } from 'jslib/services/constants.service';
import { StorageService } from 'jslib/abstractions/storage.service';
export class StateService {
private state: any = {};
constructor(private storageService: StorageService, private constantsService: ConstantsService) {
}
async init() {
if (this.storageService != null) {
const iconsDisabled = await this.storageService.get<boolean>(this.constantsService.disableFaviconKey);
this.saveState('faviconEnabled', !iconsDisabled);
}
}
saveState(key: string, data: any) {
this.state[key] = data;
}
getState(key: string): any {
if (key in this.state) {
return this.state[key];
}
return null;
}
removeState(key: string) {
delete this.state[key];
}
purgeState() {
this.state = {};
}
}
StateService.$inject = ['storageService', 'constantsService'];

View File

@@ -1,36 +0,0 @@
import * as angular from 'angular';
export class ValidationService {
constructor(private toastr: any, private i18nService: any) {
}
showError(data: any) {
const defaultErrorMessage = this.i18nService.unexpectedError;
const errors: string[] = [];
if (!data || !angular.isObject(data)) {
errors.push(defaultErrorMessage);
} else if (!data.validationErrors) {
errors.push(data.message ? data.message : defaultErrorMessage);
} else {
for (const key in data.validationErrors) {
if (!data.validationErrors.hasOwnProperty(key)) {
continue;
}
data.validationErrors[key].forEach((item: string) => {
errors.push(item);
});
}
}
if (errors.length) {
this.toastr.error(errors[0], this.i18nService.errorsOccurred);
}
return errors;
}
}
ValidationService.$inject = ['toastr', 'i18nService'];

View File

@@ -1,23 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.about}}</div>
</div>
<div class="content">
<div class="about-page">
<img src="../../images/logo@3x.png" alt="bitwarden" />
{{$ctrl.i18n.version}} {{$ctrl.version}}<br />
&copy; 8bit Solutions LLC 2015-{{$ctrl.year}}
</div>
<div class="list">
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item" ui-sref="credits({animation: 'in-slide-left'})">
{{$ctrl.i18n.credits}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
</div>
</div>
</div>

View File

@@ -1,23 +0,0 @@
import * as template from './about.component.html';
import { BrowserApi } from '../../../browser/browserApi';
export class AboutController {
version: string;
year: number;
i18n: any;
constructor(private i18nService: any) {
this.i18n = i18nService;
this.year = (new Date()).getFullYear();
this.version = BrowserApi.getApplicationVersion();
}
}
AboutController.$inject = ['i18nService'];
export const AboutComponent = {
bindings: {},
controller: AboutController,
template: template,
};

View File

@@ -1,34 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="about({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.thankYou}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.translations}}
</div>
<div class="list-section-items">
<div class="list-section-item">
<b>@sersoftin</b> - Russian<br />
<b>@lbel</b> - Dutch<br />
<b>@KarimGeiger</b> - German<br />
<b>@Primokorn</b> - French<br />
<b>@felixqu</b> - Chinese Simplified<br />
<b>@thomassth</b> - Chinese Traditional<br />
<b>@Igetin</b> - Finnish<br />
<b>@LivingWithHippos</b> - Italian<br />
<b>@King-Tut-Tut</b> - Swedish<br />
<b>@majod</b> - Slovak<br />
<b>@RixzZ</b> - Spanish<br />
<b>@SW1FT</b> - Portuguese
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.contribute}} <a href="" ng-click="$ctrl.learnMore()">{{$ctrl.i18n.learnMore}}</a>
</div>
</div>
</div>
</div>

View File

@@ -1,24 +0,0 @@
import * as template from './credits.component.html';
import { BrowserApi } from '../../../browser/browserApi';
export class CreditsController {
i18n: any;
constructor(private i18nService: any, private $analytics: any) {
this.i18n = i18nService;
}
learnMore() {
this.$analytics.eventTrack('Contribute Learn More');
BrowserApi.createNewTab('https://github.com/bitwarden/browser/blob/master/CONTRIBUTING.md');
}
}
CreditsController.$inject = ['i18nService', '$analytics'];
export const CreditsComponent = {
bindings: {},
controller: CreditsController,
template: template,
};

View File

@@ -1,56 +0,0 @@
<form name="theForm" ng-submit="$ctrl.save()">
<div class="header">
<div class="left">
<a ui-sref="home({animation: 'out-slide-down'})">{{$ctrl.i18n.close}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link">{{$ctrl.i18n.save}}</button>
</div>
<div class="title">{{$ctrl.i18n.settings}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.selfHostedEnvironment}}
</div>
<div class="list-section-items">
<div class="list-section-item">
<label for="baseUrl" class="item-label">{{$ctrl.i18n.baseUrl}}</label>
<input id="baseUrl" type="text" name="BaseUrl" ng-model="$ctrl.baseUrl"
placeholder="ex. https://bitwarden.company.com">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.selfHostedEnvironmentFooter}}
</div>
</div>
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.customEnvironment}}
</div>
<div class="list-section-items">
<div class="list-section-item">
<label for="webVaultUrl" class="item-label">{{$ctrl.i18n.webVaultUrl}}</label>
<input id="webVaultUrl" type="text" name="WebVaultUrl" ng-model="$ctrl.webVaultUrl">
</div>
<div class="list-section-item">
<label for="apiUrl" class="item-label">{{$ctrl.i18n.apiUrl}}</label>
<input id="apiUrl" type="text" name="ApiUrl" ng-model="$ctrl.apiUrl">
</div>
<div class="list-section-item">
<label for="identityUrl" class="item-label">{{$ctrl.i18n.identityUrl}}</label>
<input id="identityUrl" type="text" name="IdentityUrl" ng-model="$ctrl.identityUrl">
</div>
<div class="list-section-item">
<label for="iconsUrl" class="item-label">{{$ctrl.i18n.iconsUrl}}</label>
<input id="iconsUrl" type="text" name="IconsUrl" ng-model="$ctrl.iconsUrl">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.customEnvironmentFooter}}
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,60 +0,0 @@
import * as angular from 'angular';
import * as template from './environment.component.html';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { PopupUtilsService } from '../services/popupUtils.service';
export class EnvironmentController {
iconsUrl: string;
identityUrl: string;
apiUrl: string;
webVaultUrl: string;
baseUrl: string;
i18n: any;
constructor(private i18nService: any, private $analytics: any, private environmentService: EnvironmentService,
private toastr: any, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
}, 500);
this.baseUrl = environmentService.baseUrl || '';
this.webVaultUrl = environmentService.webVaultUrl || '';
this.apiUrl = environmentService.apiUrl || '';
this.identityUrl = environmentService.identityUrl || '';
this.iconsUrl = environmentService.iconsUrl || '';
}
save() {
this.environmentService.setUrls({
base: this.baseUrl,
api: this.apiUrl,
identity: this.identityUrl,
webVault: this.webVaultUrl,
icons: this.iconsUrl,
}).then((resUrls: any) => {
this.$timeout(() => {
// 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.$analytics.eventTrack('Set Environment URLs');
this.toastr.success(this.i18nService.environmentSaved);
});
});
}
}
EnvironmentController.$inject = ['i18nService', '$analytics', 'environmentService', 'toastr', '$timeout'];
export const EnvironmentComponent = {
bindings: {},
controller: EnvironmentController,
template: template,
};

View File

@@ -1,24 +0,0 @@
<form name="theForm" ng-submit="$ctrl.save($ctrl.folder)" bit-form="$ctrl.savePromise" autocomplete="off">
<div class="header">
<div class="left">
<a ui-sref="^.list({animation: 'out-slide-down'})">{{$ctrl.i18n.cancel}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{$ctrl.i18n.save}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{$ctrl.i18n.addFolder}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item">
<label for="name" class="item-label">{{$ctrl.i18n.name}}</label>
<input id="name" type="text" name="Name" ng-model="$ctrl.folder.name">
</div>
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,49 +0,0 @@
import * as angular from 'angular';
import * as template from './add-folder.component.html';
import { Folder } from 'jslib/models/domain/folder';
import { FolderService } from 'jslib/abstractions/folder.service';
import { PopupUtilsService } from '../../services/popupUtils.service';
export class AddFolderController {
savePromise: any;
folder: {};
i18n: any;
constructor(private folderService: FolderService, private $state: any, private toastr: any,
private $analytics: any, private i18nService: any, $timeout: ng.ITimeoutService) {
$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('name').focus();
}, 500);
this.i18n = i18nService;
this.folder = {};
this.savePromise = null;
}
save(model: any) {
if (!model.name) {
this.toastr.error(this.i18nService.nameRequired, this.i18nService.errorsOccurred);
return;
}
this.savePromise = this.folderService.encrypt(model).then((folder: Folder) => {
return this.folderService.saveWithServer(folder);
}).then(() => {
this.$analytics.eventTrack('Added Folder');
this.toastr.success(this.i18nService.addedFolder);
this.$state.go('^.list', { animation: 'out-slide-down' });
});
}
}
AddFolderController.$inject = ['folderService', '$state', 'toastr', '$analytics', 'i18nService', '$timeout'];
export const AddFolderComponent = {
bindings: {},
controller: AddFolderController,
template: template,
};

View File

@@ -1,31 +0,0 @@
<form name="theForm" ng-submit="$ctrl.save($ctrl.folder)" bit-form="savePromise" autocomplete="off">
<div class="header">
<div class="left">
<a ui-sref="^.list({animation: 'out-slide-down'})">{{$ctrl.i18n.cancel}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{$ctrl.i18n.save}}</button>
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
</div>
<div class="title">{{$ctrl.i18n.editFolder}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item">
<label for="name" class="item-label">{{$ctrl.i18n.name}}</label>
<input id="name" type="text" name="Name" ng-model="$ctrl.folder.name">
</div>
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<a href="" ng-click="$ctrl.delete()" class="list-section-item text-danger">
<i class="fa fa-trash fa-fw fa-lg"></i>{{$ctrl.i18n.deleteFolder}}
</a>
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,84 +0,0 @@
import * as angular from 'angular';
import * as template from './edit-folder.component.html';
import { Folder } from 'jslib/models/domain/folder';
import { FolderService } from 'jslib/abstractions/folder.service';
import { PopupUtilsService } from '../../services/popupUtils.service';
export class EditFolderController {
$transition$: any;
folderId: string;
savePromise: Promise<any> = null;
i18n: any;
folder: Folder;
constructor($scope: any, $stateParams: any, private folderService: FolderService, private toastr: any,
private $state: any, private SweetAlert: any, private $analytics: any, private i18nService: any,
$timeout: ng.ITimeoutService) {
this.i18n = i18nService;
$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('name').focus();
}, 500);
$scope.folder = {};
}
$onInit() {
this.folderId = this.$transition$.params('to').folderId;
this.folderService.get(this.folderId).then((folder: any) => {
return folder.decrypt();
}).then((model: Folder) => {
this.folder = model;
});
}
save(model: any) {
if (!model.name) {
this.toastr.error(this.i18nService.nameRequired, this.i18nService.errorsOccurred);
return;
}
this.savePromise = this.folderService.encrypt(model).then((folder: Folder) => {
return this.folderService.saveWithServer(folder);
}).then(() => {
this.$analytics.eventTrack('Edited Folder');
this.toastr.success(this.i18nService.editedFolder);
this.$state.go('^.list', { animation: 'out-slide-down' });
});
}
delete() {
this.SweetAlert.swal({
title: this.i18nService.deleteFolder,
text: this.i18nService.deleteFolderConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.no,
}, (confirmed: boolean) => {
if (confirmed) {
this.folderService.deleteWithServer(this.folderId).then(() => {
this.$analytics.eventTrack('Deleted Folder');
this.toastr.success(this.i18nService.deletedFolder);
this.$state.go('^.list', {
animation: 'out-slide-down',
});
});
}
});
}
}
EditFolderController.$inject = ['$scope', '$stateParams', 'folderService', 'toastr', '$state', 'SweetAlert',
'$analytics', 'i18nService', '$timeout'];
export const EditFolderComponent = {
bindings: {
$transition$: '<',
},
controller: EditFolderController,
template: template,
};

View File

@@ -1,31 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="right">
<a ui-sref="^.add({animation: 'in-slide-up'})" title="{{::$ctrl.i18n.addFolder}}"><i class="fa fa-plus fa-lg"></i></a>
</div>
<div class="title">{{$ctrl.i18n.folders}}</div>
</div>
<div class="content">
<div ng-if="$ctrl.folders.length">
<div class="list">
<div class="list-grouped">
<a href="" ng-click="$ctrl.editFolder(folder)" class="list-grouped-item" title="{{::$ctrl.i18n.edit}}"
ng-repeat="folder in $ctrl.folders track by $index">
<span class="text">{{folder.name}}</span>
</a>
</div>
</div>
</div>
<div class="centered-message" ng-if="$ctrl.loaded && !$ctrl.folders.length">
<p>
{{$ctrl.i18n.noFolders}}
<a ui-sref="^.add({animation: 'in-slide-up'})" style="margin-top: 20px;"
class="btn btn-link btn-block">{{$ctrl.i18n.addFolder}}</a>
</p>
</div>
<div class="page-loading" ng-if="!$ctrl.loaded">
<i class="fa fa-lg fa-spinner fa-spin"></i>
</div>
</div>

View File

@@ -1,44 +0,0 @@
import * as template from './folders.component.html';
import { Folder } from 'jslib/models/domain/folder';
import { FolderService } from 'jslib/abstractions/folder.service';
export class FoldersController {
folders: Folder[] = [];
i18n: any;
loaded = false;
constructor(private folderService: FolderService, private $state: any, i18nService: any) {
this.i18n = i18nService;
this.load();
}
load() {
this.folderService.getAllDecrypted().then((folders: any) => {
if (folders.length > 0 && folders[folders.length - 1].id === null) {
// remove the "none" folder
this.folders = folders.slice(0, folders.length - 1);
} else {
this.folders = folders;
}
this.loaded = true;
});
}
editFolder(folder: any) {
this.$state.go('^.edit', {
folderId: folder.id,
animation: 'in-slide-up',
});
}
}
FoldersController.$inject = ['folderService', '$state', 'i18nService'];
export const FoldersComponent = {
bindings: {},
controller: FoldersController,
template: template,
};

View File

@@ -1,54 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.helpFeedback}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item" href="" ng-click="$ctrl.email()">
{{$ctrl.i18n.emailUs}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.emailUsDirectly}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item" href="" ng-click="$ctrl.website()">
{{$ctrl.i18n.visitOurWebsite}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.visitOurWebsiteDirectly}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item" href="" ng-click="$ctrl.tutorial()">
{{$ctrl.i18n.gettingStartedTutorial}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.gettingStartedTutorialVideo}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item" href="" ng-click="$ctrl.bug()">
{{$ctrl.i18n.fileBugReport}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.gitHubIssue}}
</div>
</div>
</div>
</div>

View File

@@ -1,39 +0,0 @@
import * as template from './help.component.html';
import { BrowserApi } from '../../../browser/browserApi';
export class HelpController {
i18n: any;
constructor(private i18nService: any, private $analytics: any) {
this.i18n = i18nService;
}
email() {
this.$analytics.eventTrack('Selected Help Email');
BrowserApi.createNewTab('mailto:hello@bitwarden.com');
}
website() {
this.$analytics.eventTrack('Selected Help Website');
BrowserApi.createNewTab('https://bitwarden.com/contact/');
}
tutorial() {
this.$analytics.eventTrack('Selected Help Tutorial');
BrowserApi.createNewTab('https://bitwarden.com/browser-start/');
}
bug() {
this.$analytics.eventTrack('Selected Help Bug Report');
BrowserApi.createNewTab('https://github.com/bitwarden/browser');
}
}
HelpController.$inject = ['i18nService', '$analytics'];
export const HelpComponent = {
bindings: {},
controller: HelpController,
template: template,
};

View File

@@ -1,82 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.options}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="auto-fill">{{$ctrl.i18n.enableAutoFillOnPageLoad}}</label>
<input id="auto-fill" type="checkbox" ng-model="$ctrl.enableAutoFillOnPageLoad"
ng-change="$ctrl.updateAutoFillOnPageLoad()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.enableAutoFillOnPageLoadDesc}}
<b>{{$ctrl.i18n.warning}}</b>: {{$ctrl.i18n.experimentalFeature}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="totp-copy">{{$ctrl.i18n.disableAutoTotpCopy}}</label>
<input id="totp-copy" type="checkbox" ng-model="$ctrl.disableAutoTotpCopy"
ng-change="$ctrl.updateAutoTotpCopy()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.disableAutoTotpCopyDesc}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="ga">{{$ctrl.i18n.disableGa}}</label>
<input id="ga" type="checkbox" ng-model="$ctrl.disableGa" ng-change="$ctrl.updateGa()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.gaDesc}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="notification-bar">{{$ctrl.i18n.disableAddLoginNotification}}</label>
<input id="notification-bar" type="checkbox" ng-model="$ctrl.disableAddLoginNotification"
ng-change="$ctrl.updateAddLoginNotification()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.addLoginNotificationDesc}}
</div>
</div>
<div class="list-section" ng-if="$ctrl.showDisableContextMenu">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="context-menu">{{$ctrl.i18n.disableContextMenuItem}}</label>
<input id="context-menu" type="checkbox" ng-model="$ctrl.disableContextMenuItem"
ng-change="$ctrl.updateDisableContextMenuItem()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.disableContextMenuItemDesc}}
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="favicon">{{$ctrl.i18n.disableFavicon}}</label>
<input id="favicon" type="checkbox" ng-model="$ctrl.disableFavicon"
ng-change="$ctrl.updateDisableFavicon()">
</div>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.disableFaviconDesc}}
</div>
</div>
</div>
</div>

View File

@@ -1,107 +0,0 @@
import * as angular from 'angular';
import * as template from './options.component.html';
import { ConstantsService } from 'jslib/services/constants.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { TotpService } from 'jslib/abstractions/totp.service';
import { PopupUtilsService } from '../services/popupUtils.service';
import { StateService } from '../services/state.service';
export class OptionsController {
disableFavicon = false;
enableAutoFillOnPageLoad = false;
disableAutoTotpCopy = false;
disableContextMenuItem = false;
disableAddLoginNotification = false;
showDisableContextMenu = true;
disableGa = false;
i18n: any;
constructor(private i18nService: any, private $analytics: any, private constantsService: ConstantsService,
private platformUtilsService: PlatformUtilsService, private totpService: TotpService,
private stateService: StateService, private storageService: StorageService,
public messagingService: MessagingService, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
this.showDisableContextMenu = !platformUtilsService.isSafari();
$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
}, 500);
this.loadSettings();
}
async loadSettings() {
this.enableAutoFillOnPageLoad = await this.storageService.get<boolean>(
this.constantsService.enableAutoFillOnPageLoadKey);
const disableGa = await this.storageService.get<boolean>(
this.constantsService.disableGaKey);
this.disableGa = disableGa || (this.platformUtilsService.isFirefox() && disableGa == null);
this.disableAddLoginNotification = await this.storageService.get<boolean>(
this.constantsService.disableAddLoginNotificationKey);
this.disableContextMenuItem = await this.storageService.get<boolean>(
this.constantsService.disableContextMenuItemKey);
this.disableAutoTotpCopy = !await this.totpService.isAutoCopyEnabled();
this.disableFavicon = await this.storageService.get<boolean>(
this.constantsService.disableFaviconKey);
}
callAnalytics(name: string, enabled: boolean) {
const status = enabled ? 'Enabled' : 'Disabled';
this.$analytics.eventTrack(`${status} ${name}`);
}
updateGa() {
this.storageService.save(this.constantsService.disableGaKey, this.disableGa);
this.callAnalytics('Analytics', !this.disableGa);
}
updateAddLoginNotification() {
this.storageService.save(this.constantsService.disableAddLoginNotificationKey,
this.disableAddLoginNotification);
this.callAnalytics('Add Login Notification', !this.disableAddLoginNotification);
}
updateDisableContextMenuItem() {
this.storageService.save(this.constantsService.disableContextMenuItemKey,
this.disableContextMenuItem).then(() => {
this.messagingService.send('bgUpdateContextMenu');
});
this.callAnalytics('Context Menu Item', !this.disableContextMenuItem);
}
updateAutoTotpCopy() {
this.storageService.save(this.constantsService.disableAutoTotpCopyKey, this.disableAutoTotpCopy);
this.callAnalytics('Auto Copy TOTP', !this.disableAutoTotpCopy);
}
updateAutoFillOnPageLoad() {
this.storageService.save(this.constantsService.enableAutoFillOnPageLoadKey,
this.enableAutoFillOnPageLoad);
this.callAnalytics('Auto-fill Page Load', this.enableAutoFillOnPageLoad);
}
updateDisableFavicon() {
this.storageService.save(this.constantsService.disableFaviconKey, this.disableFavicon);
this.stateService.saveState('faviconEnabled', !this.disableFavicon);
this.callAnalytics('Favicon', !this.disableFavicon);
}
}
OptionsController.$inject = ['i18nService', '$analytics', 'constantsService', 'platformUtilsService', 'totpService',
'stateService', 'storageService', 'messagingService', '$timeout'];
export const OptionsComponent = {
bindings: {},
controller: OptionsController,
template: template,
};

View File

@@ -1,54 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.premiumMembership}}</div>
</div>
<div class="content">
<div class="premium-page">
<div ng-if="!$ctrl.isPremium">
<p class="text-center lead">{{$ctrl.i18n.premiumNotCurrentMember}}</p>
<p>{{$ctrl.i18n.premiumSignUpAndGet}}</p>
<ul class="fa-ul">
<li>
<i class="fa-li fa fa-check text-success"></i>
{{$ctrl.i18n.ppremiumSignUpStorage}}
</li>
<li>
<i class="fa-li fa fa-check text-success"></i>
{{$ctrl.i18n.ppremiumSignUpTwoStep}}
</li>
<li>
<i class="fa-li fa fa-check text-success"></i>
{{$ctrl.i18n.ppremiumSignUpTotp}}
</li>
<li>
<i class="fa-li fa fa-check text-success"></i>
{{$ctrl.i18n.ppremiumSignUpSupport}}
</li>
<li>
<i class="fa-li fa fa-check text-success"></i>
{{$ctrl.i18n.ppremiumSignUpFuture}}
</li>
</ul>
<p class="text-center lead">{{$ctrl.i18n.premiumPrice.replace('%price%', $ctrl.price)}}</p>
<div class="bottom-buttons">
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="$ctrl.purchase()">
<b>{{$ctrl.i18n.premiumPurchase}}</b>
</a>
<a class="btn btn-lg btn-link btn-block" href="#" stop-click ng-click="$ctrl.refresh()">
{{$ctrl.i18n.premiumRefresh}}
</a>
</div>
</div>
<div ng-if="$ctrl.isPremium">
<p class="text-center lead">{{$ctrl.i18n.premiumCurrentMember}}</p>
<p class="text-center">{{$ctrl.i18n.premiumCurrentMemberThanks}}</p>
<div class="bottom-buttons">
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="$ctrl.manage()">
<b>{{$ctrl.i18n.premiumManage}}</b>
</a>
</div>
</div>
</div>
</div>

View File

@@ -1,68 +0,0 @@
import * as template from './premium.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { ApiService } from 'jslib/abstractions/api.service';
import { TokenService } from 'jslib/abstractions/token.service';
export class PremiumController {
isPremium: boolean;
i18n: any;
price = '$10';
constructor(private i18nService: any, private tokenService: TokenService, private apiService: ApiService,
private toastr: any, private SweetAlert: any, private $analytics: any, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
this.isPremium = tokenService.getPremium();
}
refresh() {
this.apiService.refreshIdentityToken().then(() => {
this.toastr.success(this.i18nService.refreshComplete);
this.$timeout(() => {
this.isPremium = this.tokenService.getPremium();
});
}, (err: any) => {
this.toastr.error(this.i18nService.errorsOccurred);
});
}
purchase() {
this.SweetAlert.swal({
title: this.i18nService.premiumPurchase,
text: this.i18nService.premiumPurchaseAlert,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
this.$analytics.eventTrack('Clicked Purchase Premium');
if (confirmed) {
BrowserApi.createNewTab('https://vault.bitwarden.com/#/?premium=purchase');
}
});
}
manage() {
this.SweetAlert.swal({
title: this.i18nService.premiumManage,
text: this.i18nService.premiumManageAlert,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
this.$analytics.eventTrack('Clicked Manage Membership');
if (confirmed) {
BrowserApi.createNewTab('https://vault.bitwarden.com/#/?premium=manage');
}
});
}
}
PremiumController.$inject = ['i18nService', 'tokenService', 'apiService', 'toastr', 'SweetAlert', '$analytics',
'$timeout'];
export const PremiumComponent = {
bindings: {},
controller: PremiumController,
template: template,
};

View File

@@ -1,101 +0,0 @@
<div class="header">
<pop-out ng-if="$ctrl.showPopout" class="left"></pop-out>
<div class="title">{{$ctrl.i18n.settings}}</div>
</div>
<div class="content content-tabs">
<div class="list">
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.security}}
</div>
<div class="list-section-items">
<div class="list-section-item">
<label for="lock-option" class="item-label">{{$ctrl.i18n.lockOptions}}</label>
<select id="lock-option" name="LockOption" ng-model="$ctrl.lockOption" ng-change="$ctrl.changeLockOption()">
<option value="0">{{$ctrl.i18n.immediately}}</option>
<option value="1">{{$ctrl.i18n.oneMinute}}</option>
<option value="5">{{$ctrl.i18n.fiveMinutes}}</option>
<option value="15">{{$ctrl.i18n.fifteenMinutes}}</option>
<option value="30">{{$ctrl.i18n.thirtyMinutes}}</option>
<option value="60">{{$ctrl.i18n.oneHour}}</option>
<option value="240">{{$ctrl.i18n.fourHours}}</option>
<option value="-2" ng-if="$ctrl.showOnLocked">{{$ctrl.i18n.onLocked}}</option>
<option value="-1">{{$ctrl.i18n.onRestart}}</option>
<option value="">{{$ctrl.i18n.never}}</option>
</select>
</div>
<a class="list-section-item" href="" ng-click="$ctrl.lock()">
{{$ctrl.i18n.lockNow}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" href="" ng-click="$ctrl.twoStep()">
{{$ctrl.i18n.twoStepLogin}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
</div>
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.account}}
</div>
<div class="list-section-items">
<a class="list-section-item text-primary" ui-sref="premium({animation: 'in-slide-left'})">
<i class="fa fa-star fa-fw"></i> <b>{{$ctrl.i18n.premiumMembership}}</b>
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" href="" ng-click="$ctrl.changePassword()">
{{$ctrl.i18n.changeMasterPassword}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" href="" ng-click="$ctrl.changeEmail()">
{{$ctrl.i18n.changeEmail}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" href="" ng-click="$ctrl.logOut()">
{{$ctrl.i18n.logOut}}
</a>
</div>
</div>
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.manage}}
</div>
<div class="list-section-items">
<a class="list-section-item" ui-sref="folders.list({animation: 'in-slide-left'})">
{{$ctrl.i18n.folders}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" ui-sref="sync({animation: 'in-slide-left'})">
{{$ctrl.i18n.sync}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
</div>
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.other}}
</div>
<div class="list-section-items">
<a class="list-section-item" ui-sref="options({animation: 'in-slide-left'})">
{{$ctrl.i18n.options}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" ui-sref="about({animation: 'in-slide-left'})">
{{$ctrl.i18n.about}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" ui-sref="help({animation: 'in-slide-left'})">
{{$ctrl.i18n.helpFeedback}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
<a class="list-section-item" href="" ng-click="$ctrl.rate()">
{{$ctrl.i18n.rateExtension}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
<div class="list-section-footer">
{{$ctrl.i18n.rateExtensionDesc}}
</div>
</div>
</div>
</div>

View File

@@ -1,172 +0,0 @@
import * as angular from 'angular';
import * as template from './settings.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { DeviceType } from 'jslib/enums/deviceType';
import { ConstantsService } from 'jslib/services/constants.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { LockService } from 'jslib/abstractions/lock.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { PopupUtilsService } from '../services/popupUtils.service';
const RateUrls = {
[DeviceType.Chrome]:
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
[DeviceType.Firefox]:
'https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews',
[DeviceType.Opera]:
'https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container',
[DeviceType.Edge]:
'https://www.microsoft.com/store/p/bitwarden-free-password-manager/9p6kxl0svnnl',
[DeviceType.Vivaldi]:
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
[DeviceType.Safari]:
'https://itunes.apple.com/app/bitwarden-password-manager/id1137397744',
};
export class SettingsController {
lockOption = '';
i18n: any;
showOnLocked: boolean;
showPopout: boolean = true;
constructor(private $state: any, private SweetAlert: any,
private platformUtilsService: PlatformUtilsService, private $analytics: any,
private i18nService: any, private constantsService: ConstantsService,
private cryptoService: CryptoService, private lockService: LockService,
private storageService: StorageService, public messagingService: MessagingService,
private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
this.showPopout = !platformUtilsService.isSafari();
$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
}, 500);
this.showOnLocked = !platformUtilsService.isFirefox() && !platformUtilsService.isEdge()
&& !platformUtilsService.isSafari();
this.storageService.get(constantsService.lockOptionKey).then((lockOption: number) => {
if (lockOption != null) {
let option = lockOption.toString();
if (option === '-2' && !this.showOnLocked) {
option = '-1';
}
this.lockOption = option;
} else {
this.lockOption = '';
}
});
}
changeLockOption() {
const option = this.lockOption && this.lockOption !== '' ? parseInt(this.lockOption, 10) : null;
this.storageService.save(this.constantsService.lockOptionKey, option).then(() => {
return this.cryptoService.getKeyHash();
}).then((keyHash) => {
if (keyHash) {
this.cryptoService.toggleKey();
} else {
this.SweetAlert.swal({
title: this.i18nService.loggingOut,
text: this.i18nService.loggingOutConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
if (confirmed) {
this.cryptoService.toggleKey();
this.messagingService.send('logout');
}
});
}
});
}
lock() {
this.$analytics.eventTrack('Lock Now');
this.lockService.lock().then(() => {
return this.$state.go('lock', {
animation: 'in-slide-down',
});
});
}
logOut() {
this.SweetAlert.swal({
title: this.i18nService.logOut,
text: this.i18nService.logOutConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
if (confirmed) {
this.messagingService.send('logout');
}
});
}
changePassword() {
this.SweetAlert.swal({
title: this.i18nService.changeMasterPassword,
text: this.i18nService.changeMasterPasswordConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
this.$analytics.eventTrack('Clicked Change Password');
if (confirmed) {
BrowserApi.createNewTab('https://help.bitwarden.com/article/change-your-master-password/');
}
});
}
changeEmail() {
this.SweetAlert.swal({
title: this.i18nService.changeEmail,
text: this.i18nService.changeEmailConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
this.$analytics.eventTrack('Clicked Change Email');
if (confirmed) {
BrowserApi.createNewTab('https://help.bitwarden.com/article/change-your-email/');
}
});
}
twoStep() {
this.SweetAlert.swal({
title: this.i18nService.twoStepLogin,
text: this.i18nService.twoStepLoginConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
this.$analytics.eventTrack('Clicked Two-step Login');
if (confirmed) {
BrowserApi.createNewTab('https://help.bitwarden.com/article/setup-two-step-login/');
}
});
}
rate() {
this.$analytics.eventTrack('Rate Extension');
BrowserApi.createNewTab((RateUrls as any)[this.platformUtilsService.getDevice()]);
}
}
SettingsController.$inject = ['$state', 'SweetAlert', 'platformUtilsService', '$analytics', 'i18nService',
'constantsService', 'cryptoService', 'lockService', 'storageService', 'messagingService', '$timeout'];
export const SettingsComponent = {
bindings: {},
controller: SettingsController,
template: template,
};

View File

@@ -1,29 +0,0 @@
import * as angular from 'angular';
import { AboutComponent } from './about.component';
import { CreditsComponent } from './credits.component';
import { EnvironmentComponent } from './environment.component';
import { AddFolderComponent } from './folders/add-folder.component';
import { EditFolderComponent } from './folders/edit-folder.component';
import { FoldersComponent } from './folders/folders.component';
import { HelpComponent } from './help.component';
import { OptionsComponent } from './options.component';
import { PremiumComponent } from './premium.component';
import { SettingsComponent } from './settings.component';
import { SyncComponent } from './sync.component';
export default angular
.module('bit.settings', ['oitozero.ngSweetAlert', 'toastr'])
.component('settings', SettingsComponent)
.component('environment', EnvironmentComponent)
.component('options', OptionsComponent)
.component('about', AboutComponent)
.component('credits', CreditsComponent)
.component('help', HelpComponent)
.component('folders', FoldersComponent)
.component('addFolder', AddFolderComponent)
.component('editFolder', EditFolderComponent)
.component('premium', PremiumComponent)
.component('sync', SyncComponent)
.name;

View File

@@ -1,19 +0,0 @@
<div class="header">
<div class="left">
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="title">{{$ctrl.i18n.sync}}</div>
</div>
<div class="content">
<div class="centered-message">
<p style="margin-top: -50px;">
<a href="" class="btn btn-lg btn-link btn-block" style="display: inline-block;" ng-click="$ctrl.sync()">
{{$ctrl.i18n.syncVaultNow}}
</a>
<small class="text-muted">{{$ctrl.i18n.lastSync}} {{$ctrl.lastSync}}</small>
<span ng-show="$ctrl.loading" style="display: block; margin-top: 20px;" class="text-center">
<i class="text-muted fa fa-lg fa-spinner fa-spin"></i>
</span>
</p>
</div>
</div>

View File

@@ -1,49 +0,0 @@
import * as template from './sync.component.html';
import { SyncService } from 'jslib/abstractions/sync.service';
export class SyncController {
i18n: any;
lastSync = '--';
loading = false;
constructor(private syncService: SyncService, private toastr: any, private $analytics: any,
private i18nService: any, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
this.setLastSync();
}
sync() {
this.loading = true;
this.syncService.fullSync(true).then((success: boolean) => {
this.loading = false;
if (success) {
this.setLastSync();
this.$analytics.eventTrack('Synced Full');
this.toastr.success(this.i18n.syncingComplete);
} else {
this.toastr.error(this.i18n.syncingFailed);
}
});
}
setLastSync() {
this.syncService.getLastSync().then((last: Date) => {
this.$timeout(() => {
if (last) {
this.lastSync = last.toLocaleDateString() + ' ' + last.toLocaleTimeString();
} else {
this.lastSync = this.i18n.never;
}
});
});
}
}
SyncController.$inject = ['syncService', 'toastr', '$analytics', 'i18nService', '$timeout'];
export const SyncComponent = {
bindings: {},
controller: SyncController,
template: template,
};

View File

@@ -1,29 +0,0 @@
<form name="theForm" ng-submit="$ctrl.submit()">
<div class="header">
<div class="left">
<a ui-sref="tabs.tools({animation: 'out-slide-down'})">{{$ctrl.i18n.close}}</a>
</div>
<div class="right">
<button type="submit" class="btn btn-link">{{$ctrl.i18n.submit}}</button>
</div>
<div class="title">{{$ctrl.i18n.exportVault}}</div>
</div>
<div class="content">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-icon-input">
<i class="fa fa-lock fa-lg fa-fw"></i>
<label for="master-password" class="sr-only">{{$ctrl.i18n.masterPass}}</label>
<input id="master-password" type="password" name="MasterPassword"
placeholder="{{$ctrl.i18n.masterPass}}" ng-model="$ctrl.masterPassword">
</div>
</div>
<div class="list-section-footer">
<p>{{$ctrl.i18n.exportMasterPassword}}</p>
<b>{{$ctrl.i18n.warning}}</b>: {{$ctrl.i18n.exportWarning}}
</div>
</div>
</div>
</div>
</form>

View File

@@ -1,174 +0,0 @@
import * as angular from 'angular';
import * as papa from 'papaparse';
import * as template from './export.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { CipherType } from 'jslib/enums/cipherType';
import { CipherView } from 'jslib/models/view/cipherView';
import { FolderView } from 'jslib/models/view/folderView';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { UserService } from 'jslib/abstractions/user.service';
import { UtilsService } from 'jslib/abstractions/utils.service';
export class ExportController {
i18n: any;
masterPassword: string;
constructor(private $state: any, private cryptoService: CryptoService,
private toastr: any, private utilsService: UtilsService, private $analytics: any,
private i18nService: any, private folderService: FolderService, private cipherService: CipherService,
private $window: ng.IWindowService, private userService: UserService) {
this.i18n = i18nService;
}
$onInit() {
document.getElementById('master-password').focus();
}
async submit() {
if (this.masterPassword == null || this.masterPassword === '') {
this.toastr.error(this.i18nService.invalidMasterPassword, this.i18nService.errorsOccurred);
return;
}
const email = await this.userService.getEmail();
const key = this.cryptoService.makeKey(this.masterPassword, email);
const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) {
const csv = await this.getCsv();
this.$analytics.eventTrack('Exported Data');
this.downloadFile(csv);
this.$state.go('tabs.tools', { animation: 'out-slide-down' });
} else {
this.toastr.error(this.i18n.invalidMasterPassword, this.i18n.errorsOccurred);
}
}
private async checkPassword() {
const email = await this.userService.getEmail();
const key = this.cryptoService.makeKey(this.masterPassword, email);
const keyHash = await this.cryptoService.hashPassword(this.masterPassword, key);
const storedKeyHash = await this.cryptoService.getKeyHash();
if (storedKeyHash == null || keyHash == null || storedKeyHash !== keyHash) {
throw new Error('Invalid password.');
}
}
private async getCsv(): Promise<string> {
let decFolders: FolderView[] = [];
let decCiphers: CipherView[] = [];
const promises = [];
promises.push(this.folderService.getAllDecrypted().then((folders) => {
decFolders = folders;
}));
promises.push(this.cipherService.getAllDecrypted().then((ciphers) => {
decCiphers = ciphers;
}));
await Promise.all(promises);
const foldersMap = new Map<string, FolderView>();
decFolders.forEach((f) => {
foldersMap.set(f.id, f);
});
const exportCiphers: any[] = [];
decCiphers.forEach((c) => {
// only export logins and secure notes
if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) {
return;
}
const cipher: any = {
folder: c.folderId && foldersMap.has(c.folderId) ? foldersMap.get(c.folderId).name : null,
favorite: c.favorite ? 1 : null,
type: null,
name: c.name,
notes: c.notes,
fields: null,
// Login props
login_uri: null,
login_username: null,
login_password: null,
login_totp: null,
};
if (c.fields) {
c.fields.forEach((f: any) => {
if (!cipher.fields) {
cipher.fields = '';
} else {
cipher.fields += '\n';
}
cipher.fields += ((f.name || '') + ': ' + f.value);
});
}
switch (c.type) {
case CipherType.Login:
cipher.type = 'login';
cipher.login_username = c.login.username;
cipher.login_password = c.login.password;
cipher.login_totp = c.login.totp;
if (c.login.uris) {
cipher.login_uri = [];
c.login.uris.forEach((u) => {
cipher.login_uri.push(u.uri);
});
}
break;
case CipherType.SecureNote:
cipher.type = 'note';
break;
default:
return;
}
exportCiphers.push(cipher);
});
const csv = papa.unparse(exportCiphers);
return csv;
}
private downloadFile(csv: string): void {
const fileName = this.makeFileName();
BrowserApi.downloadFile(this.$window, csv, { type: 'text/plain' }, fileName);
}
private makeFileName(): string {
const now = new Date();
const dateString =
now.getFullYear() + '' + this.padNumber(now.getMonth() + 1, 2) + '' + this.padNumber(now.getDate(), 2) +
this.padNumber(now.getHours(), 2) + '' + this.padNumber(now.getMinutes(), 2) +
this.padNumber(now.getSeconds(), 2);
return 'bitwarden_export_' + dateString + '.csv';
}
private padNumber(num: number, width: number, padCharacter: string = '0'): string {
const numString = num.toString();
return numString.length >= width ? numString :
new Array(width - numString.length + 1).join(padCharacter) + numString;
}
}
ExportController.$inject = ['$state', 'cryptoService', 'toastr', 'utilsService', '$analytics', 'i18nService',
'folderService', 'cipherService', '$window', 'userService'];
export const ExportComponent = {
bindings: {},
controller: ExportController,
template: template,
};

View File

@@ -1,33 +0,0 @@
<div class="header">
<div class="left">
<a ng-click="$ctrl.close()" href=""><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
</div>
<div class="right">
<a ng-click="$ctrl.clear()" href="">{{$ctrl.i18n.clear}}</a>
</div>
<div class="title">{{$ctrl.i18n.passwordHistory}}</div>
</div>
<div class="content">
<div class="list" ng-if="$ctrl.loaded">
<div class="list-grouped" ng-if="$ctrl.history.length !== 0">
<div class="list-grouped-item condensed wrap"
ng-repeat="item in $ctrl.history | orderBy: 'date':true track by $index">
<div class="action-buttons">
<span class="btn-list" stop-prop stop-click title="{{$ctrl.i18n.copyPassword}}"
ngclipboard ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e, $ctrl.i18n.password)"
data-clipboard-text="{{item.password}}">
<i class="fa fa-lg fa-clipboard"></i>
</span>
</div>
<span class="text monospaced">
{{item.password}}
</span>
<span class="detail">{{item.date | date: 'medium'}}</span>
</div>
</div>
</div>
<div class="page-loading" ng-if="!$ctrl.loaded">
<i class="fa fa-lg fa-spinner fa-spin"></i>
</div>
</div>

View File

@@ -1,64 +0,0 @@
import * as template from './password-generator-history.component.html';
import { PasswordHistory } from 'jslib/models/domain/passwordHistory';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
export class PasswordGeneratorHistoryController {
$transition$: any;
history: PasswordHistory[];
editState: any;
addState: any;
i18n: any;
loaded: boolean = false;
constructor(private $state: any, private passwordGenerationService: PasswordGenerationService,
private toastr: any, private $analytics: any, private i18nService: any) {
this.i18n = i18nService;
passwordGenerationService.getHistory().then((history) => {
this.history = history;
this.loaded = true;
});
}
$onInit() {
const params = this.$transition$.params('to');
this.addState = params.addState;
this.editState = params.editState;
}
clear() {
this.history = [];
this.passwordGenerationService.clear();
}
clipboardError(e: any, password: any) {
this.toastr.info(this.i18nService.browserNotSupportClipboard);
}
clipboardSuccess(e: any) {
this.$analytics.eventTrack('Copied Historical Password');
e.clearSelection();
this.toastr.info(this.i18nService.passwordCopied);
}
close() {
this.$state.go('^.passwordGenerator', {
animation: 'out-slide-right',
addState: this.addState,
editState: this.editState,
});
}
}
PasswordGeneratorHistoryController.$inject = ['$state', 'passwordGenerationService', 'toastr', '$analytics',
'i18nService'];
export const PasswordGeneratorHistoryComponent = {
bindings: {
$transition$: '<',
},
controller: PasswordGeneratorHistoryController,
template: template,
};

View File

@@ -1,90 +0,0 @@
<div class="header">
<div class="left">
<a ng-click="$ctrl.close()" href="">{{$ctrl.i18n.close}}</a>
</div>
<div class="right">
<a ng-click="$ctrl.select()" ng-show="$ctrl.showSelect" href="">{{$ctrl.i18n.select}}</a>
</div>
<div class="title">{{$ctrl.i18n.generatePassword}}</div>
</div>
<div class="content">
<div class="generate-password-block">
{{$ctrl.password}}
</div>
<div class="list" style="margin-top: 0;">
<div class="list-section" style="padding-top: 0;">
<div class="list-section-items">
<a class="list-section-item text-primary" href="" ng-click="$ctrl.regenerate(true)">
{{$ctrl.i18n.regeneratePassword}}
</a>
<a class="list-section-item text-primary" href="" ngclipboard ngclipboard-error="$ctrl.clipboardError(e)"
ngclipboard-success="$ctrl.clipboardSuccess(e)" data-clipboard-text="{{$ctrl.password}}">
{{$ctrl.i18n.copyPassword}}
</a>
<a class="list-section-item text-primary" href="" ng-click="$ctrl.goHistory()">
{{$ctrl.i18n.passwordHistory}}
<i class="fa fa-chevron-right fa-lg"></i>
</a>
</div>
</div>
<div class="list-section">
<div class="list-section-header">
{{$ctrl.i18n.options}}
</div>
<div class="list-section-items">
<div class="list-section-item list-section-item-slider">
<label for="length">{{$ctrl.i18n.length}}</label>
<span class="slider-value">{{$ctrl.options.length}}</span>
<div class="slider-wrapper">
<input id="length" type="range" min="5" max="128" step="1" ng-model="$ctrl.options.length"
ng-change="$ctrl.sliderMoved()">
</div>
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="uppercase">A-Z</label>
<input id="uppercase" type="checkbox" ng-model="$ctrl.options.uppercase"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="lowercase">a-z</label>
<input id="lowercase" type="checkbox" ng-model="$ctrl.options.lowercase"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="numbers">0-9</label>
<input id="numbers" type="checkbox" ng-model="$ctrl.options.number"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
<div class="list-section-item list-section-item-checkbox">
<label for="special">!@#$%^&*</label>
<input id="special" type="checkbox" ng-model="$ctrl.options.special"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-input">
<label for="min-numbers">{{$ctrl.i18n.minNumbers}}</label>
<input id="min-numbers" type="number" min="0" max="5" ng-model="$ctrl.options.minNumber"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
<div class="list-section-item list-section-item-input">
<label for="min-special">{{$ctrl.i18n.minSpecial}}</label>
<input id="min-special" type="number" min="0" max="5" ng-model="$ctrl.options.minSpecial"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
</div>
</div>
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="ambiguous">{{$ctrl.i18n.avoidAmbChar}}</label>
<input id="ambiguous" type="checkbox" ng-model="$ctrl.options.ambiguous"
ng-true-value="false" ng-false-value="true"
ng-change="$ctrl.saveOptions($ctrl.options)">
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,147 +0,0 @@
import * as angular from 'angular';
import * as template from './password-generator.component.html';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PopupUtilsService } from '../services/popupUtils.service';
export class PasswordGeneratorController {
$transition$: any;
options: any;
showSelect: boolean;
password: string = '-';
editState: any;
addState: any;
i18n: any;
constructor(private $state: any, private passwordGenerationService: PasswordGenerationService,
private toastr: any, private $analytics: any, private i18nService: any, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService;
passwordGenerationService.getOptions().then((options: any) => {
this.options = options;
this.regenerate(false);
$analytics.eventTrack('Generated Password');
passwordGenerationService.addHistory(this.password);
});
// Save password once the slider stop moving.
document.querySelector('#length').addEventListener('change', (e) => {
e.preventDefault();
$analytics.eventTrack('Generated Password');
this.saveOptions(this.options, false);
passwordGenerationService.addHistory(this.password);
});
}
$onInit() {
const params = this.$transition$.params('to');
this.addState = params.addState;
this.editState = params.editState;
this.showSelect = this.addState || this.editState;
this.$timeout(() => {
PopupUtilsService.initListSectionItemListeners(document, angular);
}, 500);
}
sliderMoved() {
this.regenerate(false);
}
regenerate(trackEvent: any) {
this.password = this.passwordGenerationService.generatePassword(this.options);
if (trackEvent) {
this.$analytics.eventTrack('Regenerated Password');
this.passwordGenerationService.addHistory(this.password);
}
}
saveOptions(options: any, regenerate: boolean = true) {
if (!options.uppercase && !options.lowercase && !options.number && !options.special) {
options.lowercase = this.options.lowercase = true;
}
if (!options.minNumber) {
options.minNumber = this.options.minNumber = 0;
}
if (!options.minSpecial) {
options.minSpecial = this.options.minSpecial = 0;
}
this.passwordGenerationService.saveOptions(options);
if (regenerate) {
this.regenerate(false);
}
return true;
}
clipboardError(e: any, password: any) {
this.toastr.info(this.i18nService.browserNotSupportClipboard);
}
clipboardSuccess(e: any) {
this.$analytics.eventTrack('Copied Generated Password');
e.clearSelection();
this.toastr.info(this.i18nService.passwordCopied);
}
close() {
this.dismiss();
}
select() {
this.$analytics.eventTrack('Selected Generated Password');
if (this.addState) {
this.addState.cipher.login.password = this.password;
} else if (this.editState) {
this.editState.cipher.login.password = this.password;
}
this.dismiss();
}
goHistory() {
this.$state.go('^.passwordGeneratorHistory', {
animation: 'in-slide-left',
addState: this.addState,
editState: this.editState,
});
}
private dismiss() {
if (this.addState) {
this.$state.go('addCipher', {
animation: 'out-slide-down',
from: this.addState.from,
cipher: this.addState.cipher,
});
} else if (this.editState) {
this.$state.go('editCipher', {
animation: 'out-slide-down',
cipher: this.editState.cipher,
fromView: this.editState.fromView,
cipherId: this.editState.cipherId,
from: this.editState.from,
});
} else {
this.$state.go('tabs.tools', {
animation: 'out-slide-down',
});
}
}
}
PasswordGeneratorController.$inject = ['$state', 'passwordGenerationService', 'toastr', '$analytics', 'i18nService',
'$timeout'];
export const PasswordGeneratorComponent = {
bindings: {
$transition$: '<',
},
controller: PasswordGeneratorController,
template: template,
};

View File

@@ -1,47 +0,0 @@
<div class="header">
<pop-out ng-if="$ctrl.showPopout" class="left"></pop-out>
<div class="title">{{$ctrl.i18n.tools}}</div>
</div>
<div class="content content-tabs">
<div class="list">
<div class="list-section">
<div class="list-section-items">
<a class="list-section-item wrap" ui-sref="passwordGenerator({animation: 'in-slide-up'})">
<span class="leading-icon" style="color: #eba776;"><i class="fa fa-refresh fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.passGen}}</span>
<span class="detail">{{$ctrl.i18n.passGenInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ng-click="$ctrl.launchWebVault()">
<span class="leading-icon" style="color: #5bb630;"><i class="fa fa-globe fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.bitWebVault}}</span>
<span class="detail">{{$ctrl.i18n.bitWebVaultInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ng-click="$ctrl.launchiOS()">
<span class="leading-icon" style="color: #999999;"><i class="fa fa-apple fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.bitIosVault}}</span>
<span class="detail">{{$ctrl.i18n.bitIosVaultInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ng-click="$ctrl.launchAndroid()">
<span class="leading-icon" style="color: #a4c639;"><i class="fa fa-android fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.bitAndrVault}}</span>
<span class="detail">{{$ctrl.i18n.bitAndrVaultInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ng-click="$ctrl.launchWebVault(true)">
<span class="leading-icon" style="color: #8977af;"><i class="fa fa-share-alt fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.shareVault}}</span>
<span class="detail">{{$ctrl.i18n.shareVaultInfo}}</span>
</a>
<a class="list-section-item wrap" href="" ng-click="$ctrl.launchImport()">
<span class="leading-icon" style="color: #6fc2ff;"><i class="fa fa-cloud-upload fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.importItems}}</span>
<span class="detail">{{$ctrl.i18n.importItemsInfo}}</span>
</a>
<a class="list-section-item wrap" ui-sref="export({animation: 'in-slide-up'})" ng-if="$ctrl.showExport">
<span class="leading-icon" style="color: #ff6f6f;"><i class="fa fa-cloud-download fa-fw"></i></span>
<span class="text">{{$ctrl.i18n.exportVault}}</span>
<span class="detail">{{$ctrl.i18n.exportVaultInfo}}</span>
</a>
</div>
</div>
</div>
</div>

View File

@@ -1,64 +0,0 @@
import * as template from './tools.component.html';
import { BrowserApi } from '../../../browser/browserApi';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
export class ToolsController {
showExport: boolean;
showPopout: boolean = true;
i18n: any;
private webVaultBaseUrl: string = 'https://vault.bitwarden.com';
constructor(private SweetAlert: any, private i18nService: any, private $analytics: any,
private platformUtilsService: PlatformUtilsService, private environmentService: EnvironmentService) {
this.i18n = i18nService;
this.showExport = !platformUtilsService.isEdge();
this.showPopout = !platformUtilsService.isSafari();
if (environmentService.baseUrl) {
this.webVaultBaseUrl = environmentService.baseUrl;
} else if (environmentService.webVaultUrl) {
this.webVaultBaseUrl = environmentService.webVaultUrl;
}
}
launchWebVault(createOrg: any) {
this.$analytics.eventTrack('Launch Web Vault' + (createOrg ? ' For Share' : ''));
BrowserApi.createNewTab(this.webVaultBaseUrl + '/#/' + (createOrg ? '?org=free' : ''));
}
launchAndroid() {
this.$analytics.eventTrack('Launch Android');
BrowserApi.createNewTab('https://play.google.com/store/apps/details?id=com.x8bit.bitwarden');
}
launchiOS() {
this.$analytics.eventTrack('Launch iOS');
BrowserApi.createNewTab('https://itunes.apple.com/us/app/bitwarden-free-password-manager/' +
'id1137397744?mt=8');
}
launchImport() {
this.SweetAlert.swal({
title: this.i18nService.importItems,
text: this.i18nService.importItemsConfirmation,
showCancelButton: true,
confirmButtonText: this.i18nService.yes,
cancelButtonText: this.i18nService.cancel,
}, (confirmed: boolean) => {
if (confirmed) {
this.$analytics.eventTrack('Launch Web Vault For Import');
BrowserApi.createNewTab('https://help.bitwarden.com/article/import-data/');
}
});
}
}
ToolsController.$inject = ['SweetAlert', 'i18nService', '$analytics', 'platformUtilsService', 'environmentService'];
export const ToolsComponent = {
bindings: {},
controller: ToolsController,
template: template,
};

View File

@@ -1,15 +0,0 @@
import * as angular from 'angular';
import { ExportComponent } from './export.component';
import { PasswordGeneratorHistoryComponent } from './password-generator-history.component';
import { PasswordGeneratorComponent } from './password-generator.component';
import { ToolsComponent } from './tools.component';
export default angular
.module('bit.tools', ['ngAnimate', 'ngclipboard', 'toastr', 'oitozero.ngSweetAlert'])
.component('tools', ToolsComponent)
.component('passwordGeneratorHistory', PasswordGeneratorHistoryComponent)
.component('passwordGenerator', PasswordGeneratorComponent)
.component('export', ExportComponent)
.name;

Some files were not shown because too many files have changed in this diff Show More