before update
This commit is contained in:
parent
7f47af425d
commit
7cc6147a25
@ -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
2869
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||||
|
14
src/app/api.service.spec.ts
Normal file
14
src/app/api.service.spec.ts
Normal 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
28
src/app/api.service.ts
Normal 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 {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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({
|
||||||
|
@ -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]
|
||||||
|
12
src/app/login.service.spec.ts
Normal file
12
src/app/login.service.spec.ts
Normal 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
43
src/app/login.service.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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
6
src/proxy.conf.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"/": {
|
||||||
|
"target": "http://localhost:3000",
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user