Marcin Jaborski 3 سال پیش
والد
کامیت
e705edfb7e
37فایلهای تغییر یافته به همراه679 افزوده شده و 5 حذف شده
  1. 0 0
      IWA-angular/src/app/admin/admin.component.css
  2. 3 0
      IWA-angular/src/app/admin/admin.component.html
  3. 25 0
      IWA-angular/src/app/admin/admin.component.spec.ts
  4. 26 0
      IWA-angular/src/app/admin/admin.component.ts
  5. 17 0
      IWA-angular/src/app/app.component.html
  6. 22 0
      IWA-angular/src/app/app.component.ts
  7. 18 3
      IWA-angular/src/app/app.module.ts
  8. 7 0
      IWA-angular/src/app/auth/auth-interceptor.spec.ts
  9. 24 0
      IWA-angular/src/app/auth/auth-interceptor.ts
  10. 12 0
      IWA-angular/src/app/auth/auth.service.spec.ts
  11. 29 0
      IWA-angular/src/app/auth/auth.service.ts
  12. 7 0
      IWA-angular/src/app/auth/jwt-response.spec.ts
  13. 6 0
      IWA-angular/src/app/auth/jwt-response.ts
  14. 7 0
      IWA-angular/src/app/auth/login-info.spec.ts
  15. 10 0
      IWA-angular/src/app/auth/login-info.ts
  16. 7 0
      IWA-angular/src/app/auth/signup-info.spec.ts
  17. 12 0
      IWA-angular/src/app/auth/signup-info.ts
  18. 12 0
      IWA-angular/src/app/auth/token-storage.service.spec.ts
  19. 52 0
      IWA-angular/src/app/auth/token-storage.service.ts
  20. 16 0
      IWA-angular/src/app/guards/role.guard.spec.ts
  21. 31 0
      IWA-angular/src/app/guards/role.guard.ts
  22. 14 0
      IWA-angular/src/app/home/home.component.html
  23. 15 2
      IWA-angular/src/app/home/home.component.ts
  24. 0 0
      IWA-angular/src/app/login/login.component.css
  25. 38 0
      IWA-angular/src/app/login/login.component.html
  26. 25 0
      IWA-angular/src/app/login/login.component.spec.ts
  27. 58 0
      IWA-angular/src/app/login/login.component.ts
  28. 0 0
      IWA-angular/src/app/register/register.component.css
  29. 33 0
      IWA-angular/src/app/register/register.component.html
  30. 25 0
      IWA-angular/src/app/register/register.component.spec.ts
  31. 41 0
      IWA-angular/src/app/register/register.component.ts
  32. 12 0
      IWA-angular/src/app/services/user.service.spec.ts
  33. 22 0
      IWA-angular/src/app/services/user.service.ts
  34. 0 0
      IWA-angular/src/app/user/user.component.css
  35. 3 0
      IWA-angular/src/app/user/user.component.html
  36. 25 0
      IWA-angular/src/app/user/user.component.spec.ts
  37. 25 0
      IWA-angular/src/app/user/user.component.ts

+ 0 - 0
IWA-angular/src/app/admin/admin.component.css


+ 3 - 0
IWA-angular/src/app/admin/admin.component.html

@@ -0,0 +1,3 @@
+<h4>Content from Server</h4>
+{{board}}
+{{errorMessage}}

+ 25 - 0
IWA-angular/src/app/admin/admin.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AdminComponent } from './admin.component';
+
+describe('AdminComponent', () => {
+  let component: AdminComponent;
+  let fixture: ComponentFixture<AdminComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ AdminComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AdminComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 26 - 0
IWA-angular/src/app/admin/admin.component.ts

@@ -0,0 +1,26 @@
+import { Component, OnInit } from '@angular/core';
+import {UserService} from '../services/user.service';
+
+@Component({
+  selector: 'app-admin',
+  templateUrl: './admin.component.html',
+  styleUrls: ['./admin.component.css']
+})
+export class AdminComponent implements OnInit {
+  board?: string;
+  errorMessage?: string;
+
+  constructor(private userService: UserService) { }
+
+  ngOnInit() {
+    this.userService.getAdminPage().subscribe(
+      data => {
+        this.board = data;
+      },
+      error => {
+        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
+      }
+    );
+  }
+
+}

+ 17 - 0
IWA-angular/src/app/app.component.html

@@ -9,6 +9,23 @@
   </div>
 </div>
 
+<nav class="navbar navbar-inverse">
+  <div class="container-fluid">
+    <ul class="nav navbar-nav" routerLinkActive="active">
+      <li class="nav-item"><a class="nav-link" routerLink="home">Home</a></li>
+      <li *ngIf="authority === 'user'" class="nav-item">
+        <a class="nav-link" routerLink="user">User Page</a>
+      </li>
+      <li *ngIf="authority === 'admin'" class="nav-item">
+        <a class="nav-link" routerLink="admin">Admin Page</a>
+      </li>
+      <li *ngIf="!authority" class="nav-item">
+        <a class="nav-link" routerLink="auth/login">Login</a>
+      </li>
+    </ul>
+  </div>
+</nav>
+
 <div id="content">
   <div class="container">
     <router-outlet></router-outlet>

+ 22 - 0
IWA-angular/src/app/app.component.ts

@@ -1,4 +1,5 @@
 import { Component } from '@angular/core';
+import {TokenStorageService} from "./auth/token-storage.service";
 
 @Component({
   selector: 'app-root',
@@ -7,4 +8,25 @@ import { Component } from '@angular/core';
 })
 export class AppComponent {
   title = 'angular13-iwa2022-http-students';
+  private roles?: string[];
+  authority?: string;
+
+  constructor(private tokenStorage: TokenStorageService) {  }
+
+  ngOnInit() {
+    console.log("init");
+    if (this.tokenStorage.getToken()) {
+      console.log(this.tokenStorage.getToken());
+      this.roles = this.tokenStorage.getAuthorities();
+      this.roles.every(role => {
+        if (role === 'ROLE_ADMIN') {
+          this.authority = 'admin';
+          return false;
+        }
+        this.authority = 'user';
+        return true;
+      });
+    }
+  }
+
 }

+ 18 - 3
IWA-angular/src/app/app.module.ts

@@ -6,17 +6,32 @@ import { StudentsComponent } from './students/students.component';
 import {RouterModule, Routes} from "@angular/router";
 import {HttpClientModule} from "@angular/common/http";
 import {FormsModule} from "@angular/forms";
+import {UserComponent} from "./user/user.component";
+import {AdminComponent} from "./admin/admin.component";
+import {LoginComponent} from "./login/login.component";
+import {RegisterComponent} from "./register/register.component";
+import { httpInterceptorProviders } from './auth/auth-interceptor';
+import {RoleGuard} from "./guards/role.guard";
 
 const routes: Routes = [
   { path: 'home', component: HomeComponent },
-  { path: 'students', component: StudentsComponent }
+  { path: 'students', component: StudentsComponent },
+  { path: 'user', component: UserComponent, canActivate: [RoleGuard], data: { roles: ['ROLE_USER','ROLE_ADMIN'] },},
+  { path: 'admin', component: AdminComponent, canActivate: [RoleGuard], data: { roles: ['ROLE_ADMIN'] },},
+  { path: 'auth/login', component: LoginComponent },
+  { path: 'signup', component: RegisterComponent },
+  { path: '', redirectTo: 'home', pathMatch: 'full' }
 ];
 
 @NgModule({
   declarations: [
     AppComponent,
     HomeComponent,
-    StudentsComponent
+    StudentsComponent,
+    LoginComponent,
+    RegisterComponent,
+    UserComponent,
+    AdminComponent
   ],
   imports: [
     BrowserModule,
@@ -25,7 +40,7 @@ const routes: Routes = [
     FormsModule,
     RouterModule.forRoot(routes)
   ],
-  providers: [],
+  providers: [httpInterceptorProviders],
   bootstrap: [AppComponent]
 })
 export class AppModule { }

+ 7 - 0
IWA-angular/src/app/auth/auth-interceptor.spec.ts

@@ -0,0 +1,7 @@
+import { AuthInterceptor } from './auth-interceptor';
+
+describe('AuthInterceptor', () => {
+  it('should create an instance', () => {
+    //expect(new AuthInterceptor()).toBeTruthy();
+  });
+});

+ 24 - 0
IWA-angular/src/app/auth/auth-interceptor.ts

@@ -0,0 +1,24 @@
+import {Injectable} from '@angular/core';
+import {HTTP_INTERCEPTORS, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
+import {TokenStorageService} from './token-storage.service';
+
+const TOKEN_HEADER_KEY = 'Authorization';
+
+@Injectable()
+export class AuthInterceptor implements HttpInterceptor {
+
+  constructor(private token: TokenStorageService) { }
+
+  intercept(req: HttpRequest<any>, next: HttpHandler) {
+    let authReq = req;
+    const token = this.token.getToken();
+    if (token != null) {
+      authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
+    }
+    return next.handle(authReq);
+  }
+}
+
+export const httpInterceptorProviders = [
+  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
+];

+ 12 - 0
IWA-angular/src/app/auth/auth.service.spec.ts

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

+ 29 - 0
IWA-angular/src/app/auth/auth.service.ts

@@ -0,0 +1,29 @@
+import { Injectable } from '@angular/core';
+import {HttpClient, HttpHeaders} from '@angular/common/http';
+import {LoginInfo} from './login-info';
+import {Observable} from 'rxjs';
+import {JwtResponse} from './jwt-response';
+import {SignupInfo} from './signup-info';
+
+const httpOptions = {
+  headers: new HttpHeaders({'Content-Type': 'application/json'})
+};
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuthService {
+
+  private loginUrl = 'http://localhost:8080/auth/signin';
+  private signupUrl = 'http://localhost:8080/auth/signup';
+
+  constructor(private http: HttpClient) { }
+
+  attemptAuth(credentials: LoginInfo): Observable<JwtResponse> {
+    return this.http.post<JwtResponse>(this.loginUrl, credentials, httpOptions);
+  }
+
+  signUp(info: SignupInfo): Observable<string> {
+    return this.http.post<string>(this.signupUrl, info, httpOptions);
+  }
+}

+ 7 - 0
IWA-angular/src/app/auth/jwt-response.spec.ts

@@ -0,0 +1,7 @@
+import { JwtResponse } from './jwt-response';
+
+describe('JwtResponse', () => {
+  it('should create an instance', () => {
+    expect(new JwtResponse()).toBeTruthy();
+  });
+});

+ 6 - 0
IWA-angular/src/app/auth/jwt-response.ts

@@ -0,0 +1,6 @@
+export class JwtResponse {
+  accessToken?: string;
+  type?: string;
+  username?: string;
+  authorities?: string[];
+}

+ 7 - 0
IWA-angular/src/app/auth/login-info.spec.ts

@@ -0,0 +1,7 @@
+import { LoginInfo } from './login-info';
+
+describe('LoginInfo', () => {
+  it('should create an instance', () => {
+    // expect(new LoginInfo()).toBeTruthy();
+  });
+});

+ 10 - 0
IWA-angular/src/app/auth/login-info.ts

@@ -0,0 +1,10 @@
+export class LoginInfo {
+
+  username: string;
+  password: string;
+
+  constructor(username: string, password: string) {
+    this.username = username;
+    this.password = password;
+  }
+}

+ 7 - 0
IWA-angular/src/app/auth/signup-info.spec.ts

@@ -0,0 +1,7 @@
+import { SignupInfo } from './signup-info';
+
+describe('SignupInfo', () => {
+  it('should create an instance', () => {
+    expect(new SignupInfo()).toBeTruthy();
+  });
+});

+ 12 - 0
IWA-angular/src/app/auth/signup-info.ts

@@ -0,0 +1,12 @@
+export class SignupInfo {
+
+  username: string;
+  role: string[];
+  password: string;
+
+  constructor(username: string, password: string) {
+    this.username = username;
+    this.role = ['user'];
+    this.password = password;
+  }
+}

+ 12 - 0
IWA-angular/src/app/auth/token-storage.service.spec.ts

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

+ 52 - 0
IWA-angular/src/app/auth/token-storage.service.ts

@@ -0,0 +1,52 @@
+import { Injectable } from '@angular/core';
+
+const TOKEN_KEY = 'AuthToken';
+const USERNAME_KEY = 'AuthUsername';
+const AUTHORITIES_KEY = 'AuthAuthorities';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class TokenStorageService {
+  private roles: Array<string> = [];
+  constructor() { }
+
+  signOut() {
+    window.sessionStorage.clear();
+  }
+
+  public saveToken(token: string) {
+    window.sessionStorage.removeItem(TOKEN_KEY);
+    window.sessionStorage.setItem(TOKEN_KEY, token);
+  }
+
+  public getToken(): string {
+    return sessionStorage.getItem(TOKEN_KEY) || '{}';
+  }
+
+  public saveUsername(username: string) {
+    window.sessionStorage.removeItem(USERNAME_KEY);
+    window.sessionStorage.setItem(USERNAME_KEY, username);
+  }
+
+  public getUsername(): string {
+    return sessionStorage.getItem(USERNAME_KEY) || '{}';
+  }
+
+  public saveAuthorities(authorities: string[]) {
+    window.sessionStorage.removeItem(AUTHORITIES_KEY);
+    window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
+  }
+
+  public getAuthorities(): string[] {
+    this.roles = [];
+
+    if (sessionStorage.getItem(TOKEN_KEY)) {
+      JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY) || '{}').forEach((authority: { authority: string; }) => {
+        this.roles.push(authority.authority);
+      });
+    }
+
+    return this.roles;
+  }
+}

+ 16 - 0
IWA-angular/src/app/guards/role.guard.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { RoleGuard } from './role.guard';
+
+describe('RoleGuard', () => {
+  let guard: RoleGuard;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    guard = TestBed.inject(RoleGuard);
+  });
+
+  it('should be created', () => {
+    expect(guard).toBeTruthy();
+  });
+});

+ 31 - 0
IWA-angular/src/app/guards/role.guard.ts

@@ -0,0 +1,31 @@
+import { Injectable } from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
+import { Observable } from 'rxjs';
+import {TokenStorageService} from "../auth/token-storage.service";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class RoleGuard implements CanActivate {
+
+  constructor(private tokenStorageService: TokenStorageService, private router: Router) {
+  }
+
+  canActivate(
+    route: ActivatedRouteSnapshot,
+    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
+
+    // check if any role from authorities list is in the routing list defined
+    for (let i = 0; i < route.data['roles'].length; i++) {
+      for (let j = 0; j < this.tokenStorageService.getAuthorities().length; j++) {
+        if (route.data['roles'][i] === this.tokenStorageService.getAuthorities()[j]) {
+          return true;
+        }
+      }
+    }
+
+    // otherwise redirect to specified url
+      this.router.navigateByUrl('').then();
+      return false;
+  }
+}

+ 14 - 0
IWA-angular/src/app/home/home.component.html

@@ -1 +1,15 @@
 <p>home component works!</p>
+<div *ngIf="info.token != '{}'; else loggedOut">
+  <h5 class="text-primary">Your Information</h5>
+  <p>
+    <strong>Username:</strong> {{info.username}}<br/>
+    <strong>Roles:</strong> {{info.authorities}}<br />
+    <strong>Token:</strong> {{info.token}}.
+  </p>
+  <button class="btn btn-secondary" (click)="logout()">Logout</button>
+</div>
+
+<ng-template #loggedOut>
+  Please login.
+</ng-template>
+

+ 15 - 2
IWA-angular/src/app/home/home.component.ts

@@ -1,4 +1,5 @@
 import { Component, OnInit } from '@angular/core';
+import {TokenStorageService} from "../auth/token-storage.service";
 
 @Component({
   selector: 'app-home',
@@ -7,9 +8,21 @@ import { Component, OnInit } from '@angular/core';
 })
 export class HomeComponent implements OnInit {
 
-  constructor() { }
+  info: any;
 
-  ngOnInit(): void {
+  constructor(private token: TokenStorageService) { }
+
+  ngOnInit() {
+    this.info = {
+      token: this.token.getToken(),
+      username: this.token.getUsername(),
+      authorities: this.token.getAuthorities()
+    };
+  }
+
+  logout() {
+    this.token.signOut();
+    window.location.reload();
   }
 
 }

+ 0 - 0
IWA-angular/src/app/login/login.component.css


+ 38 - 0
IWA-angular/src/app/login/login.component.html

@@ -0,0 +1,38 @@
+<div style="float: left; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); background-color: green;">
+<div *ngIf="isLoggedIn; else loggedOut">
+  Logged in as {{roles}}.
+</div>
+
+<ng-template #loggedOut>
+  <div class="row col-sm-6" style="max-width:350px;">
+    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
+      <div class="form-group">
+        <label for="username" id="username">Username</label>
+        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
+               required />
+        <div *ngIf="f.submitted && username.invalid">
+          <div *ngIf="['username.errors?.required']">Username is required</div>
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="password" id="password">Password</label>
+        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
+               required minlength="6" />
+        <div *ngIf="f.submitted && password.invalid">
+          <div *ngIf="['password.errors?.required']">Password is required</div>
+          <div *ngIf="['password.errors?.minlength']">Password must be at least 6 characters</div>
+        </div>
+      </div>
+      <div class="form-group">
+        <button class="btn btn-primary">Login</button>
+        <div *ngIf="f.submitted && isLoginFailed" class="alert alert-danger">
+          Login failed: {{errorMessage}}
+        </div>
+      </div>
+    </form>
+    <hr />
+    <p>Don't have an account?</p>
+    <a href="signup" class="btn btn-success">Sign Up</a>
+  </div>
+</ng-template>
+</div>

+ 25 - 0
IWA-angular/src/app/login/login.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+  let component: LoginComponent;
+  let fixture: ComponentFixture<LoginComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LoginComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 58 - 0
IWA-angular/src/app/login/login.component.ts

@@ -0,0 +1,58 @@
+import { Component, OnInit } from '@angular/core';
+import {LoginInfo} from '../auth/login-info';
+import {AuthService} from '../auth/auth.service';
+import {TokenStorageService} from '../auth/token-storage.service';
+
+@Component({
+  selector: 'app-login',
+  templateUrl: './login.component.html',
+  styleUrls: ['./login.component.css']
+})
+export class LoginComponent implements OnInit {
+  form: any = {};
+  token?: string;
+  isLoggedIn = false;
+  isLoginFailed = false;
+  errorMessage = '';
+  roles: string[] = [];
+  private loginInfo?: LoginInfo;
+
+  constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
+
+  ngOnInit() {
+    if (this.tokenStorage.getToken() != null && this.tokenStorage.getToken() != '{}') {
+      this.isLoggedIn = true;
+      this.roles = this.tokenStorage.getAuthorities();
+    }
+  }
+
+  onSubmit() {
+    console.log(this.form);
+
+    this.loginInfo = new LoginInfo(this.form.username, this.form.password);
+
+    this.authService.attemptAuth(this.loginInfo).subscribe(
+      data => {
+        this.tokenStorage.saveToken(data.accessToken || '{}');
+        this.tokenStorage.saveUsername(data.username || '{}');
+        this.tokenStorage.saveAuthorities(data.authorities || []);
+
+        this.isLoginFailed = false;
+        this.isLoggedIn = true;
+        this.token = this.tokenStorage.getToken();
+        this.roles = this.tokenStorage.getAuthorities();
+        this.reloadPage();
+      },
+      error => {
+        console.log(error);
+        this.errorMessage = error.error.message;
+        this.isLoginFailed = true;
+      }
+    );
+  }
+
+  reloadPage() {
+    window.location.reload();
+  }
+
+}

+ 0 - 0
IWA-angular/src/app/register/register.component.css


+ 33 - 0
IWA-angular/src/app/register/register.component.html

@@ -0,0 +1,33 @@
+<div *ngIf="isSignedUp; else signupForm">
+  Your registration is successful. Please login!
+</div>
+
+<ng-template #signupForm>
+  <div class="row col-sm-6" style="max-width:350px;">
+    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
+      <div class="form-group">
+        <label for="username" id="username">Username</label>
+        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
+               required />
+        <div *ngIf="f.submitted && username.invalid">
+          <div *ngIf="['username.errors?.required']">Username is required</div>
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="password" id="password">Password</label>
+        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
+               required minlength="6" />
+        <div *ngIf="f.submitted && password.invalid">
+          <div *ngIf="['password.errors?.required']">Password is required</div>
+          <div *ngIf="['password.errors?.minlength']">Password must be at least 6 characters</div>
+        </div>
+      </div>
+      <div class="form-group">
+        <button class="btn btn-primary">Register</button>
+        <div *ngIf="f.submitted && isSignUpFailed" class="alert alert-warning">
+          Signup failed!<br/>{{errorMessage}}
+        </div>
+      </div>
+    </form>
+  </div>
+</ng-template>

+ 25 - 0
IWA-angular/src/app/register/register.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RegisterComponent } from './register.component';
+
+describe('RegisterComponent', () => {
+  let component: RegisterComponent;
+  let fixture: ComponentFixture<RegisterComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ RegisterComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(RegisterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 41 - 0
IWA-angular/src/app/register/register.component.ts

@@ -0,0 +1,41 @@
+import { Component, OnInit } from '@angular/core';
+import {AuthService} from '../auth/auth.service';
+import {SignupInfo} from '../auth/signup-info';
+
+@Component({
+  selector: 'app-register',
+  templateUrl: './register.component.html',
+  styleUrls: ['./register.component.css']
+})
+export class RegisterComponent implements OnInit {
+  form: any = {};
+  signupInfo?: SignupInfo;
+  isSignedUp = false;
+  isSignUpFailed = false;
+  errorMessage = '';
+
+  constructor(private authService: AuthService) { }
+
+  ngOnInit() { }
+
+  onSubmit() {
+    console.log(this.form);
+
+    this.signupInfo = new SignupInfo(
+      this.form.username,
+      this.form.password);
+
+    this.authService.signUp(this.signupInfo).subscribe(
+      data => {
+        console.log(data);
+        this.isSignedUp = true;
+        this.isSignUpFailed = false;
+      },
+      error => {
+        console.log(error);
+        this.errorMessage = error.error.message;
+        this.isSignUpFailed = true;
+      }
+    );
+  }
+}

+ 12 - 0
IWA-angular/src/app/services/user.service.spec.ts

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

+ 22 - 0
IWA-angular/src/app/services/user.service.ts

@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import {Observable} from 'rxjs';
+import {HttpClient} from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UserService {
+
+  private userUrl = 'http://localhost:8080/exampleSecurity/user';
+  private adminUrl = 'http://localhost:8080/exampleSecurity/admin';
+
+  constructor(private http: HttpClient) { }
+
+  getUserPage(): Observable<string> {
+    return this.http.get(this.userUrl, { responseType: 'text' });
+  }
+
+  getAdminPage(): Observable<string> {
+    return this.http.get(this.adminUrl, { responseType: 'text' });
+  }
+}

+ 0 - 0
IWA-angular/src/app/user/user.component.css


+ 3 - 0
IWA-angular/src/app/user/user.component.html

@@ -0,0 +1,3 @@
+<h4>Content from Server</h4>
+{{board}}
+{{errorMessage}}

+ 25 - 0
IWA-angular/src/app/user/user.component.spec.ts

@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { UserComponent } from './user.component';
+
+describe('UserComponent', () => {
+  let component: UserComponent;
+  let fixture: ComponentFixture<UserComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ UserComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(UserComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 25 - 0
IWA-angular/src/app/user/user.component.ts

@@ -0,0 +1,25 @@
+import { Component, OnInit } from '@angular/core';
+import {UserService} from '../services/user.service';
+
+@Component({
+  selector: 'app-user',
+  templateUrl: './user.component.html',
+  styleUrls: ['./user.component.css']
+})
+export class UserComponent implements OnInit {
+  board?: string;
+  errorMessage?: string;
+
+  constructor(private userService: UserService) { }
+
+  ngOnInit() {
+    this.userService.getUserPage().subscribe(
+      data => {
+        this.board = data;
+      },
+      error => {
+        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
+      }
+    );
+  }
+}