Skip to content

Commit

Permalink
Add logout functionality and error page customization
Browse files Browse the repository at this point in the history
Implemented a logout component with associated service methods and UI elements. Added a custom 500 error page and styled it accordingly. Enhanced route guards to check for permissions and introduced local storage for theme persistence.
  • Loading branch information
MichiBaum committed Oct 22, 2024
1 parent 246c72d commit e646b48
Show file tree
Hide file tree
Showing 17 changed files with 307 additions and 52 deletions.
18 changes: 18 additions & 0 deletions chess-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@
<optional>true</optional>
</dependency>

<!-- TODO make everything async with kotlinx coroutine (CoroutineCrudRepository, awaitBody<Class>(), etc) -->
<!-- <dependency>-->
<!-- <groupId>org.jetbrains.kotlinx</groupId>-->
<!-- <artifactId>kotlinx-coroutines-core</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.jetbrains.kotlinx</groupId>-->
<!-- <artifactId>kotlinx-coroutines-core-jvm</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.jetbrains.kotlinx</groupId>-->
<!-- <artifactId>kotlinx-coroutines-reactive</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.jetbrains.kotlinx</groupId>-->
<!-- <artifactId>kotlinx-coroutines-reactor</artifactId>-->
<!-- </dependency>-->

<dependency>
<groupId>com.michibaum</groupId>
<artifactId>usermanagement-library</artifactId>
Expand Down
21 changes: 21 additions & 0 deletions gateway-service/src/main/resources/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en" >

<head>
<meta charset="UTF-8">
<title>Error 500</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=IBM+Plex+Mono:400|Oswald:600'>
<link rel="stylesheet" href="./style.css">
</head>

<body>
<div id="error">Error</div>
<div class="error-num">500
<div class="error-num__clip">500</div>
</div>
<p id="desc">Uh oh, there seems to be a problem.</p>
<p>Let me help you find <a href="http://michibaum.ch" target="_blank">a way out</a></p>

</body>
</html>
127 changes: 127 additions & 0 deletions gateway-service/src/main/resources/public/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
:root {
font-size: 20px;
font-family: "IBM Plex Mono";
line-height: 1.5;
color: rgba(255, 255, 255, 0.25);
}

body {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background: #333333;
}

a {
color: white;
display: inline;
}

#error {
margin-bottom: 1rem;
font-size: 2rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.075em;
color: #C94D4D;
-webkit-animation: pulse 4s infinite alternate;
animation: pulse 4s infinite alternate;
position: relative;
}
@-webkit-keyframes pulse {
from {
opacity: 0.5;
}
50% {
opacity: 0.5;
}
}
@keyframes pulse {
from {
opacity: 0.5;
}
50% {
opacity: 0.5;
}
}
#error::before {
content: "";
width: 0.75rem;
height: 50vh;
margin-bottom: 0.75em;
position: absolute;
left: 50%;
bottom: 100%;
transform: translateX(-50%);
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 60%, transparent 100%);
}

#desc {
margin: 2em 0 1em;
}

.error-num, .error-num__clip, .error-num__clip-left, .error-num__clip-left::before, .error-num__clip::before {
position: relative;
font-size: 10rem;
font-family: "Oswald";
letter-spacing: -0.01em;
color: white;
-webkit-animation: colorSplit 1.25s steps(2, end) infinite;
animation: colorSplit 1.25s steps(2, end) infinite;
}
@-webkit-keyframes colorSplit {
25% {
text-shadow: -0.02em 0 0 #ED008C, 0.025em 0 0 #0087EF;
}
75% {
text-shadow: -0.035em 0 0 #ED008C, 0.04em 0 0 #0087EF;
}
}
@keyframes colorSplit {
25% {
text-shadow: -0.02em 0 0 #ED008C, 0.025em 0 0 #0087EF;
}
75% {
text-shadow: -0.035em 0 0 #ED008C, 0.04em 0 0 #0087EF;
}
}

.error-num__clip, .error-num__clip-left, .error-num__clip-left::before, .error-num__clip::before {
position: absolute;
top: 0;
left: -2px;
z-index: 10;
color: #333;
overflow: visible;
-webkit-clip-path: polygon(0% 0%, 100% 0, 100% 25%, 0 25%, 0 30%, 100% 30%, 100% 50%, 0 50%, 0 60%, 100% 60%, 100% 65%, 0 65%, 0 80%, 100% 80%, 100% 85%, 0 85%, 0% 0%);
clip-path: polygon(0% 0%, 100% 0, 100% 25%, 0 25%, 0 30%, 100% 30%, 100% 50%, 0 50%, 0 60%, 100% 60%, 100% 65%, 0 65%, 0 80%, 100% 80%, 100% 85%, 0 85%, 0% 0%);
-webkit-animation: glitch 1s steps(2, start) infinite;
animation: glitch 1s steps(2, start) infinite;
}
@-webkit-keyframes glitch {
30% {
left: 0;
}
to {
left: 0;
}
}
@keyframes glitch {
30% {
left: 0;
}
to {
left: 0;
}
}
.error-num__clip::before, .error-num__clip-left::before {
content: "500";
left: 0.05em;
color: white;
z-index: 9;
-webkit-clip-path: polygon(0% 0%, 100% 0, 100% 26%, 0 26%, 0 29%, 100% 29%, 100% 51%, 0 51%, 0 59%, 100% 59%, 100% 66%, 0 66%, 0 79%, 100% 79%, 100% 86%, 0 86%, 0% 0%);
clip-path: polygon(0% 0%, 100% 0, 100% 26%, 0 26%, 0 29%, 100% 29%, 100% 51%, 0 51%, 0 59%, 100% 59%, 100% 66%, 0 66%, 0 79%, 100% 79%, 100% 86%, 0 86%, 0% 0%);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import java.util.*

@Repository
interface UserRepository : JpaRepository<User, String> {
fun findByUsername(username: String): Optional<User>
fun findByUsername(username: String): User?
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,18 @@ class UserService(
private val passwordEncoder: PasswordEncoder
) {

fun getUser(id: String) = findUserById(id)
fun getUser(id: String) = userRepository.findById(id).orElseNull()

fun update(id: String, updateUserDto: UpdateUserDto): User? {
val foundUser = userRepository.findById(id).orElse(null)
val foundUser = userRepository.findById(id).orElseNull()
return foundUser?.let {
// TODO update User
userRepository.save(foundUser)
}
}

private fun findUserById(id: String) = userRepository.findById(id).orElseNull()

fun findByUsername(username: String): User? {
return userRepository.findByUsername(username).orElseNull()
return userRepository.findByUsername(username)
}

fun checkPassword(dtoPassword: String, passwordHash: String): Boolean {
Expand Down
5 changes: 1 addition & 4 deletions website/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ export class AppComponent implements OnInit {
this.translateService.get('primeng').subscribe(res => this.primengConfig.setTranslation(res));
});

if(this.lightDarkModeService.isSystemDark()){
this.lightDarkModeService.changeModeTo(document, LightDarkMode.dark)
}

this.lightDarkModeService.init(document)
}

}
23 changes: 15 additions & 8 deletions website/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Routes } from '@angular/router';
import {Routes} from '@angular/router';
import {AuthenticationComponent} from "./authentication/authentication.component";
import {MicroserviceOverviewComponent} from "./microservice-overview/microservice-overview.component";
import {AboutMeComponent} from "./about-me/about-me.component";
Expand All @@ -11,36 +11,43 @@ export const routes: Routes = [
{
path: Sides.default.navigation,
component: HomeComponent,
canActivate: Sides.default.routeCanActivate
canActivate: Sides.default.routeCanActivate,
data: {permissions: Sides.default.neededPermissions}
},
{
path: Sides.home.navigation,
component: HomeComponent,
canActivate: Sides.home.routeCanActivate
canActivate: Sides.home.routeCanActivate,
data: {permissions: Sides.home.neededPermissions}
},
{
path: Sides.login.navigation,
component: AuthenticationComponent,
canActivate: Sides.login.routeCanActivate
canActivate: Sides.login.routeCanActivate,
data: {permissions: Sides.login.neededPermissions}
},
{
path: Sides.microservices.navigation,
component: MicroserviceOverviewComponent,
canActivate: Sides.microservices.routeCanActivate
canActivate: Sides.microservices.routeCanActivate,
data: {permissions: Sides.microservices.neededPermissions}
},
{
path: Sides.imprint.navigation,
component: ImprintComponent,
canActivate: Sides.imprint.routeCanActivate
canActivate: Sides.imprint.routeCanActivate,
data: {permissions: Sides.imprint.neededPermissions}
},
{
path: Sides.about_me.navigation,
component: AboutMeComponent,
canActivate: Sides.about_me.routeCanActivate
canActivate: Sides.about_me.routeCanActivate,
data: {permissions: Sides.about_me.neededPermissions}
},
{
path: Sides.chess.navigation,
component: ChessComponent,
canActivate: Sides.chess.routeCanActivate
canActivate: Sides.chess.routeCanActivate,
data: {permissions: Sides.chess.neededPermissions}
}
];
24 changes: 16 additions & 8 deletions website/src/app/core/config/sides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ interface Side{
translationKey: string,
navigation: string,
canActivate: (service: PermissionService) => boolean,
routeCanActivate: Array<CanActivateFn>
routeCanActivate: Array<CanActivateFn>,
neededPermissions: Permissions[]
}

export const Sides = {
Expand All @@ -17,48 +18,55 @@ export const Sides = {
translationKey: "imprint.title",
navigation: "imprint",
canActivate: (service: PermissionService) => true,
routeCanActivate: []
routeCanActivate: [],
neededPermissions: []
} as Side,
microservices: {
name: "microservices",
translationKey: "microservices.title",
navigation: "microservices",
canActivate: (service: PermissionService) => service.hasAnyOf([Permissions.ADMIN_SERVICE]),
routeCanActivate: [isPermittedGuard]
routeCanActivate: [isPermittedGuard],
neededPermissions: [Permissions.ADMIN_SERVICE]
} as Side,
about_me: {
name: "about-me",
translationKey: "about-me.title",
navigation: "about-me",
canActivate: (service: PermissionService) => true,
routeCanActivate: []
routeCanActivate: [],
neededPermissions: []
} as Side,
login: {
name: "login",
translationKey: "login.title",
navigation: "login",
canActivate: (service: PermissionService) => !service.isAuthenticated(),
routeCanActivate: []
routeCanActivate: [],
neededPermissions: []
} as Side,
home: {
name: "home",
translationKey: "home.title",
navigation: "home",
canActivate: (service: PermissionService) => service.isAuthenticated(),
routeCanActivate: [isAuthenticatedGuard]
routeCanActivate: [isAuthenticatedGuard],
neededPermissions: []
} as Side,
chess: {
name: "chess",
translationKey: "chess.title",
navigation: "chess",
canActivate: (service: PermissionService) => service.isAuthenticated(),
routeCanActivate: [isAuthenticatedGuard]
routeCanActivate: [isAuthenticatedGuard],
neededPermissions: []
} as Side,
default: {
name: "default",
translationKey: "",
navigation: "",
canActivate: (service: PermissionService) => service.isAuthenticated(),
routeCanActivate: [isAuthenticatedGuard]
routeCanActivate: [isAuthenticatedGuard],
neededPermissions: []
} as Side
}
16 changes: 10 additions & 6 deletions website/src/app/core/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { CanActivateFn } from '@angular/router';
import {CanActivateFn} from '@angular/router';
import {inject} from "@angular/core";
import {AuthService} from "../services/auth.service";
import {PermissionService} from "../services/permission.service";

export const isAuthenticatedGuard: CanActivateFn = (route, state) => {
let authService = inject(AuthService);
return authService.isAuthenticated(route);
return authService.isAuthenticated();
};

export const hasPermissionGuard: CanActivateFn = (route, state) => {
// TODO get jwt && look at permissions
// inject(PermissionsService).canActivate(inject(UserToken), route. params['id']);
inject(PermissionService).hasAnyOf(route.params['id'])
return true;
let permissionService = inject(PermissionService);
return permissionService.hasAnyOf(route.data['permissions'])
};

export const isPermittedGuard: CanActivateFn = (route, state) => {
return isAuthenticatedGuard(route, state) && hasPermissionGuard(route, state)
const isAuthenticated = isAuthenticatedGuard(route, state)
if(!isAuthenticated){
return false
}

return hasPermissionGuard(route, state)
};
Loading

0 comments on commit e646b48

Please sign in to comment.