Erweiterungen

This commit is contained in:
2025-08-20 09:46:59 +02:00
parent 4d691d9b11
commit 73415def42
11 changed files with 168 additions and 126 deletions

View File

@@ -1,23 +1,4 @@
# Emulatorstation README
Bauen im Produktionsmodus:
- [ ] TODO Replace or update this README with instructions relevant to your application
./mvnw -Pproduction clean package -DskipTests
To start the application in development mode, import it into your IDE and run the `Application` class.
You can also start the application from the command line by running:
```bash
./mvnw
```
To build the application in production mode, run:
```bash
./mvnw -Pproduction package
```
## Getting Started
The [Getting Started](https://vaadin.com/docs/latest/getting-started) guide will quickly familiarize you with your new
Emulatorstation implementation. You'll learn how to set up your development environment, understand the project
structure, and find resources to help you add muscles to your skeleton — transforming it into a fully-featured
application.

View File

@@ -6,7 +6,7 @@
<groupId>de.assecutor.emulatorstation</groupId>
<artifactId>emulatorstation</artifactId>
<version>1.0-SNAPSHOT</version>
<version>0.9</version>
<packaging>jar</packaging>
@@ -71,6 +71,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>

Binary file not shown.

View File

@@ -60,8 +60,8 @@ public class AdminView extends Main {
private void logout() {
VaadinService.getCurrentRequest().getWrappedSession().invalidate();
// Navigiere den Benutzer zur Login-Seite
UI.getCurrent().navigate("");
// Navigiere den Benutzer zur Main-Seite
UI.getCurrent().navigate("main");
}
private void savePreferences() {

View File

@@ -1,17 +1,13 @@
package de.assecutor.emulatorstation.base.ui.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinSession;
import de.assecutor.emulatorstation.Application;
import com.vaadin.flow.server.auth.AnonymousAllowed;
@Route("")
@Route("login")
@AnonymousAllowed
public class LoginView extends VerticalLayout {
public LoginView() {
@@ -20,45 +16,14 @@ public class LoginView extends VerticalLayout {
Span title = new Span("Anmelden");
title.getStyle().set("font-size", "24px").set("font-weight", "bold");
TextField usernameField = new TextField("Benutzername");
PasswordField passwordField = new PasswordField("Passwort");
LoginForm loginForm = new LoginForm();
loginForm.setAction("login");
loginForm.setForgotPasswordButtonVisible(false);
Button loginButton = new Button("Login", event -> {
String username = usernameField.getValue();
String password = passwordField.getValue();
if (authenticate(username, password)) {
var currentSession = VaadinSession.getCurrent().getSession();
currentSession.setMaxInactiveInterval(60); // Timeout in Sekunden
currentSession.setAttribute("username", username);
VaadinService.getCurrentRequest().getWrappedSession().setAttribute("user", username);
Notification.show("Anmeldung erfolgreich!");
if (username.equals("admin")) {
getUI().ifPresent(ui -> ui.navigate(AdminView.class));
} else {
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
} else {
Notification.show("Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Daten!", 3000, Notification.Position.MIDDLE);
}
});
loginButton.getStyle().set("margin-top", "10px");
add(title, usernameField, passwordField, loginButton);
add(title, loginForm);
if (MainLayout.instance != null) {
MainLayout.instance.setDrawerOpened(false);
}
// Einfache Authentifizierung (hier nur als Beispiel, später durch echte Logik ersetzen)
private boolean authenticate(String username, String password) {
if (Application.users.containsKey(username)) {
var userPassword = Application.users.get(username);
return userPassword.equals(password);
}
return false;
}
}

View File

@@ -46,7 +46,7 @@ public final class MainLayout extends AppLayout {
ui.addPollListener(event -> {
if (!isUserSessionValid()) {
VaadinSession.getCurrent().getSession().invalidate(); // Session invalidieren
ui.access(() -> ui.navigate("login")); // Benutzer zur Login-Seite weiterleiten
ui.access(() -> ui.navigate("main")); // Immer zur Main-Seite weiterleiten
}
});
}

View File

@@ -15,6 +15,7 @@ import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.emulatorstation.pojo.ExecResponse;
import jakarta.annotation.security.PermitAll;
@@ -29,6 +30,7 @@ import java.util.concurrent.Executors;
@PermitAll // When security is enabled, allow all authenticated users
@Route("main")
@AnonymousAllowed
public final class MainView extends Main implements BeforeEnterObserver
{
private final String username;
@@ -101,10 +103,7 @@ public final class MainView extends Main implements BeforeEnterObserver
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
String user = (String) VaadinService.getCurrentRequest().getWrappedSession().getAttribute("user");
if (user == null) {
beforeEnterEvent.rerouteTo("login");
}
// Kein Redirect mehr zur Login-Seite bei fehlender Session
}
private void startup() {
@@ -414,8 +413,8 @@ public final class MainView extends Main implements BeforeEnterObserver
vaadinSession.invalidate();
// Navigiere den Benutzer zur Login-Seite
ui.navigate("");
// Navigiere den Benutzer zur Main-Seite
ui.navigate("main");
});
}
});

View File

@@ -0,0 +1,17 @@
package de.assecutor.emulatorstation.base.ui.view;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinService;
@Route("")
public class RootView implements BeforeEnterObserver {
@Override
public void beforeEnter(BeforeEnterEvent event) {
event.rerouteTo("main");
}
}

View File

@@ -0,0 +1,69 @@
package de.assecutor.emulatorstation.base.ui.view.security;
import com.vaadin.flow.spring.security.VaadinWebSecurity;
import de.assecutor.emulatorstation.base.ui.view.LoginView;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import de.assecutor.emulatorstation.Application;
@Configuration
public class SecurityConfig extends VaadinWebSecurity {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
// FormLogin verwenden; Vaadin-LoginView
setLoginView(http, LoginView.class);
http
.formLogin(form -> form
.loginPage("/login")
.successHandler(authenticationSuccessHandler())
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/main")
);
}
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
Application.users.forEach((username, rawPassword) ->
manager.createUser(User.withUsername(username)
.password(rawPassword)
.roles("USER")
.build())
);
return manager;
}
@Bean
@SuppressWarnings("deprecation")
public PasswordEncoder passwordEncoder() {
// Für Demo-Zwecke: Passwörter sind im Klartext hinterlegt
return NoOpPasswordEncoder.getInstance();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return (request, response, authentication) -> {
var session = request.getSession(true);
String username = authentication.getName();
session.setAttribute("user", username);
session.setAttribute("username", username);
response.sendRedirect("/main");
};
}
}

View File

@@ -0,0 +1,57 @@
package de.assecutor.emulatorstation.base.ui.view.security;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.server.VaadinServiceInitListener;
import com.vaadin.flow.shared.Registration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VaadinAccessHandler {
@Bean
public VaadinServiceInitListener registerSessionGuard() {
return event -> event.getSource().addUIInitListener(uiInit -> {
UI ui = uiInit.getUI();
Registration reg = ui.addBeforeEnterListener(this::guardRoute);
ui.addDetachListener(detach -> reg.remove());
});
}
private void guardRoute(BeforeEnterEvent event) {
var request = VaadinService.getCurrentRequest();
if (request == null) {
if (!event.getLocation().getPath().equals("main")) {
event.rerouteTo("main");
}
return;
}
var session = request.getWrappedSession(false);
if (session == null) {
if (!event.getLocation().getPath().equals("main")) {
event.rerouteTo("main");
}
return;
}
// Login-Route immer erlauben
String path = event.getLocation().getPath();
if (path.equals("login")) {
return;
}
// Wenn keine Benutzer-Session vorhanden ist, immer zu "main"
Object user = session.getAttribute("user");
if (user == null) {
if (!path.equals("main")) {
event.rerouteTo("main");
}
}
}
}

View File

@@ -1,50 +0,0 @@
package de.assecutor.emulatorstation.taskmanagement.ui.view;
import de.assecutor.emulatorstation.base.ui.component.ViewToolbar;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Menu;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import jakarta.annotation.security.PermitAll;
import java.time.Clock;
@Route("task-list")
@PageTitle("Task List")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List")
@PermitAll // When security is enabled, allow all authenticated users
public class TaskListView extends Main {
final TextField description;
final DatePicker dueDate;
final Button createBtn;
public TaskListView() {
description = new TextField();
description.setPlaceholder("What do you want to do?");
description.setAriaLabel("Task description");
description.setMinWidth("20em");
dueDate = new DatePicker();
dueDate.setPlaceholder("Due date");
dueDate.setAriaLabel("Due date");
createBtn = new Button("Create", event -> createTask());
createBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
setSizeFull();
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
add(new ViewToolbar("Task List", ViewToolbar.group(description, dueDate, createBtn)));
}
private void createTask() {
}
}