diff --git a/src/app/accounts/environment.component.html b/src/app/accounts/environment.component.html new file mode 100644 index 00000000..18ad462a --- /dev/null +++ b/src/app/accounts/environment.component.html @@ -0,0 +1,59 @@ + diff --git a/src/app/accounts/environment.component.ts b/src/app/accounts/environment.component.ts new file mode 100644 index 00000000..2f769434 --- /dev/null +++ b/src/app/accounts/environment.component.ts @@ -0,0 +1,62 @@ +import * as template from './environment.component.html'; + +import { + Component, + EventEmitter, + Output, +} from '@angular/core'; + +import { Angulartics2 } from 'angulartics2'; +import { ToasterService } from 'angular2-toaster'; + +import { EnvironmentService } from 'jslib/abstractions/environment.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +@Component({ + selector: 'app-environment', + template: template, +}) +export class EnvironmentComponent { + iconsUrl: string; + identityUrl: string; + apiUrl: string; + webVaultUrl: string; + baseUrl: string; + showCustom = false; + + @Output() onSaved = new EventEmitter(); + + constructor(private analytics: Angulartics2, private toasterService: ToasterService, + private environmentService: EnvironmentService, private i18nService: I18nService) { + this.baseUrl = environmentService.baseUrl || ''; + this.webVaultUrl = environmentService.webVaultUrl || ''; + this.apiUrl = environmentService.apiUrl || ''; + this.identityUrl = environmentService.identityUrl || ''; + this.iconsUrl = environmentService.iconsUrl || ''; + } + + async submit() { + const resUrls = await this.environmentService.setUrls({ + base: this.baseUrl, + api: this.apiUrl, + identity: this.identityUrl, + webVault: this.webVaultUrl, + icons: this.iconsUrl, + }); + + // 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.next({ action: 'Set Environment URLs' }); + this.toasterService.popAsync('success', null, this.i18nService.t('environmentSaved')); + this.onSaved.emit(); + } + + toggleCustom() { + this.showCustom = !this.showCustom; + } +} diff --git a/src/app/accounts/login.component.html b/src/app/accounts/login.component.html index 682f6eed..f82c91e3 100644 --- a/src/app/accounts/login.component.html +++ b/src/app/accounts/login.component.html @@ -28,8 +28,9 @@
{{'getMasterPasswordHint' | i18n}}
- +  {{'settings' | i18n}} + diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index f2336097..ba3f4f02 100644 --- a/src/app/accounts/login.component.ts +++ b/src/app/accounts/login.component.ts @@ -2,6 +2,9 @@ import * as template from './login.component.html'; import { Component, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, } from '@angular/core'; import { Router } from '@angular/router'; @@ -9,6 +12,9 @@ import { Router } from '@angular/router'; import { Angulartics2 } from 'angulartics2'; import { ToasterService } from 'angular2-toaster'; +import { EnvironmentComponent } from './environment.component'; +import { ModalComponent } from '../modal.component'; + import { AuthResult } from 'jslib/models/domain/authResult'; import { AuthService } from 'jslib/abstractions/auth.service'; @@ -19,12 +25,15 @@ import { I18nService } from 'jslib/abstractions/i18n.service'; template: template, }) export class LoginComponent { + @ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef; + email: string = ''; masterPassword: string = ''; formPromise: Promise; constructor(private authService: AuthService, private router: Router, private analytics: Angulartics2, - private toasterService: ToasterService, private i18nService: I18nService) { } + private toasterService: ToasterService, private i18nService: I18nService, + private componentFactoryResolver: ComponentFactoryResolver) { } async submit() { if (this.email == null || this.email === '') { @@ -56,4 +65,15 @@ export class LoginComponent { } } catch { } } + + settings() { + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + const modal = this.environmentModal.createComponent(factory).instance; + const childComponent = modal.show(EnvironmentComponent, + this.environmentModal); + + childComponent.onSaved.subscribe(() => { + modal.close(); + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ebc3e398..7d0c4371 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,6 +18,7 @@ import { AutofocusDirective } from './directives/autofocus.directive'; import { BlurClickDirective } from './directives/blur-click.directive'; import { BoxRowDirective } from './directives/box-row.directive'; import { CiphersComponent } from './vault/ciphers.component'; +import { EnvironmentComponent } from './accounts/environment.component'; import { FallbackSrcDirective } from './directives/fallback-src.directive'; import { FolderAddEditComponent } from './vault/folder-add-edit.component'; import { GroupingsComponent } from './vault/groupings.component'; @@ -58,6 +59,7 @@ import { ViewComponent } from './vault/view.component'; BlurClickDirective, BoxRowDirective, CiphersComponent, + EnvironmentComponent, FallbackSrcDirective, FolderAddEditComponent, GroupingsComponent, @@ -78,6 +80,7 @@ import { ViewComponent } from './vault/view.component'; ], entryComponents: [ AttachmentsComponent, + EnvironmentComponent, FolderAddEditComponent, ModalComponent, PasswordGeneratorComponent, diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index bdc2b77b..d21d40cf 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -537,5 +537,35 @@ }, "twoStepOptions": { "message": "Two-step Login Options" + }, + "selfHostedEnvironment": { + "message": "Self-hosted Environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premise hosted bitwarden installation." + }, + "customEnvironment": { + "message": "Custom Environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API Server URL" + }, + "webVaultUrl": { + "message": "Web Vault Server URL" + }, + "identityUrl": { + "message": "Identity Server URL" + }, + "iconsUrl": { + "message": "Icons Server URL" + }, + "environmentSaved": { + "message": "The environment URLs have been saved." } }