Erweiterungen
This commit is contained in:
@@ -5,6 +5,7 @@ import de.assecutor.votianlt.repository.AppUserRepository;
|
|||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -16,14 +17,22 @@ public class AppUserService {
|
|||||||
|
|
||||||
private final AppUserRepository appUserRepository;
|
private final AppUserRepository appUserRepository;
|
||||||
private final SecurityService securityService;
|
private final SecurityService securityService;
|
||||||
|
private final BCryptPasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AppUserService(AppUserRepository appUserRepository, SecurityService securityService) {
|
public AppUserService(AppUserRepository appUserRepository, SecurityService securityService) {
|
||||||
this.appUserRepository = appUserRepository;
|
this.appUserRepository = appUserRepository;
|
||||||
this.securityService = securityService;
|
this.securityService = securityService;
|
||||||
|
this.passwordEncoder = new BCryptPasswordEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppUser createAppUser(AppUser appUser) {
|
public AppUser createAppUser(AppUser appUser) {
|
||||||
|
// Hash the password before saving
|
||||||
|
if (appUser.getPassword() != null && !appUser.getPassword().isEmpty()) {
|
||||||
|
String hashedPassword = passwordEncoder.encode(appUser.getPassword());
|
||||||
|
appUser.setPassword(hashedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
// Set creation and update metadata
|
// Set creation and update metadata
|
||||||
appUser.setErstelltAm(java.time.LocalDateTime.now());
|
appUser.setErstelltAm(java.time.LocalDateTime.now());
|
||||||
appUser.setAktualisiertAm(java.time.LocalDateTime.now());
|
appUser.setAktualisiertAm(java.time.LocalDateTime.now());
|
||||||
@@ -51,10 +60,29 @@ public class AppUserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AppUser updateAppUser(AppUser appUser) {
|
public AppUser updateAppUser(AppUser appUser) {
|
||||||
|
// Hash the password if it's being updated and not empty
|
||||||
|
if (appUser.getPassword() != null && !appUser.getPassword().isEmpty()) {
|
||||||
|
// Only hash if it's not already hashed (BCrypt hashes start with $2a$, $2b$, or $2y$)
|
||||||
|
if (!appUser.getPassword().startsWith("$2")) {
|
||||||
|
String hashedPassword = passwordEncoder.encode(appUser.getPassword());
|
||||||
|
appUser.setPassword(hashedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
appUser.setAktualisiertAm(java.time.LocalDateTime.now());
|
appUser.setAktualisiertAm(java.time.LocalDateTime.now());
|
||||||
appUser.setAktualisiertVon(securityService.getCurrentUserId());
|
appUser.setAktualisiertVon(securityService.getCurrentUserId());
|
||||||
return appUserRepository.save(appUser);
|
return appUserRepository.save(appUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a plain text password against the stored hashed password
|
||||||
|
* @param plainPassword The plain text password to verify
|
||||||
|
* @param hashedPassword The stored BCrypt hashed password
|
||||||
|
* @return true if the password matches, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean verifyPassword(String plainPassword, String hashedPassword) {
|
||||||
|
return passwordEncoder.matches(plainPassword, hashedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteById(ObjectId id) {
|
public void deleteById(ObjectId id) {
|
||||||
appUserRepository.deleteById(id);
|
appUserRepository.deleteById(id);
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
|||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.AppUser;
|
import de.assecutor.votianlt.model.AppUser;
|
||||||
|
import de.assecutor.votianlt.model.AppDevice;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
|
import de.assecutor.votianlt.pages.service.AppDeviceService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
@PageTitle("App-Nutzer")
|
@PageTitle("App-Nutzer")
|
||||||
@Route(value = "app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
@Route(value = "app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@@ -21,10 +24,13 @@ import jakarta.annotation.security.RolesAllowed;
|
|||||||
public class AppUserView extends VerticalLayout {
|
public class AppUserView extends VerticalLayout {
|
||||||
|
|
||||||
private final AppUserService appUserService;
|
private final AppUserService appUserService;
|
||||||
|
private final AppDeviceService appDeviceService;
|
||||||
private final Grid<AppUser> appUserGrid;
|
private final Grid<AppUser> appUserGrid;
|
||||||
|
|
||||||
public AppUserView(AppUserService appUserService) {
|
@Autowired
|
||||||
|
public AppUserView(AppUserService appUserService, AppDeviceService appDeviceService) {
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
|
this.appDeviceService = appDeviceService;
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
@@ -57,7 +63,13 @@ public class AppUserView extends VerticalLayout {
|
|||||||
appUserGrid.addColumn(AppUser::getTelefon).setHeader("Telefon").setAutoWidth(true);
|
appUserGrid.addColumn(AppUser::getTelefon).setHeader("Telefon").setAutoWidth(true);
|
||||||
appUserGrid.addColumn(AppUser::getAppCode).setHeader("App-Code").setAutoWidth(true);
|
appUserGrid.addColumn(AppUser::getAppCode).setHeader("App-Code").setAutoWidth(true);
|
||||||
appUserGrid.addColumn(AppUser::getEmail).setHeader("E-Mail").setAutoWidth(true);
|
appUserGrid.addColumn(AppUser::getEmail).setHeader("E-Mail").setAutoWidth(true);
|
||||||
appUserGrid.addColumn(AppUser::getGeraet).setHeader("Gerät").setAutoWidth(true);
|
appUserGrid.addColumn(appUser -> {
|
||||||
|
if (appUser.getAppDeviceId() != null) {
|
||||||
|
AppDevice device = appDeviceService.findById(appUser.getAppDeviceId());
|
||||||
|
return device != null ? device.getName() : "Nicht gefunden";
|
||||||
|
}
|
||||||
|
return "Kein Gerät zugewiesen";
|
||||||
|
}).setHeader("Endgerät").setAutoWidth(true);
|
||||||
|
|
||||||
// Make grid rows clickable
|
// Make grid rows clickable
|
||||||
appUserGrid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
appUserGrid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.vaadin.flow.component.notification.Notification;
|
|||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
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.component.textfield.TextField;
|
||||||
import com.vaadin.flow.data.binder.Binder;
|
import com.vaadin.flow.data.binder.Binder;
|
||||||
import com.vaadin.flow.data.binder.ValidationException;
|
import com.vaadin.flow.data.binder.ValidationException;
|
||||||
@@ -19,17 +20,23 @@ import com.vaadin.flow.router.HasUrlParameter;
|
|||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.AppUser;
|
import de.assecutor.votianlt.model.AppUser;
|
||||||
|
import de.assecutor.votianlt.model.AppDevice;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
|
import de.assecutor.votianlt.pages.service.AppDeviceService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@PageTitle("App-Nutzer bearbeiten")
|
@PageTitle("App-Nutzer bearbeiten")
|
||||||
@Route(value = "edit-app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
@Route(value = "edit-app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@RolesAllowed({"USER","ADMIN"})
|
@RolesAllowed({"USER","ADMIN"})
|
||||||
public class EditAppUserView extends VerticalLayout implements HasUrlParameter<String> {
|
public class EditAppUserView extends VerticalLayout implements HasUrlParameter<String> {
|
||||||
|
|
||||||
private final AppUserService appUserService;
|
private final AppUserService appUserService;
|
||||||
|
private final AppDeviceService appDeviceService;
|
||||||
private AppUser appUser;
|
private AppUser appUser;
|
||||||
private final Binder<AppUser> binder = new Binder<>(AppUser.class);
|
private final Binder<AppUser> binder = new Binder<>(AppUser.class);
|
||||||
|
|
||||||
@@ -40,11 +47,16 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
private final TextField phoneField = new TextField("Telefon (Mobil)");
|
private final TextField phoneField = new TextField("Telefon (Mobil)");
|
||||||
private final TextField appCodeField = new TextField("App-Code");
|
private final TextField appCodeField = new TextField("App-Code");
|
||||||
private final TextField emailField = new TextField("E-Mail-Adresse");
|
private final TextField emailField = new TextField("E-Mail-Adresse");
|
||||||
private final ComboBox<String> deviceField = new ComboBox<>("kein Gerät");
|
private final PasswordField changePasswordField = new PasswordField("Passwort ändern");
|
||||||
|
private final PasswordField confirmChangePasswordField = new PasswordField("Passwort ändern wiederholen");
|
||||||
|
private final ComboBox<AppDevice> deviceComboBox = new ComboBox<>("Endgerät");
|
||||||
|
|
||||||
|
private ObjectId previousDeviceId;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EditAppUserView(AppUserService appUserService) {
|
public EditAppUserView(AppUserService appUserService, AppDeviceService appDeviceService) {
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
|
this.appDeviceService = appDeviceService;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
@@ -102,9 +114,16 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
appCodeField.setWidthFull();
|
appCodeField.setWidthFull();
|
||||||
emailField.setWidthFull();
|
emailField.setWidthFull();
|
||||||
|
|
||||||
|
// Configure password fields
|
||||||
|
changePasswordField.setWidthFull();
|
||||||
|
changePasswordField.setPlaceholder("Leer lassen, wenn nicht ändern");
|
||||||
|
confirmChangePasswordField.setWidthFull();
|
||||||
|
confirmChangePasswordField.setPlaceholder("Leer lassen, wenn nicht ändern");
|
||||||
|
|
||||||
// Configure device dropdown
|
// Configure device dropdown
|
||||||
deviceField.setItems("kein Gerät", "iPhone", "Android", "Tablet", "Desktop");
|
deviceComboBox.setWidthFull();
|
||||||
deviceField.setWidthFull();
|
deviceComboBox.setItemLabelGenerator(device -> device.getName());
|
||||||
|
deviceComboBox.setPlaceholder("Bitte wählen...");
|
||||||
|
|
||||||
// Add fields to form
|
// Add fields to form
|
||||||
formLayout.add(designationField);
|
formLayout.add(designationField);
|
||||||
@@ -112,7 +131,9 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
formLayout.add(phoneField);
|
formLayout.add(phoneField);
|
||||||
formLayout.add(appCodeField);
|
formLayout.add(appCodeField);
|
||||||
formLayout.add(emailField);
|
formLayout.add(emailField);
|
||||||
formLayout.add(deviceField);
|
formLayout.add(changePasswordField);
|
||||||
|
formLayout.add(confirmChangePasswordField);
|
||||||
|
formLayout.add(deviceComboBox);
|
||||||
|
|
||||||
contentContainer.add(formLayout);
|
contentContainer.add(formLayout);
|
||||||
|
|
||||||
@@ -146,7 +167,11 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
binder.forField(phoneField).bind(AppUser::getTelefon, AppUser::setTelefon);
|
binder.forField(phoneField).bind(AppUser::getTelefon, AppUser::setTelefon);
|
||||||
binder.forField(appCodeField).bind(AppUser::getAppCode, AppUser::setAppCode);
|
binder.forField(appCodeField).bind(AppUser::getAppCode, AppUser::setAppCode);
|
||||||
binder.forField(emailField).bind(AppUser::getEmail, AppUser::setEmail);
|
binder.forField(emailField).bind(AppUser::getEmail, AppUser::setEmail);
|
||||||
binder.forField(deviceField).bind(AppUser::getGeraet, AppUser::setGeraet);
|
binder.forField(deviceComboBox)
|
||||||
|
.bind(
|
||||||
|
appUser -> getCurrentDevice(appUser), // Get current device
|
||||||
|
(appUser, appDevice) -> appUser.setAppDeviceId(appDevice != null ? appDevice.getId() : null)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -154,13 +179,11 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
try {
|
try {
|
||||||
ObjectId appUserId = new ObjectId(parameter);
|
ObjectId appUserId = new ObjectId(parameter);
|
||||||
appUser = appUserService.findById(appUserId);
|
appUser = appUserService.findById(appUserId);
|
||||||
|
previousDeviceId = appUser.getAppDeviceId();
|
||||||
if (appUser == null) {
|
|
||||||
Notification.show("App-Nutzer nicht gefunden", 3000, Notification.Position.MIDDLE);
|
|
||||||
navigateBack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Setup device ComboBox with available devices
|
||||||
|
setupDeviceComboBox();
|
||||||
|
|
||||||
// Load app user data into form
|
// Load app user data into form
|
||||||
binder.readBean(appUser);
|
binder.readBean(appUser);
|
||||||
|
|
||||||
@@ -172,7 +195,51 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
|
|
||||||
private void saveAppUser() {
|
private void saveAppUser() {
|
||||||
try {
|
try {
|
||||||
|
// Validate password fields first
|
||||||
|
if (!validatePasswordFields()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save current password to restore if not changing
|
||||||
|
String originalPassword = appUser.getPassword();
|
||||||
|
|
||||||
binder.writeBean(appUser);
|
binder.writeBean(appUser);
|
||||||
|
|
||||||
|
// Handle password change logic
|
||||||
|
String newPassword = changePasswordField.getValue();
|
||||||
|
String confirmPassword = confirmChangePasswordField.getValue();
|
||||||
|
|
||||||
|
if (newPassword != null && !newPassword.trim().isEmpty()) {
|
||||||
|
// User wants to change password
|
||||||
|
if (confirmPassword != null && newPassword.equals(confirmPassword)) {
|
||||||
|
// Passwords match, set new password for hashing
|
||||||
|
appUser.setPassword(newPassword);
|
||||||
|
} else {
|
||||||
|
Notification.show("Passwörter stimmen nicht überein", 3000, Notification.Position.MIDDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No password change requested, restore original password
|
||||||
|
appUser.setPassword(originalPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle device assignment changes
|
||||||
|
AppDevice selectedDevice = deviceComboBox.getValue();
|
||||||
|
|
||||||
|
if (selectedDevice.getId() != previousDeviceId) {
|
||||||
|
AppDevice previousDevice = appDeviceService.findById(previousDeviceId);
|
||||||
|
if (previousDevice != null) {
|
||||||
|
previousDevice.setAppUserId(null);
|
||||||
|
appDeviceService.updateAppDevice(previousDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDevice newDevice = appDeviceService.findById(selectedDevice.getId());
|
||||||
|
if (newDevice != null) {
|
||||||
|
newDevice.setAppUserId(appUser.getId());
|
||||||
|
appDeviceService.updateAppDevice(newDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
appUserService.updateAppUser(appUser);
|
appUserService.updateAppUser(appUser);
|
||||||
Notification.show("App-Nutzer erfolgreich gespeichert", 3000, Notification.Position.MIDDLE);
|
Notification.show("App-Nutzer erfolgreich gespeichert", 3000, Notification.Position.MIDDLE);
|
||||||
navigateBack();
|
navigateBack();
|
||||||
@@ -180,6 +247,61 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
Notification.show("Bitte überprüfen Sie die Eingaben", 3000, Notification.Position.MIDDLE);
|
Notification.show("Bitte überprüfen Sie die Eingaben", 3000, Notification.Position.MIDDLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validatePasswordFields() {
|
||||||
|
String newPassword = changePasswordField.getValue();
|
||||||
|
String confirmPassword = confirmChangePasswordField.getValue();
|
||||||
|
|
||||||
|
// If one field is filled, both must be filled
|
||||||
|
boolean newPasswordFilled = newPassword != null && !newPassword.trim().isEmpty();
|
||||||
|
boolean confirmPasswordFilled = confirmPassword != null && !confirmPassword.trim().isEmpty();
|
||||||
|
|
||||||
|
if (newPasswordFilled && !confirmPasswordFilled) {
|
||||||
|
Notification.show("Bitte bestätigen Sie das neue Passwort", 3000, Notification.Position.MIDDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newPasswordFilled && confirmPasswordFilled) {
|
||||||
|
Notification.show("Bitte geben Sie das neue Passwort ein", 3000, Notification.Position.MIDDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both are filled, they must match
|
||||||
|
if (newPasswordFilled && confirmPasswordFilled && !newPassword.equals(confirmPassword)) {
|
||||||
|
Notification.show("Passwörter stimmen nicht überein", 3000, Notification.Position.MIDDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppDevice getCurrentDevice(AppUser appUser) {
|
||||||
|
if (appUser != null && appUser.getAppDeviceId() != null) {
|
||||||
|
return appDeviceService.findById(appUser.getAppDeviceId());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDeviceComboBox() {
|
||||||
|
List<AppDevice> availableDevices = new ArrayList<>();
|
||||||
|
|
||||||
|
// First, add the currently assigned device if it exists
|
||||||
|
AppDevice currentDevice = getCurrentDevice(appUser);
|
||||||
|
if (currentDevice != null) {
|
||||||
|
availableDevices.add(currentDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add all unassigned devices
|
||||||
|
List<AppDevice> unassignedDevices = appDeviceService.findUnassignedDevices();
|
||||||
|
availableDevices.addAll(unassignedDevices);
|
||||||
|
|
||||||
|
deviceComboBox.setItems(availableDevices);
|
||||||
|
|
||||||
|
// Set the current device as selected
|
||||||
|
if (currentDevice != null) {
|
||||||
|
deviceComboBox.setValue(currentDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteAppUser() {
|
private void deleteAppUser() {
|
||||||
// Show confirmation dialog
|
// Show confirmation dialog
|
||||||
|
|||||||
Reference in New Issue
Block a user