diff --git a/pom.xml b/pom.xml
index 730b6c2..e303a17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
de.assecutor.votianlt
votianlt
- 0.8.1
+ 0.8.2
jar
diff --git a/src/main/java/de/assecutor/votianlt/model/User.java b/src/main/java/de/assecutor/votianlt/model/User.java
index 79c9be1..8835f39 100644
--- a/src/main/java/de/assecutor/votianlt/model/User.java
+++ b/src/main/java/de/assecutor/votianlt/model/User.java
@@ -60,4 +60,7 @@ public class User {
// Digitale Abwicklung und App-Nutzer Ortung
private boolean digitalProcessingEnabled = true; // Digitale Abwicklung per App
private boolean locationTrackingEnabled = true; // App-Nutzer orten
+
+ // 2-Faktor-Authentifizierung (standardmäßig aktiviert für neue Nutzer)
+ private boolean twoFactorEnabled = true;
}
\ No newline at end of file
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java b/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java
index 5fefa4b..2729961 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
@Route(value = "add-app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@RolesAllowed({ "USER", "ADMIN" })
public class AddAppUserView extends VerticalLayout {
+ private final AppUserService appUserService;
private final Binder binder = new Binder<>(AppUser.class);
// Form fields
@@ -38,6 +39,7 @@ public class AddAppUserView extends VerticalLayout {
@Autowired
public AddAppUserView(AppUserService appUserService) {
+ this.appUserService = appUserService;
setSizeFull();
setPadding(true);
setSpacing(true);
@@ -179,6 +181,9 @@ public class AddAppUserView extends VerticalLayout {
AppUser newAppUser = new AppUser();
binder.writeBean(newAppUser);
+ // Save to database
+ appUserService.createAppUser(newAppUser);
+
// Show success message
Notification.show("App-Nutzer erfolgreich angelegt", 3000, Notification.Position.MIDDLE);
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java
index 1e12b4b..9eca7a3 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java
@@ -394,11 +394,19 @@ public class EditProfileView extends HorizontalLayout {
// 2-Faktor Auth
Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
+ twoFactor.setValue(currentUser.isTwoFactorEnabled());
+ twoFactor.addValueChangeListener(e -> currentUser.setTwoFactorEnabled(e.getValue()));
Icon twoFactorInfo = VaadinIcon.QUESTION_CIRCLE_O.create();
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
twoFactorLayout.setAlignItems(Alignment.CENTER);
- securityTab.add(twoFactorLayout);
+
+ Span twoFactorDescription = new Span("Bei Aktivierung wird bei jeder Anmeldung ein Code per E-Mail gesendet");
+ twoFactorDescription.getStyle().set("font-size", "var(--lumo-font-size-s)")
+ .set("color", "var(--lumo-secondary-text-color)")
+ .set("margin-left", "var(--lumo-space-xl)");
+
+ securityTab.add(twoFactorLayout, twoFactorDescription);
// Passwort ändern Button
Button changePassword = new Button("Passwort ändern");
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/LoginView.java b/src/main/java/de/assecutor/votianlt/pages/view/LoginView.java
index d3269b3..0735fb8 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/LoginView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/LoginView.java
@@ -19,6 +19,8 @@ import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.security.totp.TwoFactorService;
+import de.assecutor.votianlt.repository.UserRepository;
+import de.assecutor.votianlt.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -49,8 +51,11 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
@Autowired
private Environment environment;
+ @Autowired
+ private UserRepository userRepository;
+
@Value("${app.security.two-factor.enabled:false}")
- private boolean twoFactorEnabled;
+ private boolean twoFactorEnabledGlobal;
@Value("${app.version:unknown}")
private String appVersion;
@@ -133,7 +138,12 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
Authentication auth = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
- if (twoFactorEnabled) {
+ // Prüfe ob 2FA für diesen Nutzer aktiviert ist (global UND nutzer-spezifisch)
+ boolean userTwoFactorEnabled = userRepository.findByEmail(username)
+ .map(User::isTwoFactorEnabled)
+ .orElse(true); // Standardmäßig aktiviert falls Nutzer nicht gefunden
+
+ if (twoFactorEnabledGlobal && userTwoFactorEnabled) {
// 2FA aktiviert: Benutzer noch nicht in SecurityContext setzen
this.pendingAuth = auth;
twoFaField.setVisible(true);
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java b/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java
index 8658d7f..b5de6f0 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java
@@ -77,8 +77,8 @@ public class ShowJobsView extends VerticalLayout {
startDate.addValueChangeListener(e -> loadData());
endDate.addValueChangeListener(e -> loadData());
- // Configure grid columns: Kunde, Auftragsnummer, Auftragsdatum, Zielort
- grid.addColumn(Job::getDeliveryCompany).setHeader("Kunde").setAutoWidth(true).setFlexGrow(1).setSortable(true);
+ // Configure grid columns: Auftraggeber, Auftragsnummer, Auftragsdatum, Zielort
+ grid.addColumn(job -> extractCompanyName(job.getCustomerSelection())).setHeader("Auftraggeber").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.addColumn(Job::getJobNumber).setHeader("Auftragsnummer").setAutoWidth(true).setSortable(true);
grid.addColumn(Job::getCreatedAt).setHeader("Auftragsdatum").setAutoWidth(true).setSortable(true);
grid.addColumn(Job::getDeliveryCity).setHeader("Zielort").setAutoWidth(true).setFlexGrow(1).setSortable(true);
@@ -160,11 +160,11 @@ public class ShowJobsView extends VerticalLayout {
private String generateCsv(java.util.List jobs) {
StringBuilder csv = new StringBuilder();
// CSV Header
- csv.append("Kunde,Auftragsnummer,Auftragsdatum,Zielort\n");
+ csv.append("Auftraggeber,Auftragsnummer,Auftragsdatum,Zielort\n");
// CSV Data
for (Job job : jobs) {
- csv.append(escapeCsv(job.getDeliveryCompany())).append(",");
+ csv.append(escapeCsv(extractCompanyName(job.getCustomerSelection()))).append(",");
csv.append(escapeCsv(job.getJobNumber())).append(",");
csv.append(job.getCreatedAt() != null ? job.getCreatedAt().toString() : "").append(",");
csv.append(escapeCsv(job.getDeliveryCity())).append("\n");
@@ -181,4 +181,16 @@ public class ShowJobsView extends VerticalLayout {
}
return value;
}
+
+ private String extractCompanyName(String customerSelection) {
+ if (customerSelection == null || customerSelection.isBlank()) {
+ return "";
+ }
+ // Format: "Firmenname | Vorname Nachname" - extrahiere nur den Firmennamen
+ int separatorIndex = customerSelection.indexOf(" | ");
+ if (separatorIndex > 0) {
+ return customerSelection.substring(0, separatorIndex).trim();
+ }
+ return customerSelection.trim();
+ }
}
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/StartView.java b/src/main/java/de/assecutor/votianlt/pages/view/StartView.java
index 10719f1..f71aa60 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/StartView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/StartView.java
@@ -17,6 +17,7 @@ import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.security.SecurityService;
+import org.springframework.beans.factory.annotation.Value;
@Route("")
@PageTitle("VotianLT - Willkommen")
@@ -24,9 +25,11 @@ import de.assecutor.votianlt.security.SecurityService;
public class StartView extends VerticalLayout implements BeforeEnterObserver {
private final SecurityService securityService;
+ private final String appVersion;
- public StartView(SecurityService securityService) {
+ public StartView(SecurityService securityService, @Value("${app.version:unknown}") String appVersion) {
this.securityService = securityService;
+ this.appVersion = appVersion;
setSizeFull();
setPadding(false);
setSpacing(false);
@@ -324,7 +327,13 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver {
slogan.getStyle().set("font-style", "italic");
slogan.getStyle().set("color", "var(--lumo-primary-color)");
- footer.add(companyTitle, companyInfo, ctaText, slogan);
+ // Versionsnummer
+ Span versionSpan = new Span("Version " + appVersion);
+ versionSpan.getStyle().set("color", "var(--lumo-secondary-text-color)")
+ .set("font-size", "var(--lumo-font-size-s)")
+ .set("margin-top", "var(--lumo-space-l)");
+
+ footer.add(companyTitle, companyInfo, ctaText, slogan, versionSpan);
return footer;
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index e6ecb06..45749b3 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -56,8 +56,8 @@ spring.servlet.multipart.max-request-size=64MB
# Jackson message converter limits
spring.jackson.default-property-inclusion=non_null
-# 2FA Configuration
-app.security.two-factor.enabled=false
+# 2FA Configuration (global toggle - individual users can disable in their profile)
+app.security.two-factor.enabled=true
# Message Delivery Layer Configuration
app.messaging.delivery.max-retries=3