1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-1019] Environment selection clients (#5480)

* [PM-169][PM-142][PM-191] Add Environments to Web and Desktop (#5294)

* [PM-1351] Add property to server-config.response. Change config to be able to fetch without being authed.

* [PM-1351] fetch every hour.

* [PM-1351] fetch on vault sync.

* [PM-1351] browser desktop fetch configs on sync complete.

* [PM-1351] Add methods to retrieve feature flags

* [PM-1351] Add enum to use as key to get values feature flag values

* [PM-1351] Remove debug code

* [PM-1351] Get flags when unauthed. Add enums as params. Hourly always fetch.

* [PM-1351] add check for authed user using auth service

* [PM-169] Web: add drop down to select environment

* [PM-169] Fix pop up menu margins. Add DisplayEuEnvironmentFlag.

* [PM-169] Change menu name.

* [PM-169] Add environment selector ts and html. Add declaration and import on login.module

* [PM-169] Add environment selector to desktop.

* [PM-169] Ignore lint error.

* [PM-169] add takeUntil to subscribes

* [PM-191] PR Fixes, code format

* [PM-168] Add Environments to extension login/registration (#5434)
This commit is contained in:
André Bispo
2023-05-19 17:35:42 +01:00
committed by GitHub
parent 6383d6ff8a
commit b9fe78796a
28 changed files with 463 additions and 42 deletions

View File

@@ -2221,7 +2221,18 @@
}
}
},
"region": {
"message": "Region"
},
"opensInANewWindow": {
"message": "Opens in a new window"
},
"eu": {
"message": "EU",
"description": "European Union"
},
"us": {
"message": "US",
"description": "United States"
}
}

View File

@@ -2221,6 +2221,9 @@
}
}
},
"region": {
"message": "Region"
},
"opensInANewWindow": {
"message": "Opens in a new window"
}

View File

@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { EnvironmentComponent as BaseEnvironmentComponent } from "@bitwarden/angular/components/environment.component";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
@@ -18,9 +19,10 @@ export class EnvironmentComponent extends BaseEnvironmentComponent implements On
platformUtilsService: PlatformUtilsService,
public environmentService: BrowserEnvironmentService,
i18nService: I18nService,
private router: Router
private router: Router,
modalService: ModalService
) {
super(platformUtilsService, environmentService, i18nService);
super(platformUtilsService, environmentService, i18nService, modalService);
this.showCustom = true;
}

View File

@@ -9,9 +9,7 @@
<label for="email">{{ "emailAddress" | i18n }}</label>
<input id="email" type="email" formControlName="email" appInputVerbatim="false" />
</div>
<div class="box-footer no-margin" *ngIf="selfHostedDomain">
{{ "loggingInTo" | i18n : selfHostedDomain }}
</div>
<environment-selector class="environment-selector-padding"></environment-selector>
<div class="remember-email-check">
<input
id="rememberEmail"
@@ -35,6 +33,3 @@
</p>
</div>
</div>
<button type="button" routerLink="/environment" class="settings-icon" (click)="setFormValues()">
<i class="bwi bwi-cog-f bwi-lg" aria-hidden="true"></i><span>&nbsp;{{ "settings" | i18n }}</span>
</button>

View File

@@ -1,7 +1,9 @@
import { Component, OnInit } from "@angular/core";
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
@@ -12,9 +14,12 @@ import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"
selector: "app-home",
templateUrl: "home.component.html",
})
export class HomeComponent implements OnInit {
loginInitiated = false;
export class HomeComponent implements OnInit, OnDestroy {
@ViewChild(EnvironmentSelectorComponent, { static: true })
environmentSelector!: EnvironmentSelectorComponent;
private destroyed$: Subject<void> = new Subject();
loginInitiated = false;
formGroup = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
rememberEmail: [false],
@@ -27,9 +32,9 @@ export class HomeComponent implements OnInit {
private router: Router,
private i18nService: I18nService,
private environmentService: EnvironmentService,
private route: ActivatedRoute,
private loginService: LoginService
) {}
async ngOnInit(): Promise<void> {
let savedEmail = this.loginService.getEmail();
const rememberEmail = this.loginService.getRememberEmail();
@@ -48,6 +53,18 @@ export class HomeComponent implements OnInit {
});
}
}
this.environmentSelector.onOpenSelfHostedSettings
.pipe(takeUntil(this.destroyed$))
.subscribe(() => {
this.setFormValues();
this.router.navigate(["environment"]);
});
}
ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
submit() {

View File

@@ -10,6 +10,7 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
import { BitwardenToastModule } from "@bitwarden/angular/components/toastr.component";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe";
@@ -153,6 +154,7 @@ import "./locales";
AboutComponent,
HelpAndFeedbackComponent,
AutofillComponent,
EnvironmentSelectorComponent,
],
providers: [CurrencyPipe, DatePipe],
bootstrap: [AppComponent],

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -45,3 +45,67 @@ html.browser_safari {
border-color: #2e3440;
}
}
.environment-selector-btn {
font-size: $font-size-small;
color: $text-muted;
line-height: 25px;
font-weight: 400;
padding-left: 5px;
label {
font-weight: 600;
}
a,
a label:hover {
cursor: pointer;
}
}
.environment-selector-dialog {
@include themify($themes) {
background-color: themed("boxBackgroundColor");
}
padding: 5px;
width: 100%;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12),
0 1px 5px 0 rgba(0, 0, 0, 0.2);
border-radius: $border-radius;
}
.environment-selector-dialog-item {
@include themify($themes) {
color: themed("textColor") !important;
}
width: 100%;
text-align: left;
padding: 0px 15px 0px 5px;
border-radius: 3px;
border: 1px solid transparent;
transition: all 0.2s ease-in-out;
&:hover {
@include themify($themes) {
background-color: themed("listItemBackgroundHoverColor") !important;
}
}
img {
margin-bottom: -2px;
width: 22px;
height: 14px;
}
.img-us {
content: url("../images/us-flag.png");
}
.img-eu {
content: url("../images/eu-flag.png");
}
}
.environment-selector-padding {
padding-left: 10px;
}

View File

@@ -144,7 +144,7 @@ body.body-full {
}
.remember-email-check {
padding-top: 8px;
padding-top: 18px;
padding-left: 10px;
padding-bottom: 18px;
}

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core";
import { EnvironmentComponent as BaseEnvironmentComponent } from "@bitwarden/angular/components/environment.component";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
@@ -13,8 +14,9 @@ export class EnvironmentComponent extends BaseEnvironmentComponent {
constructor(
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
i18nService: I18nService
i18nService: I18nService,
modalService: ModalService
) {
super(platformUtilsService, environmentService, i18nService);
super(platformUtilsService, environmentService, i18nService, modalService);
}
}

View File

@@ -1,16 +1,4 @@
<div id="login-page">
<div class="login-header">
<button
type="button"
appStopClick
(click)="settings()"
class="environment-urls-settings-icon"
attr.aria-label="{{ 'settings' | i18n }}"
>
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
{{ "settings" | i18n }}
</button>
</div>
<div id="login-page" class="page-top-padding">
<form
id="login-page"
#form
@@ -19,7 +7,7 @@
[formGroup]="formGroup"
attr.aria-hidden="{{ showingModal }}"
>
<div id="content" class="content">
<div id="content" class="content" style="padding-top: 50px">
<img class="logo-image" alt="Bitwarden" />
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
<!-- start email -->
@@ -37,9 +25,7 @@
/>
</div>
</div>
<div class="box-footer" *ngIf="selfHostedDomain">
{{ "loggingInTo" | i18n : selfHostedDomain }}
</div>
<environment-selector></environment-selector>
</div>
<div class="checkbox remember-email">
<label for="rememberEmail">

View File

@@ -1,7 +1,9 @@
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
@@ -31,7 +33,10 @@ const BroadcasterSubscriptionId = "LoginComponent";
export class LoginComponent extends BaseLoginComponent implements OnDestroy {
@ViewChild("environment", { read: ViewContainerRef, static: true })
environmentModal: ViewContainerRef;
@ViewChild(EnvironmentSelectorComponent)
environmentSelector!: EnvironmentSelectorComponent;
protected componentDestroyed$: Subject<void> = new Subject();
webVaultHostname = "";
showingModal = false;
@@ -116,10 +121,17 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
});
});
this.messagingService.send("getWindowIsFocused");
this.environmentSelector.onOpenSelfHostedSettings
.pipe(takeUntil(this.componentDestroyed$))
.subscribe(() => {
this.settings();
});
}
ngOnDestroy() {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
this.componentDestroyed$.next();
this.componentDestroyed$.complete();
}
async settings() {
@@ -128,17 +140,16 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
this.environmentModal
);
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
modal.onShown.subscribe(() => {
modal.onShown.pipe(takeUntil(this.componentDestroyed$)).subscribe(() => {
this.showingModal = true;
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
modal.onClosed.subscribe(() => {
modal.onClosed.pipe(takeUntil(this.componentDestroyed$)).subscribe(() => {
this.showingModal = false;
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
childComponent.onSaved.subscribe(async () => {
// eslint-disable-next-line rxjs/no-async-subscribe
childComponent.onSaved.pipe(takeUntil(this.componentDestroyed$)).subscribe(async () => {
modal.close();
await this.checkSelfHosted();
});

View File

@@ -1,6 +1,8 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
import { SharedModule } from "../../app/shared/shared.module";
import { LoginWithDeviceComponent } from "./login-with-device.component";
@@ -8,7 +10,7 @@ import { LoginComponent } from "./login.component";
@NgModule({
imports: [SharedModule, RouterModule],
declarations: [LoginComponent, LoginWithDeviceComponent],
declarations: [LoginComponent, LoginWithDeviceComponent, EnvironmentSelectorComponent],
exports: [LoginComponent, LoginWithDeviceComponent],
})
export class LoginModule {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -2251,5 +2251,19 @@
},
"windowsBiometricUpdateWarningTitle": {
"message": "Recommended Settings Update"
},
"region": {
"message": "Region"
},
"eu": {
"message": "EU",
"description": "European Union"
},
"us": {
"message": "US",
"description": "United States"
},
"selfHosted": {
"message": "Self-hosted"
}
}

View File

@@ -38,3 +38,63 @@
padding-top: 0;
}
}
.environment-selector-btn {
font-size: $font-size-small;
color: $text-muted;
line-height: 25px;
font-weight: 400;
padding-left: 5px;
label {
font-weight: 600;
}
a,
a label:hover {
cursor: pointer;
}
}
.environment-selector-dialog {
@include themify($themes) {
background-color: themed("boxBackgroundColor");
}
padding: 5px;
width: 100%;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12),
0 1px 5px 0 rgba(0, 0, 0, 0.2);
border-radius: $border-radius;
}
.environment-selector-dialog-item {
@include themify($themes) {
color: themed("textColor") !important;
}
width: 100%;
text-align: left;
padding: 0px 15px 0px 5px;
border-radius: 3px;
border: 1px solid transparent;
transition: all 0.2s ease-in-out;
&:hover {
@include themify($themes) {
background-color: themed("listItemBackgroundHoverColor") !important;
}
}
img {
margin-bottom: -2px;
width: 22px;
height: 14px;
}
.img-us {
content: url("../images/us-flag.png");
}
.img-eu {
content: url("../images/eu-flag.png");
}
}

View File

@@ -421,6 +421,10 @@ app-root > #loading,
}
}
.page-top-padding {
padding-top: 50px;
}
.logo-image {
@include themify($themes) {
content: url("../images/logo-" + themed("logoSuffix") + "@2x.png");

View File

@@ -1,5 +1,34 @@
<router-outlet></router-outlet>
<div class="container my-5 text-muted text-center">
<div class="tw-mb-1">
<bit-menu #environmentOptions>
<a bitMenuItem href="https://vault.bitwarden.com" class="pr-4">
<i
class="bwi bwi-fw bwi-sm bwi-check pb-1"
aria-hidden="true"
[style.visibility]="isEuServer ? 'hidden' : 'visible'"
></i>
<img src="../../images/us_flag.png" alt="" class="pb-1 mr-1" />
{{ "us" | i18n }}
</a>
<a bitMenuItem href="https://vault.bitwarden.eu" class="pr-4" *ngIf="euServerFlagEnabled">
<i
class="bwi bwi-fw bwi-sm bwi-check pb-1"
aria-hidden="true"
[style.visibility]="isEuServer ? 'visible' : 'hidden'"
></i>
<img src="../../images/eu_flag.png" alt="" class="pb-1 mr-1" />
{{ "eu" | i18n }}
</a>
</bit-menu>
{{ "region" | i18n }}:
<a [routerLink]="[]" [bitMenuTriggerFor]="environmentOptions">
<b>{{ isEuServer ? ("eu" | i18n) : ("us" | i18n) }}</b
><i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i>
</a>
<br />
</div>
&copy; {{ year }} Bitwarden Inc. <br />
{{ "versionNumber" | i18n : version }}
</div>

View File

@@ -1,6 +1,9 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { Utils } from "@bitwarden/common/misc/utils";
@Component({
selector: "app-frontend-layout",
@@ -9,12 +12,22 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti
export class FrontendLayoutComponent implements OnInit, OnDestroy {
version: string;
year = "2015";
isEuServer = true;
euServerFlagEnabled: boolean;
constructor(private platformUtilsService: PlatformUtilsService) {}
constructor(
private platformUtilsService: PlatformUtilsService,
private configService: ConfigServiceAbstraction
) {}
async ngOnInit() {
this.year = new Date().getFullYear().toString();
this.version = await this.platformUtilsService.getApplicationVersion();
this.euServerFlagEnabled = await this.configService.getFeatureFlagBool(
FeatureFlag.DisplayEuEnvironmentFlag
);
this.isEuServer = Utils.getDomain(window.location.href).includes(".eu");
document.body.classList.add("layout_frontend");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -6789,6 +6789,17 @@
"enforceOnLoginDesc": {
"message": "Require existing members to change their passwords"
},
"region": {
"message": "Region"
},
"eu": {
"message": "EU",
"description": "European Union"
},
"us": {
"message": "US",
"description": "United States"
},
"smProjectDeleteAccessRestricted": {
"message": "You don't have permissions to delete this project",
"description": "The individual description shown to the user when the user doesn't have access to delete a project."