From 451242bea43413ccd6af8f8e4ebb4eff5534c323 Mon Sep 17 00:00:00 2001 From: Alex Prudencio Date: Wed, 18 Sep 2024 10:27:49 -0400 Subject: [PATCH] custom userdetails implementation --- .../config/SecurityConfig.java | 28 +++++++++++- .../primefactorsolutions/model/Employee.java | 44 ++++++++++++++++++- .../repositories/EmployeeRepository.java | 2 + .../service/EmployeeService.java | 21 +++++++-- .../views/EmployeeView.java | 4 +- .../views/MainLayout.java | 25 +++++++++-- src/main/resources/data.sql | 34 +++++++------- 7 files changed, 130 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/primefactorsolutions/config/SecurityConfig.java b/src/main/java/com/primefactorsolutions/config/SecurityConfig.java index 8d10f24..6bcfa06 100644 --- a/src/main/java/com/primefactorsolutions/config/SecurityConfig.java +++ b/src/main/java/com/primefactorsolutions/config/SecurityConfig.java @@ -1,17 +1,26 @@ package com.primefactorsolutions.config; +import com.primefactorsolutions.model.Employee; +import com.primefactorsolutions.service.EmployeeService; import com.primefactorsolutions.views.LoginView; import com.vaadin.flow.spring.security.VaadinWebSecurity; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; +import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import java.util.Collection; + @EnableWebSecurity @Configuration public class SecurityConfig extends VaadinWebSecurity { @@ -37,13 +46,30 @@ public class SecurityConfig extends VaadinWebSecurity { } @Bean - public AuthenticationManager authenticationManager() { + public AuthenticationManager authenticationManager(final UserDetailsContextMapper userDetailsContextMapper) { DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource( "ldap://localhost:8389/dc=primefactorsolutions,dc=com"); contextSource.setCacheEnvironmentProperties(false); LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); factory.setUserDnPatterns("uid={0},ou=users"); + factory.setUserDetailsContextMapper(userDetailsContextMapper); return factory.createAuthenticationManager(); } + + @Bean + public UserDetailsContextMapper userDetailsContextMapper(final EmployeeService employeeService) { + return new LdapUserDetailsMapper() { + @Override + public UserDetails mapUserFromContext(final DirContextOperations ctx, final String username, + final Collection authorities) { + final UserDetails details = super.mapUserFromContext(ctx, username, authorities); + final Employee employee = employeeService.getDetachedEmployeeByUsername(details.getUsername()); + + System.out.println(">>>>>" + employee + ">>>" + employee.getId()); + + return employee == null ? details : employee; + } + }; + } } diff --git a/src/main/java/com/primefactorsolutions/model/Employee.java b/src/main/java/com/primefactorsolutions/model/Employee.java index 4f5a49e..a8d568f 100644 --- a/src/main/java/com/primefactorsolutions/model/Employee.java +++ b/src/main/java/com/primefactorsolutions/model/Employee.java @@ -1,19 +1,23 @@ package com.primefactorsolutions.model; + import com.google.common.collect.Lists; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; + import org.springframework.security.core.GrantedAuthority; + import org.springframework.security.core.userdetails.UserDetails; import java.time.LocalDate; + import java.util.Collection; @Data @Entity @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(callSuper = true) - public class Employee extends BaseEntity { - private String userName; + public class Employee extends BaseEntity implements UserDetails { + private String username; private String firstName; private String lastName; private LocalDate birthday; @@ -33,6 +37,42 @@ private String profileImage; @Enumerated(EnumType.STRING) private Status status; + + @Override + public Collection getAuthorities() { + return Lists.newArrayList(); + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + public enum Status { ACTIVE, INACTIVE diff --git a/src/main/java/com/primefactorsolutions/repositories/EmployeeRepository.java b/src/main/java/com/primefactorsolutions/repositories/EmployeeRepository.java index 323ac5b..e27a88b 100644 --- a/src/main/java/com/primefactorsolutions/repositories/EmployeeRepository.java +++ b/src/main/java/com/primefactorsolutions/repositories/EmployeeRepository.java @@ -3,7 +3,9 @@ package com.primefactorsolutions.repositories; import com.primefactorsolutions.model.Employee; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; import java.util.UUID; public interface EmployeeRepository extends JpaRepository { + Optional findByUsername(String username); } diff --git a/src/main/java/com/primefactorsolutions/service/EmployeeService.java b/src/main/java/com/primefactorsolutions/service/EmployeeService.java index c30f9b8..94a1f64 100644 --- a/src/main/java/com/primefactorsolutions/service/EmployeeService.java +++ b/src/main/java/com/primefactorsolutions/service/EmployeeService.java @@ -1,5 +1,6 @@ package com.primefactorsolutions.service; import com.primefactorsolutions.model.Employee; +import jakarta.persistence.EntityManager; import lombok.AllArgsConstructor; import org.apache.commons.beanutils.BeanComparator; import com.primefactorsolutions.repositories.EmployeeRepository; @@ -19,16 +20,28 @@ import java.util.Collections; public class EmployeeService { private final EmployeeRepository employeeRepository; private final LdapTemplate ldapTemplate; + private final EntityManager entityManager; public static final String BASE_DN = "dc=primefactorsolutions,dc=com"; protected Name buildDn(final Employee employee) { return LdapNameBuilder.newInstance(BASE_DN) .add("ou", "users") - .add("uid", employee.getUserName()) + .add("uid", employee.getUsername()) .build(); } + public Employee getDetachedEmployeeByUsername(final String username) { + final Employee employee = employeeRepository.findByUsername(username).orElse(null); + + if (employee != null) { + entityManager.detach(employee); + return employee; + } + + return null; + } + public List findEmployees( final int start, final int pageSize, final String sortProperty, final boolean asc) { List employees = employeeRepository.findAll(); @@ -75,14 +88,14 @@ public class EmployeeService { attrs.put(ocattr); attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName())); attrs.put("sn", String.format("%s %s", employee.getFirstName(), employee.getLastName())); - attrs.put("uid", employee.getUserName()); - attrs.put("userpassword", String.format("%s%s", employee.getUserName(), 123)); + attrs.put("uid", employee.getUsername()); + attrs.put("userpassword", String.format("%s%s", employee.getUsername(), 123)); return attrs; } public void updatePassword(final Employee employee) { - final Attribute attr = new BasicAttribute("userpassword", employee.getUserName() + "123"); + final Attribute attr = new BasicAttribute("userpassword", employee.getUsername() + "123"); final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item}); diff --git a/src/main/java/com/primefactorsolutions/views/EmployeeView.java b/src/main/java/com/primefactorsolutions/views/EmployeeView.java index 8b18f3b..969b84b 100644 --- a/src/main/java/com/primefactorsolutions/views/EmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/EmployeeView.java @@ -46,7 +46,7 @@ public class EmployeeView extends BeanValidationForm implements HasUrl private final EmployeeService employeeService; // TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente. - private final TextField userName = createTextField("Username: ", 30, true); + private final TextField username = createTextField("Username: ", 30, true); private final TextField firstName = createTextField("Nombres: ", 30, true); private final TextField lastName = createTextField("Apellidos", 30, true); private final ComboBox status = createStatusComboBox(); @@ -301,7 +301,7 @@ public class EmployeeView extends BeanValidationForm implements HasUrl @Override protected List getFormComponents() { return List.of( - mt, fs, userName, firstName, lastName, status, birthday, birthCity, maritalStatus, + mt, fs, username, firstName, lastName, status, birthday, birthCity, maritalStatus, residenceAddress, phoneNumber, personalEmail, position, team, ss, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail, si, upload, profileImagePreview, saveButton, editButton diff --git a/src/main/java/com/primefactorsolutions/views/MainLayout.java b/src/main/java/com/primefactorsolutions/views/MainLayout.java index d96d150..e0dd6f6 100644 --- a/src/main/java/com/primefactorsolutions/views/MainLayout.java +++ b/src/main/java/com/primefactorsolutions/views/MainLayout.java @@ -1,5 +1,6 @@ package com.primefactorsolutions.views; +import com.primefactorsolutions.model.Employee; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.button.Button; @@ -10,6 +11,7 @@ import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.Scroller; +import com.vaadin.flow.component.shared.Tooltip; import com.vaadin.flow.component.sidenav.SideNav; import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.router.PageTitle; @@ -18,6 +20,8 @@ import com.vaadin.flow.theme.lumo.LumoUtility; import org.springframework.security.core.userdetails.UserDetails; import org.vaadin.lineawesome.LineAwesomeIcon; +import java.util.UUID; + /** * The main view is a top-level placeholder for other views. */ @@ -44,10 +48,25 @@ public class MainLayout extends AppLayout { header = authContext.getAuthenticatedUser(UserDetails.class) .map(user -> { - Button logout = new Button("Logout", click -> this.authContext.logout()); - Span loggedUser = new Span("Welcome " + user.getUsername()); - HorizontalLayout hl = new HorizontalLayout(loggedUser, logout); + final Button logout = new Button("Logout", click -> this.authContext.logout()); + final Span loggedUser = new Span("Welcome " + user.getUsername()); + String employeeId = "N/A"; + + if (user instanceof Employee) { + final UUID uuid = ((Employee) user).getId(); + + if (uuid != null) { + employeeId = uuid.toString(); + } + } + + final Tooltip tooltip = Tooltip.forComponent(loggedUser) + .withText("Employee id: " + employeeId) + .withPosition(Tooltip.TooltipPosition.TOP_START); + + final HorizontalLayout hl = new HorizontalLayout(loggedUser, logout); hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END); + return hl; }).orElseGet(HorizontalLayout::new); header.setAlignItems(FlexComponent.Alignment.STRETCH); diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index ae045c3..2a243e7 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -9,19 +9,21 @@ insert into ASSESSMENT_QUESTIONS (assessment_id, question_id) values ('46b153f4- insert into ASSESSMENT_QUESTIONS (assessment_id, question_id) values ('46b153f4-23fd-462f-8430-fbe67b83caab', '8a4b213c-ca81-4c38-b56d-d7028c2dde88'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'lgutierrez', 'Luis', 'Gutierrez Mamani', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'vmorales', 'Valeria', 'Morales Ochoa', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'fquispe', 'Felipe', 'Quispe Huanca', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE'); -insert into employee (id, version, user_name, first_name, last_name, status) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE'); \ No newline at end of file +insert into employee (id, version, username, first_name, last_name, status) values ('5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 1, 'bob', 'Bob', 'Test', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('cba3efb7-32bc-44be-9fdc-fc5e4f211254', 1, 'ben', 'Ben', 'Test', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('e99b7af5-7d3a-4c0f-b8bc-e8d0388d8fc4', 1, 'jperez', 'Juan', 'Perez Condori', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('f6ab3c6d-7078-45f6-9b22-4e37637bfec6', 1, 'agarcia', 'Ana', 'Garcia Rojas', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'lgutierrez', 'Luis', 'Gutierrez Mamani', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'vmorales', 'Valeria', 'Morales Ochoa', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'fquispe', 'Felipe', 'Quispe Huanca', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'mvargas', 'Marta', 'Vargas Soria', 'ACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'aespinoza', 'Andres', 'Espinoza Chura', 'INACTIVE'); +insert into employee (id, version, username, first_name, last_name, status) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE');