Erweiterungen
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>de.assecutor.votianlt</groupId>
|
<groupId>de.assecutor.votianlt</groupId>
|
||||||
<artifactId>votianlt</artifactId>
|
<artifactId>votianlt</artifactId>
|
||||||
<version>0.8.1</version>
|
<version>0.8.2</version>
|
||||||
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
|||||||
@@ -60,4 +60,7 @@ public class User {
|
|||||||
// Digitale Abwicklung und App-Nutzer Ortung
|
// Digitale Abwicklung und App-Nutzer Ortung
|
||||||
private boolean digitalProcessingEnabled = true; // Digitale Abwicklung per App
|
private boolean digitalProcessingEnabled = true; // Digitale Abwicklung per App
|
||||||
private boolean locationTrackingEnabled = true; // App-Nutzer orten
|
private boolean locationTrackingEnabled = true; // App-Nutzer orten
|
||||||
|
|
||||||
|
// 2-Faktor-Authentifizierung (standardmäßig aktiviert für neue Nutzer)
|
||||||
|
private boolean twoFactorEnabled = true;
|
||||||
}
|
}
|
||||||
@@ -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)
|
@Route(value = "add-app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@RolesAllowed({ "USER", "ADMIN" })
|
@RolesAllowed({ "USER", "ADMIN" })
|
||||||
public class AddAppUserView extends VerticalLayout {
|
public class AddAppUserView extends VerticalLayout {
|
||||||
|
private final AppUserService appUserService;
|
||||||
private final Binder<AppUser> binder = new Binder<>(AppUser.class);
|
private final Binder<AppUser> binder = new Binder<>(AppUser.class);
|
||||||
|
|
||||||
// Form fields
|
// Form fields
|
||||||
@@ -38,6 +39,7 @@ public class AddAppUserView extends VerticalLayout {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AddAppUserView(AppUserService appUserService) {
|
public AddAppUserView(AppUserService appUserService) {
|
||||||
|
this.appUserService = appUserService;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
@@ -179,6 +181,9 @@ public class AddAppUserView extends VerticalLayout {
|
|||||||
AppUser newAppUser = new AppUser();
|
AppUser newAppUser = new AppUser();
|
||||||
binder.writeBean(newAppUser);
|
binder.writeBean(newAppUser);
|
||||||
|
|
||||||
|
// Save to database
|
||||||
|
appUserService.createAppUser(newAppUser);
|
||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
Notification.show("App-Nutzer erfolgreich angelegt", 3000, Notification.Position.MIDDLE);
|
Notification.show("App-Nutzer erfolgreich angelegt", 3000, Notification.Position.MIDDLE);
|
||||||
|
|
||||||
|
|||||||
@@ -394,11 +394,19 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
|
|
||||||
// 2-Faktor Auth
|
// 2-Faktor Auth
|
||||||
Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
|
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();
|
Icon twoFactorInfo = VaadinIcon.QUESTION_CIRCLE_O.create();
|
||||||
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
|
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
|
||||||
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
|
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
|
||||||
twoFactorLayout.setAlignItems(Alignment.CENTER);
|
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
|
// Passwort ändern Button
|
||||||
Button changePassword = new Button("Passwort ändern");
|
Button changePassword = new Button("Passwort ändern");
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import com.vaadin.flow.router.PageTitle;
|
|||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
import de.assecutor.votianlt.security.totp.TwoFactorService;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@@ -49,8 +51,11 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
|
|||||||
@Autowired
|
@Autowired
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@Value("${app.security.two-factor.enabled:false}")
|
@Value("${app.security.two-factor.enabled:false}")
|
||||||
private boolean twoFactorEnabled;
|
private boolean twoFactorEnabledGlobal;
|
||||||
|
|
||||||
@Value("${app.version:unknown}")
|
@Value("${app.version:unknown}")
|
||||||
private String appVersion;
|
private String appVersion;
|
||||||
@@ -133,7 +138,12 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
|
|||||||
Authentication auth = authenticationManager
|
Authentication auth = authenticationManager
|
||||||
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
|
.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
|
// 2FA aktiviert: Benutzer noch nicht in SecurityContext setzen
|
||||||
this.pendingAuth = auth;
|
this.pendingAuth = auth;
|
||||||
twoFaField.setVisible(true);
|
twoFaField.setVisible(true);
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ public class ShowJobsView extends VerticalLayout {
|
|||||||
startDate.addValueChangeListener(e -> loadData());
|
startDate.addValueChangeListener(e -> loadData());
|
||||||
endDate.addValueChangeListener(e -> loadData());
|
endDate.addValueChangeListener(e -> loadData());
|
||||||
|
|
||||||
// Configure grid columns: Kunde, Auftragsnummer, Auftragsdatum, Zielort
|
// Configure grid columns: Auftraggeber, Auftragsnummer, Auftragsdatum, Zielort
|
||||||
grid.addColumn(Job::getDeliveryCompany).setHeader("Kunde").setAutoWidth(true).setFlexGrow(1).setSortable(true);
|
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::getJobNumber).setHeader("Auftragsnummer").setAutoWidth(true).setSortable(true);
|
||||||
grid.addColumn(Job::getCreatedAt).setHeader("Auftragsdatum").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);
|
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<Job> jobs) {
|
private String generateCsv(java.util.List<Job> jobs) {
|
||||||
StringBuilder csv = new StringBuilder();
|
StringBuilder csv = new StringBuilder();
|
||||||
// CSV Header
|
// CSV Header
|
||||||
csv.append("Kunde,Auftragsnummer,Auftragsdatum,Zielort\n");
|
csv.append("Auftraggeber,Auftragsnummer,Auftragsdatum,Zielort\n");
|
||||||
|
|
||||||
// CSV Data
|
// CSV Data
|
||||||
for (Job job : jobs) {
|
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(escapeCsv(job.getJobNumber())).append(",");
|
||||||
csv.append(job.getCreatedAt() != null ? job.getCreatedAt().toString() : "").append(",");
|
csv.append(job.getCreatedAt() != null ? job.getCreatedAt().toString() : "").append(",");
|
||||||
csv.append(escapeCsv(job.getDeliveryCity())).append("\n");
|
csv.append(escapeCsv(job.getDeliveryCity())).append("\n");
|
||||||
@@ -181,4 +181,16 @@ public class ShowJobsView extends VerticalLayout {
|
|||||||
}
|
}
|
||||||
return value;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.vaadin.flow.router.BeforeEnterEvent;
|
|||||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
@Route("")
|
@Route("")
|
||||||
@PageTitle("VotianLT - Willkommen")
|
@PageTitle("VotianLT - Willkommen")
|
||||||
@@ -24,9 +25,11 @@ import de.assecutor.votianlt.security.SecurityService;
|
|||||||
public class StartView extends VerticalLayout implements BeforeEnterObserver {
|
public class StartView extends VerticalLayout implements BeforeEnterObserver {
|
||||||
|
|
||||||
private final SecurityService securityService;
|
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.securityService = securityService;
|
||||||
|
this.appVersion = appVersion;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
@@ -324,7 +327,13 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver {
|
|||||||
slogan.getStyle().set("font-style", "italic");
|
slogan.getStyle().set("font-style", "italic");
|
||||||
slogan.getStyle().set("color", "var(--lumo-primary-color)");
|
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;
|
return footer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ spring.servlet.multipart.max-request-size=64MB
|
|||||||
# Jackson message converter limits
|
# Jackson message converter limits
|
||||||
spring.jackson.default-property-inclusion=non_null
|
spring.jackson.default-property-inclusion=non_null
|
||||||
|
|
||||||
# 2FA Configuration
|
# 2FA Configuration (global toggle - individual users can disable in their profile)
|
||||||
app.security.two-factor.enabled=false
|
app.security.two-factor.enabled=true
|
||||||
|
|
||||||
# Message Delivery Layer Configuration
|
# Message Delivery Layer Configuration
|
||||||
app.messaging.delivery.max-retries=3
|
app.messaging.delivery.max-retries=3
|
||||||
|
|||||||
Reference in New Issue
Block a user