custom userdetails implementation

This commit is contained in:
alex 2024-09-18 10:27:49 -04:00
parent 1f429221d4
commit 451242bea4
7 changed files with 130 additions and 28 deletions

View File

@ -1,17 +1,26 @@
package com.primefactorsolutions.config; package com.primefactorsolutions.config;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.primefactorsolutions.views.LoginView; import com.primefactorsolutions.views.LoginView;
import com.vaadin.flow.spring.security.VaadinWebSecurity; import com.vaadin.flow.spring.security.VaadinWebSecurity;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory; 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.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.Collection;
@EnableWebSecurity @EnableWebSecurity
@Configuration @Configuration
public class SecurityConfig extends VaadinWebSecurity { public class SecurityConfig extends VaadinWebSecurity {
@ -37,13 +46,30 @@ public class SecurityConfig extends VaadinWebSecurity {
} }
@Bean @Bean
public AuthenticationManager authenticationManager() { public AuthenticationManager authenticationManager(final UserDetailsContextMapper userDetailsContextMapper) {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource( DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://localhost:8389/dc=primefactorsolutions,dc=com"); "ldap://localhost:8389/dc=primefactorsolutions,dc=com");
contextSource.setCacheEnvironmentProperties(false); contextSource.setCacheEnvironmentProperties(false);
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource); LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=users"); factory.setUserDnPatterns("uid={0},ou=users");
factory.setUserDetailsContextMapper(userDetailsContextMapper);
return factory.createAuthenticationManager(); 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<? extends GrantedAuthority> 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;
}
};
}
} }

View File

@ -1,19 +1,23 @@
package com.primefactorsolutions.model; package com.primefactorsolutions.model;
import com.google.common.collect.Lists;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collection;
@Data @Data
@Entity @Entity
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity { public class Employee extends BaseEntity implements UserDetails {
private String userName; private String username;
private String firstName; private String firstName;
private String lastName; private String lastName;
private LocalDate birthday; private LocalDate birthday;
@ -33,6 +37,42 @@
private String profileImage; private String profileImage;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Status status; private Status status;
@Override
public Collection<? extends GrantedAuthority> 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 { public enum Status {
ACTIVE, ACTIVE,
INACTIVE INACTIVE

View File

@ -3,7 +3,9 @@ package com.primefactorsolutions.repositories;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
public interface EmployeeRepository extends JpaRepository<Employee, UUID> { public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByUsername(String username);
} }

View File

@ -1,5 +1,6 @@
package com.primefactorsolutions.service; package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Employee; import com.primefactorsolutions.model.Employee;
import jakarta.persistence.EntityManager;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.beanutils.BeanComparator;
import com.primefactorsolutions.repositories.EmployeeRepository; import com.primefactorsolutions.repositories.EmployeeRepository;
@ -19,16 +20,28 @@ import java.util.Collections;
public class EmployeeService { public class EmployeeService {
private final EmployeeRepository employeeRepository; private final EmployeeRepository employeeRepository;
private final LdapTemplate ldapTemplate; private final LdapTemplate ldapTemplate;
private final EntityManager entityManager;
public static final String BASE_DN = "dc=primefactorsolutions,dc=com"; public static final String BASE_DN = "dc=primefactorsolutions,dc=com";
protected Name buildDn(final Employee employee) { protected Name buildDn(final Employee employee) {
return LdapNameBuilder.newInstance(BASE_DN) return LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "users") .add("ou", "users")
.add("uid", employee.getUserName()) .add("uid", employee.getUsername())
.build(); .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<Employee> findEmployees( public List<Employee> findEmployees(
final int start, final int pageSize, final String sortProperty, final boolean asc) { final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Employee> employees = employeeRepository.findAll(); List<Employee> employees = employeeRepository.findAll();
@ -75,14 +88,14 @@ public class EmployeeService {
attrs.put(ocattr); attrs.put(ocattr);
attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName())); attrs.put("cn", String.format("%s %s", employee.getFirstName(), employee.getLastName()));
attrs.put("sn", 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("uid", employee.getUsername());
attrs.put("userpassword", String.format("%s%s", employee.getUserName(), 123)); attrs.put("userpassword", String.format("%s%s", employee.getUsername(), 123));
return attrs; return attrs;
} }
public void updatePassword(final Employee employee) { 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); final ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item}); ldapTemplate.modifyAttributes(buildDn(employee), new ModificationItem[] {item});

View File

@ -46,7 +46,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
private final EmployeeService employeeService; private final EmployeeService employeeService;
// TODO: campo usado para registrar al empleado en LDAP. Este campo podria estar en otro form eventualmente. // 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 firstName = createTextField("Nombres: ", 30, true);
private final TextField lastName = createTextField("Apellidos", 30, true); private final TextField lastName = createTextField("Apellidos", 30, true);
private final ComboBox<Employee.Status> status = createStatusComboBox(); private final ComboBox<Employee.Status> status = createStatusComboBox();
@ -301,7 +301,7 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
@Override @Override
protected List<Component> getFormComponents() { protected List<Component> getFormComponents() {
return List.of( 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, residenceAddress, phoneNumber, personalEmail, position, team, ss, emergencyCName,
emergencyCAddress, emergencyCPhone, emergencyCEmail, si, upload, profileImagePreview, emergencyCAddress, emergencyCPhone, emergencyCEmail, si, upload, profileImagePreview,
saveButton, editButton saveButton, editButton

View File

@ -1,5 +1,6 @@
package com.primefactorsolutions.views; package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.applayout.DrawerToggle;
import com.vaadin.flow.component.button.Button; 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.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller; 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.SideNav;
import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.PageTitle; 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.springframework.security.core.userdetails.UserDetails;
import org.vaadin.lineawesome.LineAwesomeIcon; import org.vaadin.lineawesome.LineAwesomeIcon;
import java.util.UUID;
/** /**
* The main view is a top-level placeholder for other views. * The main view is a top-level placeholder for other views.
*/ */
@ -44,10 +48,25 @@ public class MainLayout extends AppLayout {
header = header =
authContext.getAuthenticatedUser(UserDetails.class) authContext.getAuthenticatedUser(UserDetails.class)
.map(user -> { .map(user -> {
Button logout = new Button("Logout", click -> this.authContext.logout()); final Button logout = new Button("Logout", click -> this.authContext.logout());
Span loggedUser = new Span("Welcome " + user.getUsername()); final Span loggedUser = new Span("Welcome " + user.getUsername());
HorizontalLayout hl = new HorizontalLayout(loggedUser, logout); 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); hl.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
return hl; return hl;
}).orElseGet(HorizontalLayout::new); }).orElseGet(HorizontalLayout::new);
header.setAlignItems(FlexComponent.Alignment.STRETCH); header.setAlignItems(FlexComponent.Alignment.STRETCH);

View File

@ -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 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, username, first_name, last_name, status) values ('5c6f11fe-c341-4be7-a9a6-bba0081ad7c6', 1, 'bob', 'Bob', 'Test', 'ACTIVE');
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, username, first_name, last_name, status) values ('cba3efb7-32bc-44be-9fdc-fc5e4f211254', 1, 'ben', 'Ben', 'Test', '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, username, 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 ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'mfernandez', 'Maria', 'Fernandez Villca', 'ACTIVE'); 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, user_name, 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 ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'clopez', 'Carlos', 'Lopez Mendoza', 'INACTIVE');
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, username, 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 ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'rsantos', 'Roberto', 'Santos Escobar', '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, user_name, 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 ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'lmartinez', 'Laura', 'Martinez Paredes', '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, username, 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 ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'storres', 'Sandra', 'Torres Huanca', '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, user_name, 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 ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'jramirez', 'Jorge', 'Ramirez Tapia', 'ACTIVE');
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, username, 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 ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'oflores', 'Oscar', 'Flores Quiroga', 'INACTIVE'); 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, user_name, 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 ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'grivas', 'Gabriela', 'Rivas Arana', '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, username, 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 ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'cvillanueva', 'Carla', 'Villanueva Arce', 'ACTIVE'); 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');