Implemeting spring security auth

This commit is contained in:
alex 2024-05-13 23:05:24 -04:00
parent 344477881f
commit 0afb90c068
14 changed files with 179 additions and 44 deletions

View File

@ -5,11 +5,11 @@ 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.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@EnableWebSecurity
@ -25,13 +25,18 @@ public class SecurityConfig extends VaadinWebSecurity {
setLoginView(http, LoginView.class);
}
@Bean
public AuthenticationManager authenticationManager() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://ldap.primefactorsolutions.com:389/dc=primefactorsolutions,dc=com");
contextSource.setCacheEnvironmentProperties(false);
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=users");
return factory.createAuthenticationManager();
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider(final UserDetailsService userDetailsService) {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}

View File

@ -6,4 +6,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface UserRepository extends JpaRepository<User, UUID> {
User getByEmail(String email);
}

View File

@ -0,0 +1,13 @@
package com.primefactorsolutions.invoices.model;
import org.springframework.security.core.GrantedAuthority;
public enum Permission implements GrantedAuthority {
INVALIDATE_INVOICE,
EDIT_COMPANY;
@Override
public String getAuthority() {
return null;
}
}

View File

@ -1,6 +1,24 @@
package com.primefactorsolutions.invoices.model;
public enum Role {
ADMIN,
REGULAR
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import java.util.List;
@Getter
public enum Role implements GrantedAuthority {
ADMIN(List.of(Permission.EDIT_COMPANY, Permission.INVALIDATE_INVOICE)),
REGULAR(List.of());
private final List<Permission> permissions;
Role(final List<Permission> permissions) {
this.permissions = permissions;
}
@Override
public String getAuthority() {
return this.name();
}
}

View File

@ -1,25 +1,33 @@
package com.primefactorsolutions.invoices.model;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "user_")
@Data
public class User extends AbstractEntity {
public class User extends AbstractEntity implements UserDetails {
@NotBlank
private String email;
@NotBlank
private String fullName;
private Role role;
@Type(JsonType.class)
@Column(columnDefinition = "json")
private List<Role> roles;
private String password;
@NotNull
private Status status;
@ -28,4 +36,34 @@ public class User extends AbstractEntity {
@JoinColumn(name = "company_id")
@NotNull
private Company company;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().flatMap(r -> r.getPermissions().stream()).toList();
}
@Override
public String getUsername() {
return this.email;
}
@Override
public boolean isAccountNonExpired() {
return this.status != Status.DISABLED;
}
@Override
public boolean isAccountNonLocked() {
return this.status != Status.DISABLED;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.status == Status.ACTIVE;
}
}

View File

@ -0,0 +1,55 @@
package com.primefactorsolutions.invoices.services;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.i18n.I18NProvider;
import lombok.Data;
import org.springframework.stereotype.Service;
import java.util.Locale;
@Service
@Data
public class ComponentBuilder {
private final I18NProvider i18NProvider;
public ButtonBuilder button(String text) {
return ButtonBuilder.builder().i18NProvider(i18NProvider).text(text);
}
@Data
public static class ButtonBuilder {
private String text;
private I18NProvider i18NProvider;
private ButtonVariant buttonVariant;
public static ButtonBuilder builder() {
return new ButtonBuilder();
}
public Button build() {
final Button button = new Button(i18NProvider.getTranslation(text, Locale.of("es")));
if (buttonVariant != null) {
button.addThemeVariants(buttonVariant);
}
return button;
}
public ButtonBuilder primary() {
this.setButtonVariant(ButtonVariant.LUMO_PRIMARY);
return this;
}
public ButtonBuilder i18NProvider(I18NProvider i18NProvider) {
this.setI18NProvider(i18NProvider);
return this;
}
public ButtonBuilder text(String text) {
this.setText(text);
return this;
}
}
}

View File

@ -5,6 +5,9 @@ import com.primefactorsolutions.invoices.model.Role;
import com.primefactorsolutions.invoices.model.Status;
import com.primefactorsolutions.invoices.model.User;
import lombok.Data;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
@ -12,7 +15,7 @@ import java.util.UUID;
@Service
@Data
public class UserService {
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final CompanyService companyService;
@ -28,10 +31,15 @@ public class UserService {
if (updatedUser.getId() == null) {
var company = companyService.getCompany();
updatedUser.setCompany(company);
updatedUser.setRole(Role.REGULAR);
updatedUser.setRoles(List.of(Role.REGULAR));
updatedUser.setStatus(Status.ACTIVE);
}
userRepository.save(updatedUser);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.getByEmail(username);
}
}

View File

@ -2,10 +2,9 @@ package com.primefactorsolutions.invoices.views;
import com.primefactorsolutions.invoices.model.Client;
import com.primefactorsolutions.invoices.services.ClientService;
import com.primefactorsolutions.invoices.services.ComponentBuilder;
import com.primefactorsolutions.invoices.views.component.Breadcrumbs;
import com.primefactorsolutions.invoices.views.component.GenericForm;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.i18n.I18NProvider;
@ -20,7 +19,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import static com.primefactorsolutions.invoices.utils.UiUtils.goTo;
@ -34,26 +32,26 @@ public class ClientEditView extends VerticalLayout implements HasUrlParameter<St
ClientService clientService;
I18NProvider i18NProvider;
ComponentBuilder componentBuilder;
GenericForm<Client> clientGenericForm;
public ClientEditView(ClientService clientService, I18NProvider i18NProvider) {
public ClientEditView(ClientService clientService, I18NProvider i18NProvider, ComponentBuilder componentBuilder) {
this.clientService = clientService;
this.i18NProvider = i18NProvider;
this.componentBuilder = componentBuilder;
var client = new Client();
this.clientGenericForm = new GenericForm<>(Client.class);
this.clientGenericForm.setBean(client);
String text = this.i18NProvider.getTranslation("action.save", Locale.of("es"));
var saveButton = new Button(text);
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
var saveButton = this.componentBuilder.button("action.save").primary().build();
saveButton.addClickListener(c -> {
var updatedClient = clientGenericForm.getBean();
clientService.saveOrUpdateClient(updatedClient);
goTo(this, "clients");
});
var cancelButton = new Button("Cancel");
var cancelButton = this.componentBuilder.button("action.cancel").build();
cancelButton.addClickListener(c -> goTo(this, "clients"));
var breadcrumbs = new Breadcrumbs(List.of(Pair.of("Clientes", "clients"), Pair.of("Editar", null)));

View File

@ -18,6 +18,7 @@ import jakarta.annotation.security.PermitAll;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.button.VButton;
import java.util.List;
import java.util.Locale;
@ -34,7 +35,6 @@ public class ProductEditView extends VerticalLayout implements HasUrlParameter<S
ProductService productService;
I18NProvider i18NProvider;
GenericForm<Product> productGenericForm;
public ProductEditView(ProductService productService, I18NProvider i18NProvider) {
@ -54,9 +54,7 @@ public class ProductEditView extends VerticalLayout implements HasUrlParameter<S
goTo(this, "products");
});
var cancelButton = new Button("Cancel");
cancelButton.addClickListener(c -> {
goTo(this, "products");
});
cancelButton.addClickListener(c -> goTo(this, "products"));
var breadcrumbs = new Breadcrumbs(List.of(Pair.of("Products", "products"), Pair.of("Editar", null)));
var buttonLayout = new HorizontalLayout(saveButton, cancelButton);

View File

@ -1,4 +1,4 @@
package com.primefactorsolutions.invoices.views;
package com.primefactorsolutions.invoices.views.invoices;
import com.primefactorsolutions.invoices.beans.CabeceraDTO;
import com.primefactorsolutions.invoices.beans.ClientDTO;
@ -10,6 +10,7 @@ import com.primefactorsolutions.invoices.model.mappers.DetalleMapper;
import com.primefactorsolutions.invoices.services.ClientService;
import com.primefactorsolutions.invoices.services.CompanyService;
import com.primefactorsolutions.invoices.services.InvoiceService;
import com.primefactorsolutions.invoices.views.MainLayout;
import com.primefactorsolutions.invoices.views.component.invoice.*;
import com.primefactorsolutions.invoices.xsd.FacturaComputarizadaComercialExportacionServicio;
import com.vaadin.flow.component.*;

View File

@ -14,5 +14,8 @@ INSERT INTO "PRODUCT" (ID, VERSION, COMPANY_ID, STATUS, codigo_Producto_Sin, cod
precio_Unitario) VALUES ('dcecb825-7bc6-402d-850a-9d4a08fe6663', 1, '108ace19-6d78-4a70-a01f-30b9e3f6b2b8', 1, 1,
'PROD001', 'Test product', 1, 1);
INSERT INTO "USER_" (ID, VERSION, COMPANY_ID, EMAIL, FULL_NAME, ROLE, STATUS) VALUES ('b9f1bbe0-4542-48d3-b88e-452f2891cb68',
1, '108ace19-6d78-4a70-a01f-30b9e3f6b2b8', 'alex@test.com', 'alex prudencio', 1, 1);
INSERT INTO "USER_" (ID, VERSION, COMPANY_ID, EMAIL, FULL_NAME, ROLES, PASSWORD, STATUS) VALUES ('b9f1bbe0-4542-48d3-b88e-452f2891cb68',
1, '108ace19-6d78-4a70-a01f-30b9e3f6b2b8', 'admin@test.com', 'admin test', '[0]' FORMAT JSON, '$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', 0);
INSERT INTO "USER_" (ID, VERSION, COMPANY_ID, EMAIL, FULL_NAME, ROLES, PASSWORD, STATUS) VALUES ('547274a6-bb73-462d-85ab-b529fde798fb',
1, '108ace19-6d78-4a70-a01f-30b9e3f6b2b8', 'user@test.com', 'user test', '[1]' FORMAT JSON, '$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW', 0);

View File

@ -1 +1,2 @@
action.save=Save
action.cancel=Cancel

View File

@ -1 +1,2 @@
action.save=Guardar
action.cancel=Cancelar

View File

@ -1,12 +1,7 @@
package com.primefactorsolutions.invoices.views.list;
import com.primefactorsolutions.invoices.views.FacturaComputarizadaComercialExportacionesServicioEditView;
import com.vaadin.flow.component.Component;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
public class InvoiceFormTest {
@Test
public void formShownWhenContactSelected() {