diff --git a/pom.xml b/pom.xml index acc4df5..56ecef0 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,12 @@ spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + com.mysql mysql-connector-j diff --git a/src/main/java/com/printhub/printhub/config/PasswordEncoderConfig.java b/src/main/java/com/printhub/printhub/config/PasswordEncoderConfig.java new file mode 100644 index 0000000..4152b06 --- /dev/null +++ b/src/main/java/com/printhub/printhub/config/PasswordEncoderConfig.java @@ -0,0 +1,15 @@ +package com.printhub.printhub.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/printhub/printhub/config/SecurityConfig.java b/src/main/java/com/printhub/printhub/config/SecurityConfig.java new file mode 100644 index 0000000..fdb9f53 --- /dev/null +++ b/src/main/java/com/printhub/printhub/config/SecurityConfig.java @@ -0,0 +1,50 @@ +package com.printhub.printhub.config; + +import com.printhub.printhub.service.configuration.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.*; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.*; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.*; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Autowired + private UserService userService; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/login", "/register", "/assets/**", "/api/lang", "/api/lang/**").permitAll() + .anyRequest().authenticated()) + .userDetailsService(userService) + .formLogin(login -> login + .loginPage("/login") + .loginProcessingUrl("/login") + .defaultSuccessUrl("/", true) + .failureUrl("/login?error=true") + .permitAll()) + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessHandler((request, response, authentication) -> { + String lang = request.getParameter("lang"); + if (lang == null || lang.isBlank()) lang = "es"; + response.sendRedirect("/login?logout&lang=" + lang); + })); + + + return http.build(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) + throws Exception { + return authConfig.getAuthenticationManager(); + } +} diff --git a/src/main/java/com/printhub/printhub/controller/config/CurrentUserAdvice.java b/src/main/java/com/printhub/printhub/controller/config/CurrentUserAdvice.java new file mode 100644 index 0000000..1edd804 --- /dev/null +++ b/src/main/java/com/printhub/printhub/controller/config/CurrentUserAdvice.java @@ -0,0 +1,25 @@ +package com.printhub.printhub.controller.config; + +import com.printhub.printhub.entity.configuration.User; +import com.printhub.printhub.repository.configuration.UserRepository; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.beans.factory.annotation.Autowired; + +@ControllerAdvice +public class CurrentUserAdvice { + + @Autowired + private UserRepository userRepository; + + @ModelAttribute("currentUser") + public User getCurrentUser() { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (principal instanceof UserDetails userDetails) { + return userRepository.findByUsername(userDetails.getUsername()).orElse(null); + } + return null; + } +} diff --git a/src/main/java/com/printhub/printhub/controller/configuration/AuthController.java b/src/main/java/com/printhub/printhub/controller/configuration/AuthController.java new file mode 100644 index 0000000..0b1454f --- /dev/null +++ b/src/main/java/com/printhub/printhub/controller/configuration/AuthController.java @@ -0,0 +1,48 @@ +package com.printhub.printhub.controller.configuration; + +import com.printhub.printhub.service.configuration.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import org.springframework.stereotype.Controller; + +import com.printhub.printhub.entity.configuration.User; +import com.printhub.printhub.repository.configuration.UserRepository; + +@Controller +public class AuthController { + + private UserService userService; + private UserRepository userRepository; + + @Autowired + public AuthController(UserRepository userRepository, UserService userService) { + this.userRepository = userRepository; + this.userService = userService; + } + + @PostMapping("/register") + public String register(@RequestParam String username, + @RequestParam String password, + @RequestParam(defaultValue = "USER") String role) { + userService.register(username, password, role); + return "Usuario registrado correctamente."; + } + + @GetMapping("/login") + public String login() { + return "printhub/login"; + } + + @ModelAttribute("currentUser") + public User currentUser() { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + + if (principal instanceof org.springframework.security.core.userdetails.User springUser) { + return userRepository.findByUsername(springUser.getUsername()) + .orElse(null); + } + + return null; + } +} diff --git a/src/main/java/com/printhub/printhub/controller/configuration/PrintersController.java b/src/main/java/com/printhub/printhub/controller/configuration/PrintersController.java index fa52eaf..dc1de7f 100644 --- a/src/main/java/com/printhub/printhub/controller/configuration/PrintersController.java +++ b/src/main/java/com/printhub/printhub/controller/configuration/PrintersController.java @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.printhub.printhub.model.Breadcrum; +import com.printhub.printhub.helper.Breadcrum; import com.printhub.printhub.service.configuration.PrinterService; import com.printhub.printhub.utils.datatables.DataTableRequest; diff --git a/src/main/java/com/printhub/printhub/entity/configuration/Customer.java b/src/main/java/com/printhub/printhub/entity/configuration/Customer.java new file mode 100644 index 0000000..91b8cdf --- /dev/null +++ b/src/main/java/com/printhub/printhub/entity/configuration/Customer.java @@ -0,0 +1,68 @@ +package com.printhub.printhub.entity.configuration; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "customers") +public class Customer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String alias; + private String cif; + private String address; + private String city; + private String zipCode; + private String country; + private String phone; + private String email; + + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private LocalDateTime deletedAt; + + // Getters y Setters + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getAlias() { return alias; } + public void setAlias(String alias) { this.alias = alias; } + + public String getCif() { return cif; } + public void setCif(String cif) { this.cif = cif; } + + public String getAddress() { return address; } + public void setAddress(String address) { this.address = address; } + + public String getCity() { return city; } + public void setCity(String city) { this.city = city; } + + public String getZipCode() { return zipCode; } + public void setZipCode(String zipCode) { this.zipCode = zipCode; } + + public String getCountry() { return country; } + public void setCountry(String country) { this.country = country; } + + public String getPhone() { return phone; } + public void setPhone(String phone) { this.phone = phone; } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + + public LocalDateTime getCreatedAt() { return createdAt; } + public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + + public LocalDateTime getUpdatedAt() { return updatedAt; } + public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } + + public LocalDateTime getDeletedAt() { return deletedAt; } + public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } +} diff --git a/src/main/java/com/printhub/printhub/model/configuration/Printer.java b/src/main/java/com/printhub/printhub/entity/configuration/Printer.java similarity index 90% rename from src/main/java/com/printhub/printhub/model/configuration/Printer.java rename to src/main/java/com/printhub/printhub/entity/configuration/Printer.java index 47cc4e6..97ec6b6 100644 --- a/src/main/java/com/printhub/printhub/model/configuration/Printer.java +++ b/src/main/java/com/printhub/printhub/entity/configuration/Printer.java @@ -1,4 +1,4 @@ -package com.printhub.printhub.model.configuration; +package com.printhub.printhub.entity.configuration; import jakarta.persistence.*; diff --git a/src/main/java/com/printhub/printhub/entity/configuration/User.java b/src/main/java/com/printhub/printhub/entity/configuration/User.java new file mode 100644 index 0000000..0f74fda --- /dev/null +++ b/src/main/java/com/printhub/printhub/entity/configuration/User.java @@ -0,0 +1,96 @@ +package com.printhub.printhub.entity.configuration; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String username; + + @Column(nullable = false) + private String password; + + @Column(nullable = false) + private String role; // Ej: ROLE_USER, ROLE_ADMIN + + @Column(nullable = false) + private boolean enabled = true; + + // Campos adicionales + private String firstName; + private String lastName; + + @Column(columnDefinition = "TEXT") + private String comments; + + private LocalDateTime lastActive; + + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private LocalDateTime deletedAt; + + @ManyToOne + @JoinColumn(name = "customer_id") + private Customer customer; + + // Getters y Setters + + public Long getId() { return id; } + + public void setId(Long id) { this.id = id; } + + public String getUsername() { return username; } + + public void setUsername(String username) { this.username = username; } + + public String getPassword() { return password; } + + public void setPassword(String password) { this.password = password; } + + public String getRole() { return role; } + + public void setRole(String role) { this.role = role; } + + public boolean isEnabled() { return enabled; } + + public void setEnabled(boolean enabled) { this.enabled = enabled; } + + public String getFirstName() { return firstName; } + + public void setFirstName(String firstName) { this.firstName = firstName; } + + public String getLastName() { return lastName; } + + public void setLastName(String lastName) { this.lastName = lastName; } + + public String getComments() { return comments; } + + public void setComments(String comments) { this.comments = comments; } + + public LocalDateTime getLastActive() { return lastActive; } + + public void setLastActive(LocalDateTime lastActive) { this.lastActive = lastActive; } + + public LocalDateTime getCreatedAt() { return createdAt; } + + public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + + public LocalDateTime getUpdatedAt() { return updatedAt; } + + public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } + + public LocalDateTime getDeletedAt() { return deletedAt; } + + public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } + + public Customer getCustomer() { return customer; } + + public void setCustomer(Customer customer) { this.customer = customer; } +} \ No newline at end of file diff --git a/src/main/java/com/printhub/printhub/model/Breadcrum.java b/src/main/java/com/printhub/printhub/helper/Breadcrum.java similarity index 96% rename from src/main/java/com/printhub/printhub/model/Breadcrum.java rename to src/main/java/com/printhub/printhub/helper/Breadcrum.java index d91fdf1..e3749c1 100644 --- a/src/main/java/com/printhub/printhub/model/Breadcrum.java +++ b/src/main/java/com/printhub/printhub/helper/Breadcrum.java @@ -1,4 +1,4 @@ -package com.printhub.printhub.model; +package com.printhub.printhub.helper; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/printhub/printhub/repository/configuration/PrinterRepository.java b/src/main/java/com/printhub/printhub/repository/configuration/PrinterRepository.java index 1b6a174..f7faa43 100644 --- a/src/main/java/com/printhub/printhub/repository/configuration/PrinterRepository.java +++ b/src/main/java/com/printhub/printhub/repository/configuration/PrinterRepository.java @@ -1,9 +1,10 @@ package com.printhub.printhub.repository.configuration; -import com.printhub.printhub.model.configuration.Printer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import com.printhub.printhub.entity.configuration.Printer; + import java.util.List; public interface PrinterRepository extends JpaRepository, JpaSpecificationExecutor { diff --git a/src/main/java/com/printhub/printhub/repository/configuration/UserRepository.java b/src/main/java/com/printhub/printhub/repository/configuration/UserRepository.java new file mode 100644 index 0000000..3649789 --- /dev/null +++ b/src/main/java/com/printhub/printhub/repository/configuration/UserRepository.java @@ -0,0 +1,11 @@ +package com.printhub.printhub.repository.configuration; + +import com.printhub.printhub.entity.configuration.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + + +public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); +} diff --git a/src/main/java/com/printhub/printhub/security/AuthenticationSuccessListener.java b/src/main/java/com/printhub/printhub/security/AuthenticationSuccessListener.java new file mode 100644 index 0000000..27092b5 --- /dev/null +++ b/src/main/java/com/printhub/printhub/security/AuthenticationSuccessListener.java @@ -0,0 +1,30 @@ +package com.printhub.printhub.security; + +import com.printhub.printhub.entity.configuration.User; +import com.printhub.printhub.repository.configuration.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +@Component +public class AuthenticationSuccessListener implements ApplicationListener { + + @Autowired + private UserRepository userRepository; + + @Override + public void onApplicationEvent(AuthenticationSuccessEvent event) { + Object principal = event.getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails userDetails) { + userRepository.findByUsername(userDetails.getUsername()).ifPresent(user -> { + user.setLastActive(LocalDateTime.now()); + userRepository.save(user); + }); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/printhub/printhub/service/configuration/PrinterService.java b/src/main/java/com/printhub/printhub/service/configuration/PrinterService.java index 44da2dc..447b4ee 100644 --- a/src/main/java/com/printhub/printhub/service/configuration/PrinterService.java +++ b/src/main/java/com/printhub/printhub/service/configuration/PrinterService.java @@ -1,6 +1,6 @@ package com.printhub.printhub.service.configuration; -import com.printhub.printhub.model.configuration.Printer; +import com.printhub.printhub.entity.configuration.Printer; import com.printhub.printhub.repository.configuration.PrinterRepository; import com.printhub.printhub.specification.configuration.PrinterSpecification; import com.printhub.printhub.utils.datatables.DataTableBuilder; diff --git a/src/main/java/com/printhub/printhub/service/configuration/UserService.java b/src/main/java/com/printhub/printhub/service/configuration/UserService.java new file mode 100644 index 0000000..d0d1c17 --- /dev/null +++ b/src/main/java/com/printhub/printhub/service/configuration/UserService.java @@ -0,0 +1,50 @@ +package com.printhub.printhub.service.configuration; + +import com.printhub.printhub.entity.configuration.User; +import com.printhub.printhub.repository.configuration.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.*; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Optional; + +@Service +public class UserService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + public void register(String username, String password, String role) { + User user = new User(); + user.setUsername(username); + user.setPassword(passwordEncoder.encode(password)); + user.setRole("ROLE_" + role.toUpperCase()); + user.setEnabled(true); + userRepository.save(user); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Optional userOpt = userRepository.findByUsername(username); + if (userOpt.isEmpty() || userOpt.get().getDeletedAt() != null) { + throw new UsernameNotFoundException("Usuario no encontrado o eliminado"); + } + + User user = userOpt.get(); + return new org.springframework.security.core.userdetails.User( + user.getUsername(), + user.getPassword(), + user.isEnabled(), + true, + true, + user.getDeletedAt() == null, + Collections.singletonList(new SimpleGrantedAuthority(user.getRole())) + ); + } +} diff --git a/src/main/java/com/printhub/printhub/specification/configuration/PrinterSpecification.java b/src/main/java/com/printhub/printhub/specification/configuration/PrinterSpecification.java index 7707ce8..828063b 100644 --- a/src/main/java/com/printhub/printhub/specification/configuration/PrinterSpecification.java +++ b/src/main/java/com/printhub/printhub/specification/configuration/PrinterSpecification.java @@ -1,6 +1,6 @@ package com.printhub.printhub.specification.configuration; -import com.printhub.printhub.model.configuration.Printer; +import com.printhub.printhub.entity.configuration.Printer; import com.printhub.printhub.utils.datatables.DataTableRequest; import org.springframework.data.jpa.domain.Specification; diff --git a/src/main/resources/i18n/en/general.properties b/src/main/resources/i18n/en/general.properties index 403dc26..9cf2ec2 100644 --- a/src/main/resources/i18n/en/general.properties +++ b/src/main/resources/i18n/en/general.properties @@ -1 +1,10 @@ -t-paginas=Pages \ No newline at end of file +# Breadcrumbs +t-paginas=Pages + +# Roles +t-role.ROLE_ADMIN=Administrator +t-role.ROLE_USER=User + +# Topbar +t-topbar-logout=Logout +t-topbar-profile=Profile \ No newline at end of file diff --git a/src/main/resources/i18n/en/login.properties b/src/main/resources/i18n/en/login.properties new file mode 100644 index 0000000..591094e --- /dev/null +++ b/src/main/resources/i18n/en/login.properties @@ -0,0 +1,16 @@ +t-login-title=Welcome to Print Hub! +t-login-subtitle=Sign in to continue +t-login-username=Username +t-login-password=Password +t-login-button=Sign In + +t-login-password-recovery-message=Forgot your password? +t-login-password-recovery=Recover Password + +t-login-signup-message=Don't have an account ? +t-login-signup-signup=Sign Up + +t-login-logout=You have logged out successfully +t-login-error=Incorrect username or password + + diff --git a/src/main/resources/i18n/es/general.properties b/src/main/resources/i18n/es/general.properties index 3c0d0aa..9cf5c47 100644 --- a/src/main/resources/i18n/es/general.properties +++ b/src/main/resources/i18n/es/general.properties @@ -1 +1,10 @@ -t-paginas=Páginas \ No newline at end of file +# Breadcrumbs +t-paginas=Páginas + +# Roles +t-role.ROLE_ADMIN=Administrador +t-role.ROLE_USER=Usuario + +# Topbar +t-topbar-logout=Cerrar sesión +t-topbar-profile=Perfil \ No newline at end of file diff --git a/src/main/resources/i18n/es/login.properties b/src/main/resources/i18n/es/login.properties new file mode 100644 index 0000000..7754ec9 --- /dev/null +++ b/src/main/resources/i18n/es/login.properties @@ -0,0 +1,14 @@ +t-login-title=¡Bienvenido a Print Hub! +t-login-subtitle=Inicia sesión para continuar +t-login-username=Nombre de usuario +t-login-password=Contraseña +t-login-button=Iniciar sesión + +t-login-password-recovery-message=¿Olvidaste tu contraseña? +t-login-password-recovery=Recuperar contraseña + +t-login-signup-message=¿No tienes una cuenta? +t-login-signup-signup=Registrarse + +t-login-logout=Has cerrado sesión correctamente +t-login-error=Nombre de usuario o contraseña incorrectos diff --git a/src/main/resources/templates/printhub/layout.html b/src/main/resources/templates/printhub/layout.html index 1db50be..0d22117 100644 --- a/src/main/resources/templates/printhub/layout.html +++ b/src/main/resources/templates/printhub/layout.html @@ -49,6 +49,7 @@ + \ No newline at end of file diff --git a/src/main/resources/templates/printhub/login.html b/src/main/resources/templates/printhub/login.html new file mode 100644 index 0000000..be8a038 --- /dev/null +++ b/src/main/resources/templates/printhub/login.html @@ -0,0 +1,168 @@ + + + + + +
+ + +
+ + + + + +
+
+ +
+
+
+
+
+
+
+
+ + +
+
+
Welcome Back !
+

Sign in to continue to + Velzon. +

+
+ +
+
+ Usuario o contraseña incorrectos. +
+
+ Has cerrado sesión correctamente. +
+
+
+ + +
+ +
+ + +
+ +
+ +
+
+
+ +
+

+ Don't have an account ? + + + Signup + +

+
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+

© + PrintHub by JJO & IMN +

+
+
+
+
+
+ +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/printhub/partials/topbar.html b/src/main/resources/templates/printhub/partials/topbar.html index edbf3bb..30a8d49 100644 --- a/src/main/resources/templates/printhub/partials/topbar.html +++ b/src/main/resources/templates/printhub/partials/topbar.html @@ -27,7 +27,9 @@ -
-
-
@@ -101,7 +126,7 @@ - +