before update
This commit is contained in:
		
							
								
								
									
										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 { Routes, RouterModule } from '@angular/router';
 | 
			
		||||
import {HomeComponent} from './home/home.component';
 | 
			
		||||
import {LoginService} from './login.service';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  {path: '', component: HomeComponent}
 | 
			
		||||
  {path: '', component: HomeComponent},
 | 
			
		||||
  {path: 'replace-me', component: HomeComponent, canActivate: [LoginService]},
 | 
			
		||||
 | 
			
		||||
  // if not authenticated
 | 
			
		||||
  { path: '**', redirectTo: '' }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
 | 
			
		||||
import { LoginComponent } from './login/login.component';
 | 
			
		||||
import { HomeComponent } from './home/home.component';
 | 
			
		||||
import {FormsModule} from '@angular/forms';
 | 
			
		||||
import {LocalStorageModule} from 'angular-2-local-storage';
 | 
			
		||||
import {HttpClientModule} from '@angular/common/http';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -15,10 +17,15 @@ import {FormsModule} from '@angular/forms';
 | 
			
		||||
    HomeComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    LocalStorageModule.forRoot({
 | 
			
		||||
      prefix: 'dfop',
 | 
			
		||||
      storageType: 'localStorage'
 | 
			
		||||
    }),
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
    AppRoutingModule,
 | 
			
		||||
    RbUiComponentsModule,
 | 
			
		||||
    FormsModule
 | 
			
		||||
    FormsModule,
 | 
			
		||||
    HttpClientModule
 | 
			
		||||
  ],
 | 
			
		||||
  providers: [],
 | 
			
		||||
  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">
 | 
			
		||||
  <h2>Please log in</h2>
 | 
			
		||||
 | 
			
		||||
  <rb-form-input name="username" label="username">
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <rb-form-input type="password" name="password" label="password">
 | 
			
		||||
  </rb-form-input>
 | 
			
		||||
  <button class="rb-btn rb-primary">Login</button>
 | 
			
		||||
  <span>{{message}}</span>
 | 
			
		||||
  <rb-form-input name="username" label="username" [(ngModel)]="username"></rb-form-input>
 | 
			
		||||
  <rb-form-input type="password" name="password" label="password" [(ngModel)]="password"></rb-form-input>
 | 
			
		||||
  <span class="message">{{message}}</span>
 | 
			
		||||
  <button class="rb-btn rb-primary login-button" (click)="login()">Login</button>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,12 @@
 | 
			
		||||
.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 {ValidationService} from '../validation.service';
 | 
			
		||||
import {LoginService} from '../login.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-login',
 | 
			
		||||
@@ -7,13 +9,34 @@ import { Component, OnInit } from '@angular/core';
 | 
			
		||||
})
 | 
			
		||||
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() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  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({}));
 | 
			
		||||
 | 
			
		||||
  it('should be created', () => {
 | 
			
		||||
    const service: ValidationService = TestBed.get(ValidationService);
 | 
			
		||||
    const service: ValidationService = TestBed.inject(ValidationService);
 | 
			
		||||
    expect(service).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,36 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import Joi from '@hapi/joi';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
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() { }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user