diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java index fc999c2..6d19163 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java @@ -1,5 +1,6 @@ package at.mueller.eeg.backend.iam.config; +import at.mueller.eeg.backend.iam.repository.UserRepository; import com.nimbusds.jose.jwk.source.ImmutableSecret; import jakarta.servlet.DispatcherType; import org.springframework.beans.factory.annotation.Value; @@ -12,6 +13,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.jwt.JwtDecoder; @@ -143,4 +146,10 @@ public class SecurityConfig { SecretKeySpec secretKey = new SecretKeySpec(jwtSecret.getBytes(), "HmacSHA256"); return new NimbusJwtEncoder(new ImmutableSecret<>(secretKey)); } + + @Bean + public UserDetailsService userDetailsService(UserRepository userRepository) { + return username -> userRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User mit E-Mail " + username + " nicht gefunden.")); + } } diff --git a/eeg_frontend/src/app/layout/header/header.html b/eeg_frontend/src/app/layout/header/header.html new file mode 100644 index 0000000..864abcc --- /dev/null +++ b/eeg_frontend/src/app/layout/header/header.html @@ -0,0 +1,46 @@ + diff --git a/eeg_frontend/src/app/layout/header/header.scss b/eeg_frontend/src/app/layout/header/header.scss new file mode 100644 index 0000000..e69de29 diff --git a/eeg_frontend/src/app/layout/header/header.spec.ts b/eeg_frontend/src/app/layout/header/header.spec.ts new file mode 100644 index 0000000..9ef7403 --- /dev/null +++ b/eeg_frontend/src/app/layout/header/header.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Header } from './header'; + +describe('Header', () => { + let component: Header; + let fixture: ComponentFixture
; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Header], + }).compileComponents(); + + fixture = TestBed.createComponent(Header); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/eeg_frontend/src/app/layout/header/header.ts b/eeg_frontend/src/app/layout/header/header.ts new file mode 100644 index 0000000..f309b98 --- /dev/null +++ b/eeg_frontend/src/app/layout/header/header.ts @@ -0,0 +1,27 @@ +import {Component} from '@angular/core'; +import {Router, RouterLink} from '@angular/router'; +import {AuthService} from '../../services/auth'; +import {AsyncPipe} from '@angular/common'; +import {MatButton} from '@angular/material/button'; + +@Component({ + selector: 'app-header', + imports: [ + RouterLink, + AsyncPipe, + MatButton + ], + templateUrl: './header.html' +}) +export class HeaderComponent { + + constructor( + public authService: AuthService, + private router: Router + ) {} + + onLogout() { + this.authService.logout(); + this.router.navigate(['/login']); + } +} diff --git a/eeg_frontend/src/app/pages/login/login.html b/eeg_frontend/src/app/pages/login/login.html new file mode 100644 index 0000000..b85c5bc --- /dev/null +++ b/eeg_frontend/src/app/pages/login/login.html @@ -0,0 +1,47 @@ +
+
+
+

+ In Ihr EEG-Portal einloggen +

+
+ + + +
+
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
diff --git a/eeg_frontend/src/app/pages/login/login.scss b/eeg_frontend/src/app/pages/login/login.scss new file mode 100644 index 0000000..e69de29 diff --git a/eeg_frontend/src/app/pages/login/login.spec.ts b/eeg_frontend/src/app/pages/login/login.spec.ts new file mode 100644 index 0000000..1ee4152 --- /dev/null +++ b/eeg_frontend/src/app/pages/login/login.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Login } from './login'; + +describe('Login', () => { + let component: Login; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Login], + }).compileComponents(); + + fixture = TestBed.createComponent(Login); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/eeg_frontend/src/app/pages/login/login.ts b/eeg_frontend/src/app/pages/login/login.ts new file mode 100644 index 0000000..3d7af8d --- /dev/null +++ b/eeg_frontend/src/app/pages/login/login.ts @@ -0,0 +1,56 @@ +import {Component} from '@angular/core'; +import {Router} from '@angular/router'; +import {AuthService} from '../../services/auth'; +import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; +import {CommonModule} from '@angular/common'; // 1. Hier importieren + +@Component({ + selector: 'app-login', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], + templateUrl: './login.html' +}) +export class LoginComponent { + loginForm: FormGroup; + errorMessage: string = ''; + + constructor( + private fb: FormBuilder, + private authService: AuthService, + private router: Router + ) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required]], + password: ['', [Validators.required]] + }); + } + + onSubmit() { + if (this.loginForm.invalid) return; + + this.authService.login(this.loginForm.value).subscribe({ + next: (response) => { + // Hier passiert die bedingte Weiterleitung anhand der Rolle + this.redirectBasedOnRole(response.role!); + }, + error: (err) => { + this.errorMessage = err.error?.message || 'Ein unbekannter Fehler ist aufgetreten.'; + } + }); + } + + private redirectBasedOnRole(role: string) { + switch (role) { + case 'ADMIN': + void this.router.navigate(['/admin/approvals']); + break; + case 'MEMBER': + void this.router.navigate(['/dashboard']); + break; + default: + // Fallback, falls die Rolle unbekannt ist + void this.router.navigate(['/']); + break; + } + } +}