diff --git a/jslib b/jslib index 167558168c4..579f970323e 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 167558168c4ddff8eff67d7f12dfac47d5d25866 +Subproject commit 579f970323ea0bbc0d26f17cb9454c142f47bf6a diff --git a/src/popup2/accounts/login.component.html b/src/popup2/accounts/login.component.html index c13e25d3917..bea95b70889 100644 --- a/src/popup2/accounts/login.component.html +++ b/src/popup2/accounts/login.component.html @@ -1,3 +1,43 @@ -
- Login Form + +
+

{{'loginOrCreateNewAccount' | i18n}}

+
+
+
+ + +
+
+
+ + +
+
+ + + +
+
+
+
+
+ + + {{'createAccount' | i18n}} + +
+ + +  {{'settings' | i18n}} + +
diff --git a/src/popup2/accounts/login.component.ts b/src/popup2/accounts/login.component.ts index aa5c52cd878..41c2d82b1ee 100644 --- a/src/popup2/accounts/login.component.ts +++ b/src/popup2/accounts/login.component.ts @@ -22,18 +22,44 @@ import { SyncService } from 'jslib/abstractions/sync.service'; template: template, }) export class LoginComponent { - @ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef; - email: string = ''; masterPassword: string = ''; showPassword: boolean = false; formPromise: Promise; - constructor(private router: Router, private analytics: Angulartics2, - private toasterService: ToasterService) { } + constructor(private authService: AuthService, private router: Router, + private analytics: Angulartics2, private toasterService: ToasterService, + private i18nService: I18nService, private syncService: SyncService) { } async submit() { - + if (this.email == null || this.email === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('emailRequired')); + return; + } + if (this.email.indexOf('@') === -1) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('invalidEmail')); + return; + } + if (this.masterPassword == null || this.masterPassword === '') { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPassRequired')); + return; + } + + try { + this.formPromise = this.authService.logIn(this.email, this.masterPassword); + const response = await this.formPromise; + if (response.twoFactor) { + this.analytics.eventTrack.next({ action: 'Logged In To Two-step' }); + this.router.navigate(['2fa']); + } else { + this.syncService.fullSync(true); + this.analytics.eventTrack.next({ action: 'Logged In' }); + this.router.navigate(['vault']); + } + } catch { } } togglePassword() { diff --git a/src/popup2/app.module.ts b/src/popup2/app.module.ts index 789e71c1ef0..bcedb15a9a1 100644 --- a/src/popup2/app.module.ts +++ b/src/popup2/app.module.ts @@ -17,6 +17,15 @@ import { AppComponent } from './app.component'; import { LoginComponent } from './accounts/login.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 { 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'; + @NgModule({ imports: [ BrowserModule, @@ -32,8 +41,15 @@ import { LoginComponent } from './accounts/login.component'; ToasterModule, ], declarations: [ + ApiActionDirective, AppComponent, + AutofocusDirective, + BlurClickDirective, + FallbackSrcDirective, + I18nPipe, LoginComponent, + StopClickDirective, + StopPropDirective ], entryComponents: [ diff --git a/src/popup2/services/auth-guard.service.ts b/src/popup2/services/auth-guard.service.ts deleted file mode 100644 index bb6cdd2376f..00000000000 --- a/src/popup2/services/auth-guard.service.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - CanActivate, - Router, -} from '@angular/router'; - -import { CryptoService } from 'jslib/abstractions/crypto.service'; -import { MessagingService } from 'jslib/abstractions/messaging.service'; -import { UserService } from 'jslib/abstractions/user.service'; - -@Injectable() -export class AuthGuardService implements CanActivate { - constructor(private cryptoService: CryptoService, private userService: UserService, private router: Router, - private messagingService: MessagingService) { } - - async canActivate() { - const isAuthed = await this.userService.isAuthenticated(); - if (!isAuthed) { - this.messagingService.send('logout'); - return false; - } - - const key = await this.cryptoService.getKey(); - if (key == null) { - this.router.navigate(['lock']); - return false; - } - - return true; - } -} diff --git a/src/popup2/services/broadcaster.service.ts b/src/popup2/services/broadcaster.service.ts deleted file mode 100644 index fe747e7b0b3..00000000000 --- a/src/popup2/services/broadcaster.service.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable() -export class BroadcasterService { - subscribers: Map any> = new Map any>(); - - send(message: any, id?: string) { - if (id != null) { - if (this.subscribers.has(id)) { - this.subscribers.get(id)(message); - } - return; - } - - this.subscribers.forEach((value) => { - value(message); - }); - } - - subscribe(id: string, messageCallback: (message: any) => any) { - if (this.subscribers.has(id)) { - return; - } - - this.subscribers.set(id, messageCallback); - } - - unsubscribe(id: string) { - if (this.subscribers.has(id)) { - this.subscribers.delete(id); - } - } -} diff --git a/src/popup2/services/services.module.ts b/src/popup2/services/services.module.ts index 95f7f77f041..56001d47dc3 100644 --- a/src/popup2/services/services.module.ts +++ b/src/popup2/services/services.module.ts @@ -5,13 +5,59 @@ import { import { ToasterModule } from 'angular2-toaster'; -import { AuthGuardService } from './auth-guard.service'; -import { BroadcasterService } from './broadcaster.service'; -import { ValidationService } from './validation.service'; +import { AuthGuardService } from 'jslib/angular/services/auth-guard.service'; +import { ValidationService } from 'jslib/angular/services/validation.service'; + +import { BrowserApi } from '../../browser/browserApi'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { AppIdService } from 'jslib/abstractions/appId.service'; +import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.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 { MessagingService } from 'jslib/abstractions/messaging.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'; + +import BrowserMessagingService from '../../services/browserMessaging.service'; + +import { AuthService } from 'jslib/services/auth.service'; +import { ConstantsService } from 'jslib/services/constants.service'; + +function getBgService(service: string) { + return (): T => { + const page = BrowserApi.getBackgroundPage(); + return page ? page.bitwardenMain[service] as T : null; + }; +} + +const messagingService = new BrowserMessagingService(getBgService('platformUtilsService')()); +const authService = new AuthService(getBgService('cryptoService')(), + getBgService('apiService')(), getBgService('userService')(), + getBgService('tokenService')(), getBgService('appIdService')(), + getBgService('i18n2Service')(), getBgService('platformUtilsService')(), + getBgService('constantsService')(), messagingService); function initFactory(): Function { return async () => { - + if (getBgService('i18n2Service')() != null) { + authService.init(); + } }; } @@ -23,6 +69,36 @@ function initFactory(): Function { providers: [ ValidationService, AuthGuardService, + { provide: MessagingService, useValue: messagingService }, + { provide: AuthServiceAbstraction, useValue: authService }, + { provide: AuditService, useFactory: getBgService('auditService'), deps: [] }, + { provide: CipherService, useFactory: getBgService('cipherService'), deps: [] }, + { provide: FolderService, useFactory: getBgService('folderService'), deps: [] }, + { provide: CollectionService, useFactory: getBgService('collectionService'), deps: [] }, + { provide: EnvironmentService, useFactory: getBgService('environmentService'), deps: [] }, + { provide: TotpService, useFactory: getBgService('totpService'), deps: [] }, + { provide: TokenService, useFactory: getBgService('tokenService'), deps: [] }, + { provide: I18nService, useFactory: getBgService('i18n2Service'), deps: [] }, + { provide: UtilsService, useFactory: getBgService('utilsService'), deps: [] }, + { provide: CryptoService, useFactory: getBgService('cryptoService'), deps: [] }, + { + provide: PlatformUtilsService, + useFactory: getBgService('platformUtilsService'), + deps: [] + }, + { + provide: PasswordGenerationService, + useFactory: getBgService('passwordGenerationService'), + deps: [] + }, + { provide: ApiService, useFactory: getBgService('apiService'), deps: [] }, + { provide: SyncService, useFactory: getBgService('syncService'), deps: [] }, + { provide: UserService, useFactory: getBgService('userService'), deps: [] }, + { provide: SettingsService, useFactory: getBgService('settingsService'), deps: [] }, + { provide: LockService, useFactory: getBgService('lockService'), deps: [] }, + { provide: StorageService, useFactory: getBgService('storageService'), deps: [] }, + { provide: AppIdService, useFactory: getBgService('appIdService'), deps: [] }, + { provide: AutofillService, useFactory: getBgService('autofillService'), deps: [] }, { provide: APP_INITIALIZER, useFactory: initFactory, diff --git a/src/popup2/services/validation.service.ts b/src/popup2/services/validation.service.ts deleted file mode 100644 index cc7ee9ce65b..00000000000 --- a/src/popup2/services/validation.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { ToasterService } from 'angular2-toaster'; - -import { I18nService } from 'jslib/abstractions/i18n.service'; - -@Injectable() -export class ValidationService { - constructor(private toasterService: ToasterService, private i18nService: I18nService) { } - - showError(data: any): string[] { - const defaultErrorMessage = this.i18nService.t('unexpectedError'); - const errors: string[] = []; - - if (data == null || typeof data !== 'object') { - errors.push(defaultErrorMessage); - } else if (data.validationErrors == null) { - 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 > 0) { - this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), errors[0]); - } - - return errors; - } -} diff --git a/src/services/abstractions/autofill.service.ts b/src/services/abstractions/autofill.service.ts index 5d2b1e44c83..330c2640a77 100644 --- a/src/services/abstractions/autofill.service.ts +++ b/src/services/abstractions/autofill.service.ts @@ -1,7 +1,7 @@ import AutofillPageDetails from '../../models/autofillPageDetails'; -export interface AutofillService { - getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[]; - doAutoFill(options: any): Promise; - doAutoFillForLastUsedLogin(pageDetails: any, fromCommand: boolean): Promise; +export abstract class AutofillService { + getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => any[]; + doAutoFill: (options: any) => Promise; + doAutoFillForLastUsedLogin: (pageDetails: any, fromCommand: boolean) => Promise; } diff --git a/webpack2.js b/webpack2.js index 3f9064a32d3..bd8ae44bfef 100644 --- a/webpack2.js +++ b/webpack2.js @@ -135,13 +135,19 @@ module.exports = { { from: './src/images', to: 'images' }, { from: './src/content/autofill.css', to: 'content' } ]), + new webpack.SourceMapDevToolPlugin({ + filename: '[name].js.map', + include: ['popup/main.js'] + }), extractCss ], resolve: { extensions: ['.tsx', '.ts', '.js'], alias: { - jslib: path.join(__dirname, 'jslib/src') - } + jslib: path.join(__dirname, 'jslib/src'), + }, + symlinks: false, + modules: [path.resolve('node_modules')] }, output: { filename: '[name].js',