diff --git a/pom.xml b/pom.xml index 2e2f10e..3ac49ae 100644 --- a/pom.xml +++ b/pom.xml @@ -1,272 +1,278 @@ - - 4.0.0 - - com.example.application - my-app - my-app - 1.0-SNAPSHOT - jar + + my-app + + + spring-boot:run + + + spring-boot-maven-plugin + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5988 + + 240 + 500 + + org.springframework.boot + - - 17 - 24.3.0 - 2.40.0 - - + + vaadin-maven-plugin + + + + prepare-frontend + + + + com.vaadin + ${vaadin.version} + + + + spotless-maven-plugin + + + + + 4 + true + + + + com.diffplug.spotless + ${spotless.version} + + + + + + zxcvbn + com.nulab-inc + 1.8.2 + + + vaadin + + com.vaadin + + + vaadin-spring-boot-starter + com.vaadin + + + line-awesome + org.parttio + 2.0.0 + + + maven-wrapper + org.apache.maven.wrapper + 3.2.0 + - - org.springframework.boot - spring-boot-starter-parent - 3.1.5 - + + spring-boot-starter-security + org.springframework.boot + + + + spring-boot-starter-data-jpa + org.springframework.boot + + + ojdbc11 + com.oracle.database.jdbc + + + spring-boot-starter-validation + org.springframework.boot + + + spring-boot-devtools + org.springframework.boot + true + + + spring-boot-starter-test + org.springframework.boot + test + + + vaadin-testbench-junit5 + com.vaadin + test + + + + spotless-maven-plugin + com.diffplug.spotless + ${spotless.version} + + + + spring-boot-testcontainers + org.springframework.boot + test + + + lombok + org.projectlombok + + + junit-jupiter + org.testcontainers + test + + + oracle-xe + org.testcontainers + test + 1.19.2 + + + + + + vaadin-bom + com.vaadin + import + pom + ${vaadin.version} + + + + com.example.application + 4.0.0 - - - Vaadin Directory - https://maven.vaadin.com/vaadin-addons - - false - - - - maven-central - https://mvnrepository.com/artifact/ - - + my-app - - - - com.vaadin - vaadin-bom - ${vaadin.version} - pom - import - - - + jar - - - com.vaadin - - vaadin - - - com.vaadin - vaadin-spring-boot-starter - - - org.parttio - line-awesome - 2.0.0 - - - org.apache.maven.wrapper - maven-wrapper - 3.2.0 - + + spring-boot-starter-parent + org.springframework.boot + 3.1.5 + - - org.springframework.boot - spring-boot-starter-security - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - com.oracle.database.jdbc - ojdbc11 - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-devtools - true - - - org.springframework.boot - spring-boot-starter-test - test - - + + + + + + + vaadin-maven-plugin + + + + build-frontend + + compile + + com.vaadin - vaadin-testbench-junit5 - test - - - - com.diffplug.spotless - spotless-maven-plugin - ${spotless.version} - - - - org.springframework.boot - spring-boot-testcontainers - test - - - org.projectlombok - lombok - - - org.testcontainers - junit-jupiter - test - + ${vaadin.version} + + + + + - org.testcontainers - oracle-xe - 1.19.2 - test + vaadin-core + + + vaadin-dev + com.vaadin + + + com.vaadin - + + production + + production + production + + - - spring-boot:run + + - - org.springframework.boot - spring-boot-maven-plugin - - -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5988 - 500 - 240 - - + + spring-boot-maven-plugin + + + + start + + start-spring-boot + pre-integration-test + + + + stop + + stop-spring-boot + post-integration-test + + + org.springframework.boot + - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - prepare-frontend - - - - - - - com.diffplug.spotless - spotless-maven-plugin - ${spotless.version} - - - - - true - 4 - - - - + + + maven-failsafe-plugin + + true + false + + + + + integration-test + verify + + + + org.apache.maven.plugins + - + + it + - - - - production - - production - production - - - - - com.vaadin - vaadin-core - - - com.vaadin - vaadin-dev - - - - - - - - com.vaadin - vaadin-maven-plugin - ${vaadin.version} - - - - build-frontend - - compile - - - - - - + - - it - - - - org.springframework.boot - spring-boot-maven-plugin - - - start-spring-boot - pre-integration-test - - start - - - - stop-spring-boot - post-integration-test - - stop - - - - + + 17 + 2.40.0 + 24.3.0 + + - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - integration-test - verify - - - - - false - true - - - - - + + + Vaadin Directory + + false + + https://maven.vaadin.com/vaadin-addons + + + maven-central + https://mvnrepository.com/artifact/ + + - + 1.0-SNAPSHOT \ No newline at end of file diff --git a/src/main/java/com/samic/samic/components/form/UserForm.java b/src/main/java/com/samic/samic/components/form/UserForm.java index e6a7769..1a21ed5 100644 --- a/src/main/java/com/samic/samic/components/form/UserForm.java +++ b/src/main/java/com/samic/samic/components/form/UserForm.java @@ -1,15 +1,22 @@ package com.samic.samic.components.form; +import com.nulabinc.zxcvbn.Zxcvbn; import com.samic.samic.components.UIFactory; import com.samic.samic.data.entity.Profile; import com.samic.samic.data.entity.Role; import com.samic.samic.data.entity.User; +import com.vaadin.flow.component.Text; import com.vaadin.flow.component.combobox.ComboBox; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.icon.Icon; +import com.vaadin.flow.component.icon.VaadinIcon; 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.data.binder.Binder; import com.vaadin.flow.data.validator.EmailValidator; +import com.vaadin.flow.data.value.ValueChangeMode; import jakarta.annotation.PostConstruct; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -25,8 +32,10 @@ public class UserForm extends VerticalLayout { private final TextField lastname = new TextField("Nachname"); private final TextField username = new TextField("Benutzername"); private final ComboBox role = new ComboBox<>("Rolle"); - private final Binder binderUser = new Binder<>(User.class); + private final Zxcvbn zxcvbn = new Zxcvbn(); + private Icon checkIcon = VaadinIcon.CHECK.create(); + private Span passwordStrengthText; @PostConstruct @@ -41,6 +50,28 @@ private void initUI() { initBinder(); initRolesComboBox(); initRolesData(); + initPasswordfield(); + } + + private void initPasswordfield() { + checkIcon.setVisible(false); + password.setClassName("Password"); + checkIcon.getStyle().set("color", "var(--lumo-success-color)"); + password.setSuffixComponent(checkIcon); + + Div passwordStrength = new Div(); + passwordStrengthText = new Span(); + passwordStrength.add(new Text("Password strength: "), + passwordStrengthText); + password.setHelperComponent(passwordStrength); + + password.setValueChangeMode(ValueChangeMode.EAGER); + password.addValueChangeListener(e -> { + String password = e.getValue(); + updateHelper(password); + }); + updateHelper(""); + } private void initBinder() { @@ -55,6 +86,37 @@ private void initBinder() { binderUser.forField(role).asRequired().bind(User::getRole, User::setRole); } + private void updateHelper(String password) { + int strength = zxcvbn.measure(password).getScore(); + + passwordStrengthText.setText(String.valueOf(strength)); + + switch (strength) { + case 0 -> { + passwordStrengthText.setText("very weak"); + passwordStrengthText.getStyle().set("color", "#FF0800"); + } + case 1 -> { + passwordStrengthText.setText("weak"); + passwordStrengthText.getStyle().set("color", "#FF5601"); + } + case 2 -> { + passwordStrengthText.setText("medium"); + passwordStrengthText.getStyle().set("color", "#FF9B01"); + } + case 3 -> { + passwordStrengthText.setText("strong"); + passwordStrengthText.getStyle().set("color", "#300A233"); + checkIcon.setVisible(true); + } + case 4 -> { + passwordStrengthText.setText("very strong"); + passwordStrengthText.getStyle().set("color", "#00A233"); + checkIcon.setVisible(true); + } + } + } + private void initRolesComboBox() { role.setItemLabelGenerator(Role::getLongVersion); role.setAllowCustomValue(false); @@ -76,11 +138,12 @@ public User saveBean() { public Boolean isValid() { binderUser.validate(); - return binderUser.isValid(); + return binderUser.isValid() + /*&& zxcvbn.measure(binderUser.getBean().getHashedPassword()).getScore() > 3*/; } public void clearFields() { - binderUser.setBean(User.builder().profile(Profile.builder().build()).build()); + binderUser.setBean(User.builder().activated(false).profile(Profile.builder().build()).build()); passwordConfirm.setValue(""); role.setValue(null); } diff --git a/src/main/java/com/samic/samic/views/dashboard/DashboardView.java b/src/main/java/com/samic/samic/views/dashboard/DashboardView.java index 35e54ad..a620b46 100644 --- a/src/main/java/com/samic/samic/views/dashboard/DashboardView.java +++ b/src/main/java/com/samic/samic/views/dashboard/DashboardView.java @@ -18,12 +18,12 @@ import com.samic.samic.data.entity.Supply; import com.samic.samic.data.entity.User; import com.samic.samic.security.AuthenticatedUser; +import com.samic.samic.security.UserDetailsServiceImpl; import com.samic.samic.services.ServiceObjectType; import com.samic.samic.services.ServiceProducer; import com.samic.samic.services.ServiceReservation; import com.samic.samic.services.ServiceStorage; import com.samic.samic.services.ServiceStorageObject; -import com.samic.samic.services.ServiceUser; import com.samic.samic.views.MainLayout; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.contextmenu.MenuItem; @@ -61,24 +61,24 @@ public class DashboardView extends VerticalLayout implements BeforeEnterObserver private final UserForm userForm; private final StorageForm storageForm; private final ServiceObjectType objectTypeService; - private final ServiceStorage stoageService; + private final ServiceStorage storageService; private final ServiceProducer producerService; private final ComboBox storageComboBox = new ComboBox<>("Lager"); private final ComboBox producerComboBox = new ComboBox<>("Hersteller"); private final ReservationGrid reservationGrid; private final StorageObjectGrid hardwareGrid; - private final ServiceStorage storageService; - private final ServiceUser userService; + private final UserDetailsServiceImpl userDetailsService; public DashboardView(ServiceReservation reservationService, ServiceStorageObject storageObjectService, AuthenticatedUser authenticatedUser, ServiceProducer producerService, ServiceObjectType serviceObjectType, CPEForm cpeForm, SFPForm sfpForm, SupplyForm supplyForm, UserForm userForm, - ServiceObjectType objectTypeService, ServiceStorage stoageService, StorageForm storageForm, + ServiceObjectType objectTypeService, ServiceStorage storageService, StorageForm storageForm, ReservationGrid reservationGrid, - StorageObjectGrid hardwareGrid, ServiceStorage storageService, ServiceUser userService) { + StorageObjectGrid hardwareGrid, + UserDetailsServiceImpl userDetailsService) { this.reservationService = reservationService; this.storageObjectService = storageObjectService; this.authenticatedUser = authenticatedUser; @@ -88,15 +88,16 @@ public DashboardView(ServiceReservation reservationService, this.supplyForm = supplyForm; this.userForm = userForm; this.objectTypeService = objectTypeService; - this.stoageService = stoageService; + this.storageService = storageService; this.storageForm = storageForm; this.reservationGrid = reservationGrid; this.hardwareGrid = hardwareGrid; - this.storageService = storageService; - this.userService = userService; this.producerService = producerService; + this.userDetailsService = userDetailsService; initUI(); + initDropdowns(); + } private void initUI() { @@ -110,6 +111,17 @@ private void initUI() { } } + private void initDropdowns() { + storageComboBox.setItems(storageService.findAll().toList()); + producerComboBox.setItems(producerService.findAll().toList()); + + storageComboBox.setItemLabelGenerator(Storage::getName); + storageComboBox.setAllowCustomValue(false); + + producerComboBox.setAllowCustomValue(false); + producerComboBox.setItemLabelGenerator(Producer::getName); + } + private void initStats() { // STATS - Container VerticalLayout statsContainer = UIFactory.rootComponentContainer("Statistiken"); @@ -190,12 +202,6 @@ private Dialog createDialog(SFPForm sfpForm) { Dialog dialog = new Dialog(); dialog.setCloseOnOutsideClick(false); - storageComboBox.setItems(stoageService.findAll().toList()); - storageComboBox.setItemLabelGenerator(Storage::getName); - storageComboBox.setAllowCustomValue(false); - producerComboBox.setItems(producerService.findAll().toList()); - producerComboBox.setAllowCustomValue(false); - producerComboBox.setItemLabelGenerator(Producer::getName); dialog.add(producerComboBox, storageComboBox, sfpForm); dialog.add(UIFactory.btnPrimary("Speichern", e -> { @@ -237,12 +243,6 @@ private Dialog createDialog(CPEForm form) { Dialog dialog = new Dialog(); dialog.setCloseOnOutsideClick(false); - storageComboBox.setItems(stoageService.findAll().toList()); - storageComboBox.setItemLabelGenerator(Storage::getName); - storageComboBox.setAllowCustomValue(false); - producerComboBox.setItems(producerService.findAll().toList()); - producerComboBox.setAllowCustomValue(false); - producerComboBox.setItemLabelGenerator(Producer::getName); dialog.add(producerComboBox, storageComboBox, form); dialog.add(UIFactory.btnPrimary("Speichern", e -> { @@ -271,7 +271,7 @@ private Dialog createDialog(TextField field) { private void onSetUser(String id) { StorageObject storageObject = storageObjectService.findStorageObjectById(Long.valueOf(id)); storageObject.setStoredAtUser(authenticatedUser.getUser().get()); - UIFactory.notificationSuccess("Lagerobjekt umgebucht").open(); + UIFactory.notificationSuccess("Lagerobjekt umgebucht").close(); storageObjectService.saveStorageObject(storageObject); hardwareGrid.getDataProvider().refreshAll(); } @@ -281,8 +281,9 @@ private void onSave(User user) { UIFactory.notificationError("Benutzer konnte nicht gespeichert werden").open(); return; } - var persisted = userService.saveUser(user); + userDetailsService.register(user); UIFactory.notificationSuccess("Benutzer erfolgreich gespeichert").open(); + userForm.clearFields(); } private void onSave(Storage storage) { @@ -321,13 +322,11 @@ private void onSave(StorageObject storageObject) { } private void initHardware() { - // MY_HARDWARE - PREPARE GRID hardwareGrid.isAllRowsVisible(); hardwareGrid.setMaxHeight("300px"); hardwareGrid.getStyle().setBorder("0px"); initHardwareData(); - // MY_HARDWARE - PUT UI TOGETHER add(UIFactory.rootComponentContainer("Meine Hardware", hardwareGrid)); } @@ -338,7 +337,6 @@ private void initHardwareData() { } private void initReservation() { - // MY_RESERVATIONS reservationGrid.isAllRowsVisible(); reservationGrid.setMaxHeight("300px"); reservationGrid.getStyle().setBorder("0px"); diff --git a/src/main/java/com/samic/samic/views/login/LoginView.java b/src/main/java/com/samic/samic/views/login/LoginView.java index e37e3de..34f9d2c 100644 --- a/src/main/java/com/samic/samic/views/login/LoginView.java +++ b/src/main/java/com/samic/samic/views/login/LoginView.java @@ -4,7 +4,10 @@ import com.vaadin.flow.component.login.LoginI18n; import com.vaadin.flow.component.login.LoginOverlay; import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.router.*; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; import com.vaadin.flow.router.internal.RouteUtil; import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.auth.AnonymousAllowed; @@ -14,46 +17,50 @@ @Route(value = "login") public class LoginView extends VerticalLayout implements BeforeEnterObserver { -private final AuthenticatedUser authenticatedUser; + private static final String USERNAME_LABEL = "Benutzername"; + private static final String PASSWORD_LABEL = "Passwort"; + private static final boolean HAS_FORGOT_PASSWORD = false; + private static final String ANMELDE_BUTTON = "Anmelden"; + private static final String ERROR_MESSAGE = "Benutzername/Passwort ungültig"; + private static final String ERROR_TITLE = "Fehler beim Anmelden"; + private final AuthenticatedUser authenticatedUser; + private final LoginOverlay loginOverlay = new LoginOverlay(); -private final LoginOverlay loginOverlay = new LoginOverlay(); -private static final String USERNAME_LABEL = "Username"; -private static final String PASSWORD_LABEL = "Password"; -private static final boolean HAS_FORGOT_PASSWORD = false; + public LoginView(AuthenticatedUser authenticatedUser) { + this.authenticatedUser = authenticatedUser; + initUI(); + } -public LoginView(AuthenticatedUser authenticatedUser) { - this.authenticatedUser = authenticatedUser; - initUI(); -} - -private void initUI() { - LoginI18n i18n = LoginI18n.createDefault(); - i18n.setHeader(new LoginI18n.Header()); - i18n.getForm().setUsername(USERNAME_LABEL); - i18n.getForm().setPassword(PASSWORD_LABEL); - - loginOverlay.setI18n(i18n); - loginOverlay.setForgotPasswordButtonVisible(HAS_FORGOT_PASSWORD); - loginOverlay.setOpened(true); - add(loginOverlay); - // Prevent the example from stealing focus when browsing the - // documentation - loginOverlay.getElement().setAttribute("no-autofocus", ""); - // Login does not work without this one - loginOverlay.setAction( - RouteUtil.getRoutePath(VaadinService.getCurrent().getContext(), getClass())); -} + private void initUI() { + LoginI18n i18n = LoginI18n.createDefault(); + i18n.setHeader(new LoginI18n.Header()); + i18n.getForm().setUsername(USERNAME_LABEL); + i18n.getForm().setPassword(PASSWORD_LABEL); + i18n.getForm().setSubmit(ANMELDE_BUTTON); + i18n.getErrorMessage().setMessage(ERROR_MESSAGE); + i18n.getErrorMessage().setTitle(ERROR_TITLE); + loginOverlay.setI18n(i18n); + loginOverlay.setForgotPasswordButtonVisible(HAS_FORGOT_PASSWORD); + loginOverlay.setOpened(true); + add(loginOverlay); + // Prevent the example from stealing focus when browsing the + // documentation + loginOverlay.getElement().setAttribute("no-autofocus", ""); + // Login does not work without this one + loginOverlay.setAction( + RouteUtil.getRoutePath(VaadinService.getCurrent().getContext(), getClass())); + } -@Override -public void beforeEnter(BeforeEnterEvent event) { + @Override + public void beforeEnter(BeforeEnterEvent event) { - if (authenticatedUser.getUser().isPresent()) { - // Already logged in - // setOpened(false); - event.forwardTo(""); - } + if (authenticatedUser.getUser().isPresent()) { + // Already logged in + // setOpened(false); + event.forwardTo(""); + } - loginOverlay.setError( - event.getLocation().getQueryParameters().getParameters().containsKey("error")); -} + loginOverlay.setError( + event.getLocation().getQueryParameters().getParameters().containsKey("error")); + } } diff --git a/src/main/java/com/samic/samic/views/meine_hardware/MeineHardwareView.java b/src/main/java/com/samic/samic/views/meine_hardware/MeineHardwareView.java index 5dc99db..6a49b66 100644 --- a/src/main/java/com/samic/samic/views/meine_hardware/MeineHardwareView.java +++ b/src/main/java/com/samic/samic/views/meine_hardware/MeineHardwareView.java @@ -82,7 +82,15 @@ private void initReservationGrid() { UIFactory.btnIconWithTooltip(LineAwesomeIcon.TRASH_SOLID.create(), "Löschen", e -> onDelete(item)), UIFactory.btnIconWithTooltip(LineAwesomeIcon.EDIT.create(), "Bearbeiten", - e -> onEdit(item)))); + e -> onEdit(item)), + UIFactory.btnIconWithTooltip(LineAwesomeIcon.GET_POCKET.create(), "Gerät zu sich " + + "holen", + e -> moveToUser(item)))); + } + + private void moveToUser(Reservation item) { + storageObjectService.moveFromReservationToMeineHardware(item); + } private void initReservationGridData() {