Skip to content

Commit

Permalink
Merge pull request #520 from Senyoret1/pass
Browse files Browse the repository at this point in the history
Add password options to the UI of the proxy and vpn client apps
  • Loading branch information
jdknives authored Sep 16, 2020
2 parents 477d30c + ddf507e commit 68d6e59
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 9 deletions.
4 changes: 4 additions & 0 deletions static/skywire-manager-src/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ import {
import {
SkysocksClientFilterComponent
} from './components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-filter/skysocks-client-filter.component';
import {
SkysocksClientPasswordComponent
} from './components/pages/node/apps/node-apps/skysocks-client-settings/skysocks-client-password/skysocks-client-password.component';
import { FiltersSelectionComponent } from './components/layout/filters-selection/filters-selection.component';
import { LabeledElementTextComponent } from './components/layout/labeled-element-text/labeled-element-text.component';
import { AllLabelsComponent } from './components/pages/settings/all-labels/all-labels.component';
Expand Down Expand Up @@ -144,6 +147,7 @@ const globalRippleConfig: RippleGlobalOptions = {
UpdaterConfigComponent,
EditSkysocksClientNoteComponent,
SkysocksClientFilterComponent,
SkysocksClientPasswordComponent,
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<app-dialog [headline]="'apps.vpn-socks-client-settings.password-dialog.title' | translate">
<form [formGroup]="form">
<div class="info">{{ 'apps.vpn-socks-client-settings.password-dialog.info' | translate }}</div>
<mat-form-field>
<input
#firstInput
[placeholder]="'apps.vpn-socks-client-settings.password-dialog.password' | translate"
type="password"
id="password"
formControlName="password"
maxlength="100"
matInput
>
</mat-form-field>
<app-button class="float-right" color="primary" type="mat-raised-button" (action)="finish()">
{{ 'apps.vpn-socks-client-settings.password-dialog.continue-button' | translate }}
</app-button>
</form>
</app-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import "variables";

.info {
font-size: $font-size-mini;
margin-bottom: 15px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { MatDialogRef, MatDialogConfig, MatDialog } from '@angular/material/dialog';
import { FormGroup, FormBuilder } from '@angular/forms';

import { AppConfig } from 'src/app/app.config';

/**
* Modal window for entering the password for connecting to a backend shown by the history of
* SkysocksClientSettingsComponent. If the user presses the continue button, the modal window
* is closed and the password is returned in the "afterClosed" envent, but with an hyphen "-"
* added to the begining, to help avoiding problems while checking empty strings.
*/
@Component({
selector: 'app-skysocks-client-password',
templateUrl: './skysocks-client-password.component.html',
styleUrls: ['./skysocks-client-password.component.scss']
})
export class SkysocksClientPasswordComponent implements OnInit {
@ViewChild('firstInput', { static: false }) firstInput: ElementRef;

form: FormGroup;

/**
* Opens the modal window. Please use this function instead of opening the window "by hand".
*/
public static openDialog(dialog: MatDialog): MatDialogRef<SkysocksClientPasswordComponent, any> {
const config = new MatDialogConfig();
config.autoFocus = false;
config.width = AppConfig.smallModalWidth;

return dialog.open(SkysocksClientPasswordComponent, config);
}

constructor(
private dialogRef: MatDialogRef<SkysocksClientPasswordComponent>,
private formBuilder: FormBuilder,
) { }

ngOnInit() {
this.form = this.formBuilder.group({
'password': [''],
});

setTimeout(() => (this.firstInput.nativeElement as HTMLElement).focus());
}

// Closes the modal window and returns the password.
finish() {
const password = this.form.get('password').value;
this.dialogRef.close('-' + password);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@
{{ 'apps.vpn-socks-client-settings.remote-key-chars-error' | translate }}
</ng-template>
</mat-form-field>

<mat-form-field *ngIf="configuringVpn">
<input
id="password"
type="password"
formControlName="password"
maxlength="100"
[placeholder]="'apps.vpn-socks-client-settings.password' | translate"
matInput
>
</mat-form-field>

<div class="password-history-warning" *ngIf="form && form.get('password').value">
<mat-icon [inline]="true">warning</mat-icon>
{{ 'apps.vpn-socks-client-settings.password-history-warning' | translate }}
</div>

<app-button
#button
Expand Down Expand Up @@ -79,7 +95,7 @@
<button
mat-button
class="list-button grey-button-background w-100"
(click)="saveChanges(proxy.pk, false, proxy.location)"
(click)="saveChanges(proxy.pk, null, false, proxy.location)"
>
<div class="button-content">
<div class="item">
Expand Down Expand Up @@ -133,7 +149,7 @@
<button
mat-button
class="list-button grey-button-background w-100 d-none d-md-inline"
(click)="saveChanges(entry.key, entry.enteredManually, entry.location, entry.note)"
(click)="useFronHistory(entry)"
>
<ng-container *ngTemplateOutlet="content"></ng-container>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ form {
height: 100px;
}

.password-history-warning {
font-size: $font-size-mini;
opacity: 0.7;
position: relative;
top: -5px;
}

.list-button {
border-bottom: solid 1px $grey-separator;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
FilterWindowData
} from './skysocks-client-filter/skysocks-client-filter.component';
import { countriesList } from 'src/app/utils/countries-list';
import { SkysocksClientPasswordComponent } from './skysocks-client-password/skysocks-client-password.component';

/**
* Data of the entries from the history.
Expand All @@ -43,6 +44,10 @@ export interface HistoryEntry {
* Custom note added by the user.
*/
note?: string;
/**
* If the user entered a password.
*/
hasPassword?: boolean;
}

/**
Expand Down Expand Up @@ -168,6 +173,7 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
Validators.maxLength(66),
Validators.pattern('^[0-9a-fA-F]+$')])
],
'password': ['', Validators.maxLength(100)]
});

setTimeout(() => (this.firstInput.nativeElement as HTMLElement).focus());
Expand Down Expand Up @@ -355,7 +361,7 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {

SelectOptionComponent.openDialog(this.dialog, options, 'common.options').afterClosed().subscribe((selectedOption: number) => {
if (selectedOption === 1) {
this.saveChanges(historyEntry.key, historyEntry.enteredManually, historyEntry.location, historyEntry.note);
this.useFromHistory(historyEntry);
} else if (selectedOption === 2) {
this.changeNote(historyEntry);
} else if (selectedOption === 3) {
Expand Down Expand Up @@ -406,51 +412,90 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
});
}

/**
* Makes the app use the data from a history entry.
* @param entry Entry to be used.
*/
useFromHistory(entry: HistoryEntry) {
// If the entry was created without a password, use it inmediatelly.
if (!entry.hasPassword) {
this.saveChanges(entry.key, null, entry.enteredManually, entry.location, entry.note);
} else {
// If the entry was created with a password, ask for it.
SkysocksClientPasswordComponent.openDialog(this.dialog).afterClosed().subscribe((response: string) => {
if (response) {
// Remove the "-" char the modal window adds at the start of the password.
response = response.substr(1, response.length - 1);

this.saveChanges(entry.key, response, entry.enteredManually, entry.location, entry.note);
}
});
}
}

/**
* Saves the settings. If no argument is provided, the function will take the public key
* from the form and fill the rest of the data. The arguments are mainly for elements selected
* from the discovery list and entries from the history.
* @param publicKey New public key to be used.
* @param password New password to be used.
* @param enteredManually If the user manually entered the data using the form.
* @param location Location of the server.
* @param note Personal note for the history.
*/
saveChanges(publicKey: string = null, enteredManually: boolean = null, location: string = null, note: string = null) {
saveChanges(
publicKey: string = null,
password: string = null,
enteredManually: boolean = null,
location: string = null,
note: string = null
) {

// If no public key was provided, the data will be retrieved from the form, so the form
// must be valid. Also, the operation can not continue if the component is already working.
if ((!this.form.valid && !publicKey) || this.working) {
return;
}

enteredManually = publicKey ? enteredManually : true;
password = publicKey ? password : this.form.get('password').value;
publicKey = publicKey ? publicKey : this.form.get('pk').value;

// Ask for confirmation.
const confirmationMsg = 'apps.vpn-socks-client-settings.change-key-confirmation';
const confirmationDialog = GeneralUtils.createConfirmationDialog(this.dialog, confirmationMsg);
confirmationDialog.componentInstance.operationAccepted.subscribe(() => {
confirmationDialog.close();
this.continueSavingChanges(publicKey, enteredManually, location, note);
this.continueSavingChanges(publicKey, password, enteredManually, location, note);
});
}

// Makes the call to the hypervisor API for changing the configuration.
private continueSavingChanges(publicKey: string, enteredManually: boolean, location: string, note: string) {
private continueSavingChanges(publicKey: string, password: string, enteredManually: boolean, location: string, note: string) {
this.button.showLoading();
this.working = true;

const data = { pk: publicKey };
if (this.configuringVpn) {
if (password) {
data['passcode'] = password;
} else {
data['passcode'] = '';
}
}

this.operationSubscription = this.appsService.changeAppSettings(
// The node pk is obtained from the currently openned node page.
NodeComponent.getCurrentNodeKey(),
this.data.name,
{ pk: publicKey },
data,
).subscribe(
() => this.onSuccess(publicKey, enteredManually, location, note),
() => this.onSuccess(publicKey, !!password, enteredManually, location, note),
err => this.onError(err),
);
}

private onSuccess(publicKey: string, enteredManually: boolean, location: string, note: string) {
private onSuccess(publicKey: string, hasPassword: boolean, enteredManually: boolean, location: string, note: string) {
// Remove any repeated entry from the history.
this.history = this.history.filter(value => value.key !== publicKey);

Expand All @@ -459,6 +504,9 @@ export class SkysocksClientSettingsComponent implements OnInit, OnDestroy {
key: publicKey,
enteredManually: enteredManually,
};
if (hasPassword) {
newEntry.hasPassword = hasPassword;
}
if (location) {
newEntry.location = location;
}
Expand Down
9 changes: 9 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@
"state-available": "Available",
"state-offline": "Offline",
"public-key": "Remote visor public key",
"password": "Password",
"password-history-warning": "Note: the password will not be saved in the history.",
"no-elements": "Currently there are no elements to show. Please try again later.",
"no-elements-for-filters": "There are no elements that meet the filter criteria.",
"no-filter": "No filter has been selected",
Expand All @@ -384,6 +386,13 @@
"note": "Note"
},

"password-dialog": {
"title": "Enter Password",
"password": "Password",
"info": "You are being asked for a password because a password was set when the selected entry was created, but the it was not saved for security reasons. You can leave the password empty if needed.",
"continue-button": "Continue"
},

"filter-dialog": {
"title": "Filters",
"country": "The country must be",
Expand Down
9 changes: 9 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@
"state-available": "Disponible",
"state-offline": "Offline",
"public-key": "Llave pública del visor remoto",
"password": "Contraseña",
"password-history-warning": "Nota: la contraseña no se guardará en el historial.",
"no-elements": "Actualmente no hay elementos para mostrar. Por favor, inténtelo de nuevo más tarde.",
"no-elements-for-filters": "No hay elementos que cumplan los criterios de filtro.",
"no-filter": "No se ha seleccionado ningún filtro",
Expand All @@ -382,6 +384,13 @@
"note": "Nota"
},

"password-dialog": {
"title": "Introducir Contraseña",
"password": "Contraseña",
"info": "Se le solicita una contraseña porque una contraseña fue utilizada cuando se creó la entrada seleccionada, pero no fue guardada por razones de seguridad. Puede dejar la contraseña vacía si es necesario.",
"continue-button": "Continuar"
},

"filter-dialog": {
"title": "Filtros",
"country": "El país debe ser",
Expand Down
9 changes: 9 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/es_base.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@
"state-available": "Available",
"state-offline": "Offline",
"public-key": "Remote visor public key",
"password": "Password",
"password-history-warning": "Note: the password will not be saved in the history.",
"no-elements": "Currently there are no elements to show. Please try again later.",
"no-elements-for-filters": "There are no elements that meet the filter criteria.",
"no-filter": "No filter has been selected",
Expand All @@ -382,6 +384,13 @@
"note": "Note"
},

"password-dialog": {
"title": "Enter Password",
"password": "Password",
"info": "You are being asked for a password because a password was set when the selected entry was created, but the it was not saved for security reasons. You can leave the password empty if needed.",
"continue-button": "Continue"
},

"filter-dialog": {
"title": "Filters",
"country": "The country must be",
Expand Down

0 comments on commit 68d6e59

Please sign in to comment.