diff --git a/README.md b/README.md index cfb9ad2..ef5b496 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/pom.xml b/pom.xml index d3679ce..7fe0693 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ de.assecutor.emulatorstation emulatorstation - 1.0-SNAPSHOT + 0.9 jar @@ -71,6 +71,10 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-validation diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle new file mode 100644 index 0000000..3168f43 Binary files /dev/null and b/src/main/bundles/prod.bundle differ diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/AdminView.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/AdminView.java index d8753f2..b29b035 100644 --- a/src/main/java/de/assecutor/emulatorstation/base/ui/view/AdminView.java +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/AdminView.java @@ -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() { diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/LoginView.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/LoginView.java index 628ef17..aebe489 100644 --- a/src/main/java/de/assecutor/emulatorstation/base/ui/view/LoginView.java +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/LoginView.java @@ -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"); - - Button loginButton = new Button("Login", event -> { - String username = usernameField.getValue(); - String password = passwordField.getValue(); + LoginForm loginForm = new LoginForm(); + loginForm.setAction("login"); + loginForm.setForgotPasswordButtonVisible(false); - if (authenticate(username, password)) { - var currentSession = VaadinSession.getCurrent().getSession(); - currentSession.setMaxInactiveInterval(60); // Timeout in Sekunden - currentSession.setAttribute("username", username); + add(title, loginForm); - 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); - - 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); + if (MainLayout.instance != null) { + MainLayout.instance.setDrawerOpened(false); } - - return false; } } \ No newline at end of file diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainLayout.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainLayout.java index 21e6c9f..2f95ed0 100644 --- a/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainLayout.java +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainLayout.java @@ -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 } }); } diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainView.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainView.java index 183c3dc..36403d3 100644 --- a/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainView.java +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/MainView.java @@ -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"); }); } }); diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/RootView.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/RootView.java new file mode 100644 index 0000000..4c6cb80 --- /dev/null +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/RootView.java @@ -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"); + } +} + + diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/SecurityConfig.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/SecurityConfig.java new file mode 100644 index 0000000..6974bf8 --- /dev/null +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/SecurityConfig.java @@ -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"); + }; + } +} + + diff --git a/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/VaadinAccessHandler.java b/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/VaadinAccessHandler.java new file mode 100644 index 0000000..b34c7cf --- /dev/null +++ b/src/main/java/de/assecutor/emulatorstation/base/ui/view/security/VaadinAccessHandler.java @@ -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"); + } + } + } +} + + diff --git a/src/main/java/de/assecutor/emulatorstation/taskmanagement/ui/view/TaskListView.java b/src/main/java/de/assecutor/emulatorstation/taskmanagement/ui/view/TaskListView.java deleted file mode 100644 index cd8027a..0000000 --- a/src/main/java/de/assecutor/emulatorstation/taskmanagement/ui/view/TaskListView.java +++ /dev/null @@ -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() { - } -}