Erweiterungen
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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<AppUser> 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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Job> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user