before update

This commit is contained in:
VLE2FE 2020-05-20 10:07:34 +02:00
parent 7f47af425d
commit 7cc6147a25
16 changed files with 1324 additions and 1760 deletions

View File

@ -68,7 +68,8 @@
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": { "options": {
"browserTarget": "UI:build" "browserTarget": "UI:build",
"proxyConfig": "src/proxy.conf.json"
}, },
"configurations": { "configurations": {
"production": { "production": {

2869
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,19 +22,20 @@
"@angular/router": "~8.2.14", "@angular/router": "~8.2.14",
"@hapi/joi": "^17.1.1", "@hapi/joi": "^17.1.1",
"@inst-iot/bosch-angular-ui-components": "^0.5.30", "@inst-iot/bosch-angular-ui-components": "^0.5.30",
"angular-2-local-storage": "^3.0.2",
"flatpickr": "^4.6.3", "flatpickr": "^4.6.3",
"rxjs": "~6.4.0", "rxjs": "~6.4.0",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"zone.js": "~0.9.1" "zone.js": "~0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.803.22", "@angular-devkit/build-angular": "^0.803.25",
"@angular/cli": "~8.3.22", "@angular/cli": "~8.3.22",
"@angular/compiler-cli": "~8.2.14", "@angular/compiler-cli": "~8.2.14",
"@angular/language-service": "~8.2.14", "@angular/language-service": "~8.2.14",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8", "@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.0", "codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0", "jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1", "jasmine-spec-reporter": "~4.2.1",

View File

@ -0,0 +1,14 @@
import { TestBed } from '@angular/core/testing';
import { ApiService } from './api.service';
describe('ApiService', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [ApiService, HttpClient]
}));
it('should be created', () => {
const service: ApiService = TestBed.inject(ApiService);
expect(service).toBeTruthy();
});
});

28
src/app/api.service.ts Normal file
View File

@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {LocalStorageService} from 'angular-2-local-storage';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(
private http: HttpClient,
private storage: LocalStorageService
) { }
get(url) {
return this.http.get(url, this.authOptions());
}
private authOptions() {
const auth = this.storage.get('basicAuth');
if (auth) {
return {headers: new HttpHeaders({Authorization: 'Basic ' + auth})};
}
else {
return {};
}
}
}

View File

@ -1,10 +1,15 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import {HomeComponent} from './home/home.component'; import {HomeComponent} from './home/home.component';
import {LoginService} from './login.service';
const routes: Routes = [ const routes: Routes = [
{path: '', component: HomeComponent} {path: '', component: HomeComponent},
{path: 'replace-me', component: HomeComponent, canActivate: [LoginService]},
// if not authenticated
{ path: '**', redirectTo: '' }
]; ];
@NgModule({ @NgModule({

View File

@ -7,6 +7,8 @@ import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import { LoginComponent } from './login/login.component'; import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component'; import { HomeComponent } from './home/home.component';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {LocalStorageModule} from 'angular-2-local-storage';
import {HttpClientModule} from '@angular/common/http';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -15,10 +17,15 @@ import {FormsModule} from '@angular/forms';
HomeComponent HomeComponent
], ],
imports: [ imports: [
LocalStorageModule.forRoot({
prefix: 'dfop',
storageType: 'localStorage'
}),
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
RbUiComponentsModule, RbUiComponentsModule,
FormsModule FormsModule,
HttpClientModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { LoginService } from './login.service';
describe('LoginService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: LoginService = TestBed.inject(LoginService);
expect(service).toBeTruthy();
});
});

43
src/app/login.service.ts Normal file
View File

@ -0,0 +1,43 @@
import { Injectable } from '@angular/core';
import {ApiService} from './api.service';
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
import {LocalStorageService} from 'angular-2-local-storage';
@Injectable({
providedIn: 'root'
})
export class LoginService implements CanActivate {
private loggedIn = false;
constructor(
private api: ApiService,
private storage: LocalStorageService
) { }
login(username, password) {
return new Promise(resolve => {
this.storage.set('basicAuth', btoa(username + ':' + password));
this.api.get('/authorized').subscribe((data: any) => {
if (data.status === 'Authorization successful') {
this.loggedIn = true;
resolve(true);
}
else {
this.loggedIn = false;
this.storage.remove('basicAuth');
resolve(false);
}
},
() => {
this.loggedIn = false;
this.storage.remove('basicAuth');
resolve(false);
});
});
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.loggedIn;
}
}

View File

@ -1,10 +1,8 @@
<div class="login-wrapper"> <div class="login-wrapper">
<h2>Please log in</h2> <h2>Please log in</h2>
<rb-form-input name="username" label="username"> <rb-form-input name="username" label="username" [(ngModel)]="username"></rb-form-input>
</rb-form-input> <rb-form-input type="password" name="password" label="password" [(ngModel)]="password"></rb-form-input>
<rb-form-input type="password" name="password" label="password"> <span class="message">{{message}}</span>
</rb-form-input> <button class="rb-btn rb-primary login-button" (click)="login()">Login</button>
<button class="rb-btn rb-primary">Login</button>
<span>{{message}}</span>
</div> </div>

View File

@ -1,3 +1,12 @@
.login-wrapper { .login-wrapper {
max-width: 220px; max-width: 250px;
}
.message {
font-size: 13px;
white-space: pre-line;
}
.login-button {
display: block;
} }

View File

@ -1,4 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {ValidationService} from '../validation.service';
import {LoginService} from '../login.service';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
@ -7,13 +9,34 @@ import { Component, OnInit } from '@angular/core';
}) })
export class LoginComponent implements OnInit { export class LoginComponent implements OnInit {
message = ''; message = ''; // message below login fields
username = ''; // credentials
password = '';
validCredentials = false; // true if entered credentials are valid
constructor() { } constructor(
private validate: ValidationService,
private loginService: LoginService
) { }
ngOnInit() { ngOnInit() {
} }
login() {
const {ok: userOk, error: userError} = this.validate.username(this.username);
const {ok: passwordOk, error: passwordError} = this.validate.password(this.password);
this.message = userError + (userError + passwordError === '' ? '' : '\n') + passwordError; // display errors
console.log(this.message);
if (userOk && passwordOk) {
this.loginService.login(this.username, this.password).then(ok => {
if (ok) {
this.message = 'Login successful'; // TODO: think about following action, write tests!
}
else {
this.message = 'Wrong credentials! Try again.';
}
});
}
}
} }

View File

@ -6,7 +6,7 @@ describe('ValidationService', () => {
beforeEach(() => TestBed.configureTestingModule({})); beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => { it('should be created', () => {
const service: ValidationService = TestBed.get(ValidationService); const service: ValidationService = TestBed.inject(ValidationService);
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
}); });

View File

@ -1,9 +1,36 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import Joi from '@hapi/joi';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ValidationService { export class ValidationService {
private vUsername = Joi.string()
.lowercase()
.pattern(new RegExp('^[a-z0-9-_.]+$'))
.min(1)
.max(128);
private vPassword = Joi.string()
.pattern(new RegExp('^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#%&\'()*+,-.\\/:;<=>?@[\\]^_`{|}~])(?=\\S+$).{8,}$'))
.max(128);
constructor() { } constructor() { }
username(data) {
const {ignore, error} = this.vUsername.validate(data);
if (error) {
return {ok: false, error: 'username must only contain a-z0-9-_.'};
}
return {ok: true, error: ''};
}
password(data) {
const {ignore, error} = this.vPassword.validate(data);
if (error) {
return {ok: false, error: 'password must only contain a-zA-Z0-9!"#%&\'()*+,-./:;<=>?@[]^_`{|}~'};
}
return {ok: true, error: ''};
}
} }

6
src/proxy.conf.json Normal file
View File

@ -0,0 +1,6 @@
{
"/": {
"target": "http://localhost:3000",
"secure": false
}
}

View File

@ -3,6 +3,13 @@
"rules": { "rules": {
"array-type": false, "array-type": false,
"arrow-parens": false, "arrow-parens": false,
"brace-style": [
true,
"stroustrup",
{
"allowSingleLine": true
}
],
"deprecation": { "deprecation": {
"severity": "warning" "severity": "warning"
}, },