before update
This commit is contained in:
		@@ -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"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -88,4 +95,4 @@
 | 
				
			|||||||
  "rulesDirectory": [
 | 
					  "rulesDirectory": [
 | 
				
			||||||
    "codelyzer"
 | 
					    "codelyzer"
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user