diff --git a/build.gradle b/build.gradle index 16e0e67..d45838f 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,11 @@ dependencies { implementation 'org.webjars:bootstrap:5.1.3' implementation 'org.webjars:jquery:3.6.0' implementation 'org.webjars:font-awesome:6.1.0' + + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' + implementation group: 'org.springframework.security', name: 'spring-security-web' + implementation group: 'org.springframework.security', name: 'spring-security-config' } tasks.named('test') { diff --git a/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java b/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..8cbafdc --- /dev/null +++ b/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java @@ -0,0 +1,14 @@ +package ip.labwork.configuration; + +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 PasswordEncoderConfiguration { + @Bean + public PasswordEncoder createPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/ip/labwork/configuration/SecurityConfiguration.java b/src/main/java/ip/labwork/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..e8efd7b --- /dev/null +++ b/src/main/java/ip/labwork/configuration/SecurityConfiguration.java @@ -0,0 +1,76 @@ +package ip.labwork.configuration; + +import ip.labwork.user.controller.UserSignupMvcController; +import ip.labwork.user.model.UserRole; +import ip.labwork.user.service.UserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true) +public class SecurityConfiguration { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private static final String LOGIN_URL = "/login"; + private final UserService userService; + public SecurityConfiguration(UserService userService) { + this.userService = userService; + createAdminOnStartup(); + } + + private void createAdminOnStartup() { + final String admin = "admin"; + if (userService.findByLogin(admin) == null) { + log.info("Admin user successfully created"); + userService.createUser(admin, admin, admin, UserRole.ADMIN); + } + } + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.headers().frameOptions().sameOrigin().and() + .cors().and() + .csrf().disable() + .authorizeRequests() + .requestMatchers(UserSignupMvcController.SIGNUP_URL).permitAll() + .requestMatchers(HttpMethod.GET, LOGIN_URL).permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage(LOGIN_URL).permitAll() + .and() + .logout().permitAll(); + return http.build(); + } + @Bean + public AuthenticationManager authenticationManager(HttpSecurity http, PasswordEncoderConfiguration bCryptPasswordEncoder) + throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class) + .userDetailsService(userService) + .passwordEncoder(bCryptPasswordEncoder.createPasswordEncoder()) + .and() + .build(); + } + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring() + .requestMatchers("/css/**") + .requestMatchers("/js/**") + .requestMatchers("/templates/**") + .requestMatchers("/webjars/**"); + } +} \ No newline at end of file diff --git a/src/main/java/ip/labwork/WebConfiguration.java b/src/main/java/ip/labwork/configuration/WebConfiguration.java similarity index 85% rename from src/main/java/ip/labwork/WebConfiguration.java rename to src/main/java/ip/labwork/configuration/WebConfiguration.java index 0913c9c..0aa47b3 100644 --- a/src/main/java/ip/labwork/WebConfiguration.java +++ b/src/main/java/ip/labwork/configuration/WebConfiguration.java @@ -1,28 +1,28 @@ -package ip.labwork; +package ip.labwork.configuration; import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.ViewControllerRegistration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.http.HttpStatus; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfiguration implements WebMvcConfigurer { public static final String REST_API = "/api"; - @Override - public void addCorsMappings(CorsRegistry registry){ - registry.addMapping("/**").allowedMethods("*"); - } + @Override public void addViewControllers(ViewControllerRegistry registry) { + WebMvcConfigurer.super.addViewControllers(registry); ViewControllerRegistration registration = registry.addViewController("/notFound"); registration.setViewName("forward:/index.html"); registration.setStatusCode(HttpStatus.OK); + registry.addViewController("rest-test"); + registry.addViewController("login"); } @Bean @@ -31,4 +31,8 @@ public class WebConfiguration implements WebMvcConfigurer { container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound")); }; } + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedMethods("*"); + } } diff --git a/src/main/java/ip/labwork/method/controller/MethodController.java b/src/main/java/ip/labwork/method/controller/MethodController.java index 3fa801f..6fa7be9 100644 --- a/src/main/java/ip/labwork/method/controller/MethodController.java +++ b/src/main/java/ip/labwork/method/controller/MethodController.java @@ -1,6 +1,6 @@ package ip.labwork.method.controller; -import ip.labwork.WebConfiguration; +import ip.labwork.configuration.WebConfiguration; import ip.labwork.method.service.MethodService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/src/main/java/ip/labwork/shop/controller/ComponentController.java b/src/main/java/ip/labwork/shop/controller/ComponentController.java index e3fc83a..afdc639 100644 --- a/src/main/java/ip/labwork/shop/controller/ComponentController.java +++ b/src/main/java/ip/labwork/shop/controller/ComponentController.java @@ -1,6 +1,6 @@ package ip.labwork.shop.controller; -import ip.labwork.WebConfiguration; +import ip.labwork.configuration.WebConfiguration; import ip.labwork.shop.service.ComponentService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/ip/labwork/shop/controller/ComponentMvcController.java b/src/main/java/ip/labwork/shop/controller/ComponentMvcController.java index 9c99bc2..9ddacd0 100644 --- a/src/main/java/ip/labwork/shop/controller/ComponentMvcController.java +++ b/src/main/java/ip/labwork/shop/controller/ComponentMvcController.java @@ -1,7 +1,9 @@ package ip.labwork.shop.controller; import ip.labwork.shop.service.ComponentService; +import ip.labwork.user.model.UserRole; import jakarta.validation.Valid; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -16,11 +18,13 @@ public class ComponentMvcController { this.componentService = componentService; } @GetMapping + @Secured({UserRole.AsString.ADMIN}) public String getComponents(Model model) { model.addAttribute("components", componentService.findAllComponent()); return "component"; } @GetMapping(value = {"/edit", "/edit/{id}"}) + @Secured({UserRole.AsString.ADMIN}) public String editComponent(@PathVariable(required = false) Long id, Model model) { if (id == null || id <= 0) { diff --git a/src/main/java/ip/labwork/shop/controller/OrderController.java b/src/main/java/ip/labwork/shop/controller/OrderController.java index 36e83b8..378cf7c 100644 --- a/src/main/java/ip/labwork/shop/controller/OrderController.java +++ b/src/main/java/ip/labwork/shop/controller/OrderController.java @@ -1,6 +1,6 @@ package ip.labwork.shop.controller; -import ip.labwork.WebConfiguration; +import ip.labwork.configuration.WebConfiguration; import ip.labwork.shop.service.OrderService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/ip/labwork/shop/controller/OrderDTO.java b/src/main/java/ip/labwork/shop/controller/OrderDTO.java index dccbedd..417f729 100644 --- a/src/main/java/ip/labwork/shop/controller/OrderDTO.java +++ b/src/main/java/ip/labwork/shop/controller/OrderDTO.java @@ -14,6 +14,7 @@ public class OrderDTO { private Date date = new Date(); @NotBlank(message = "Price can't be null or empty") private int price; + private long user_id; private OrderStatus status = OrderStatus.Неизвестен; private List productDTOList; public OrderDTO(Order order) { @@ -25,6 +26,7 @@ public class OrderDTO { .map(y -> new ProductDTO(y.getProduct(), y.getCount())) .toList(); this.status = Objects.equals(order.getStatus().toString(), "") ? OrderStatus.Неизвестен : order.getStatus(); + this.user_id = order.getUser_id() == null ? -1 : order.getUser_id(); } public OrderDTO() { @@ -69,4 +71,12 @@ public class OrderDTO { public void setProductDTOList(List productDTOList) { this.productDTOList = productDTOList; } + + public long getUser_id() { + return user_id; + } + + public void setUser_id(long user_id) { + this.user_id = user_id; + } } \ No newline at end of file diff --git a/src/main/java/ip/labwork/shop/controller/OrderMvcController.java b/src/main/java/ip/labwork/shop/controller/OrderMvcController.java index c71601a..de654bc 100644 --- a/src/main/java/ip/labwork/shop/controller/OrderMvcController.java +++ b/src/main/java/ip/labwork/shop/controller/OrderMvcController.java @@ -3,6 +3,7 @@ package ip.labwork.shop.controller; import ip.labwork.shop.model.OrderStatus; import ip.labwork.shop.service.OrderService; import ip.labwork.shop.service.ProductService; +import ip.labwork.user.service.UserService; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -11,6 +12,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import java.security.Principal; import java.util.ArrayList; import java.util.List; @@ -19,9 +21,11 @@ import java.util.List; public class OrderMvcController { private final OrderService orderService; private final ProductService productService; - public OrderMvcController(OrderService orderService, ProductService productService) { + private final UserService userService; + public OrderMvcController(OrderService orderService, ProductService productService, UserService userService) { this.orderService = orderService; this.productService = productService; + this.userService = userService; } @GetMapping public String getOrders(HttpServletRequest request, @@ -45,7 +49,7 @@ public class OrderMvcController { } @PostMapping public String createOrder(HttpServletRequest request, - HttpServletResponse response) { + HttpServletResponse response, Principal principal) { Cookie[] cookies = request.getCookies(); OrderDTO orderDTO = new OrderDTO(); List productDTOS = new ArrayList<>(); @@ -60,13 +64,14 @@ public class OrderMvcController { orderDTO.setPrice(totalPrice); orderDTO.setProductDTOList(productDTOS); orderDTO.setStatus(OrderStatus.Готов); + orderDTO.setUser_id(userService.findByLogin(principal.getName()).getId()); orderService.create(orderDTO); response.addCookie(new Cookie("delete","")); return "redirect:/order"; } @GetMapping(value = {"/all"}) - public String getOrders(Model model) { - model.addAttribute("orders", orderService.findAllOrder()); + public String getOrders(Model model, Principal principal) { + model.addAttribute("orders", orderService.findFiltredOrder(userService.findByLogin(principal.getName()).getId())); return "orders"; } diff --git a/src/main/java/ip/labwork/shop/controller/ProductController.java b/src/main/java/ip/labwork/shop/controller/ProductController.java index 46e88c7..04d043c 100644 --- a/src/main/java/ip/labwork/shop/controller/ProductController.java +++ b/src/main/java/ip/labwork/shop/controller/ProductController.java @@ -1,6 +1,6 @@ package ip.labwork.shop.controller; -import ip.labwork.WebConfiguration; +import ip.labwork.configuration.WebConfiguration; import ip.labwork.shop.service.ProductService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/ip/labwork/shop/controller/ProductMvcController.java b/src/main/java/ip/labwork/shop/controller/ProductMvcController.java index 4f14c4f..c4758cb 100644 --- a/src/main/java/ip/labwork/shop/controller/ProductMvcController.java +++ b/src/main/java/ip/labwork/shop/controller/ProductMvcController.java @@ -2,7 +2,9 @@ package ip.labwork.shop.controller; import ip.labwork.shop.service.ComponentService; import ip.labwork.shop.service.ProductService; +import ip.labwork.user.model.UserRole; import jakarta.validation.Valid; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -27,6 +29,7 @@ public class ProductMvcController { return "product"; } @GetMapping(value = {"/edit", "/edit/{id}"}) + @Secured({UserRole.AsString.ADMIN}) public String editProduct(@PathVariable(required = false) Long id, Model model) { if (id == null || id <= 0) { diff --git a/src/main/java/ip/labwork/shop/model/Order.java b/src/main/java/ip/labwork/shop/model/Order.java index 9b0e535..d7f6fb8 100644 --- a/src/main/java/ip/labwork/shop/model/Order.java +++ b/src/main/java/ip/labwork/shop/model/Order.java @@ -20,6 +20,7 @@ public class Order { @NotNull(message = "Price can't be null or empty") @Column(name = "price") private Integer price; + private Long user_id; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private List products; private OrderStatus status; @@ -27,10 +28,11 @@ public class Order { } - public Order(Date date, Integer price, OrderStatus status) { + public Order(Date date, Integer price, OrderStatus status, Long user_id) { this.date = date; this.price = price; this.status = status; + this.user_id = user_id; } public Long getId() { @@ -84,11 +86,19 @@ public class Order { this.status = status; } + public Long getUser_id() { + return user_id; + } + + public void setUser_id(Long user_id) { + this.user_id = user_id; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Order order)) return false; - return Objects.equals(getId(), order.getId()) && Objects.equals(getDate(), order.getDate()) && Objects.equals(getPrice(), order.getPrice()); + return Objects.equals(getId(), order.getId()) && Objects.equals(getDate(), order.getDate()) && Objects.equals(getPrice(), order.getPrice()) && Objects.equals(getUser_id(), order.getUser_id()) && Objects.equals(getProducts(), order.getProducts()) && getStatus() == order.getStatus(); } @Override diff --git a/src/main/java/ip/labwork/shop/repository/OrderRepository.java b/src/main/java/ip/labwork/shop/repository/OrderRepository.java index e2444fc..1198351 100644 --- a/src/main/java/ip/labwork/shop/repository/OrderRepository.java +++ b/src/main/java/ip/labwork/shop/repository/OrderRepository.java @@ -11,4 +11,7 @@ import java.util.List; public interface OrderRepository extends JpaRepository { @Query("Select os from OrderProducts os where os.order.id = :orderId") List getOrderProduct(@Param("orderId") Long orderId); + + @Query("Select o from Order o where o.user_id = :userId") + List getOrdersByUser_id(@Param("userId") Long userId); } \ No newline at end of file diff --git a/src/main/java/ip/labwork/shop/service/OrderService.java b/src/main/java/ip/labwork/shop/service/OrderService.java index aede5b7..e01ce20 100644 --- a/src/main/java/ip/labwork/shop/service/OrderService.java +++ b/src/main/java/ip/labwork/shop/service/OrderService.java @@ -28,7 +28,7 @@ public class OrderService { for(int i = 0; i < orderDTO.getProductDTOList().size(); i++){ price += orderDTO.getProductDTOList().get(i).getPrice() * orderDTO.getProductDTOList().get(i).getCount(); } - final Order order = new Order(new Date(), price, orderDTO.getStatus()); + final Order order = new Order(new Date(), price, orderDTO.getStatus(), orderDTO.getUser_id()); validatorUtil.validate(order); orderRepository.save(order); for (int i = 0; i < orderDTO.getProductDTOList().size(); i++) { @@ -47,6 +47,11 @@ public class OrderService { public List findAllOrder() { return orderRepository.findAll().stream().map(x -> new OrderDTO(x)).toList(); } + + @Transactional(readOnly = true) + public List findFiltredOrder(long userid) { + return orderRepository.getOrdersByUser_id(userid).stream().map(x -> new OrderDTO(x)).toList(); + } @Transactional public OrderDTO update(Long id, OrderDTO orderDTO) { final Order currentOrder = findOrder(id); diff --git a/src/main/java/ip/labwork/test/controller/TestController.java b/src/main/java/ip/labwork/test/controller/TestController.java index b3f4a5d..09676da 100644 --- a/src/main/java/ip/labwork/test/controller/TestController.java +++ b/src/main/java/ip/labwork/test/controller/TestController.java @@ -1,6 +1,6 @@ package ip.labwork.test.controller; -import ip.labwork.WebConfiguration; +import ip.labwork.configuration.WebConfiguration; import ip.labwork.test.model.TestDto; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/main/java/ip/labwork/user/controller/UserMvcController.java b/src/main/java/ip/labwork/user/controller/UserMvcController.java new file mode 100644 index 0000000..5cda810 --- /dev/null +++ b/src/main/java/ip/labwork/user/controller/UserMvcController.java @@ -0,0 +1,42 @@ +package ip.labwork.user.controller; + +import ip.labwork.user.model.UserDto; +import ip.labwork.user.model.UserRole; +import ip.labwork.user.service.UserService; +import org.springframework.data.domain.Page; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; +import java.util.stream.IntStream; + +@Controller +@RequestMapping("/users") +public class UserMvcController { + private final UserService userService; + + public UserMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + @Secured({UserRole.AsString.ADMIN}) + public String getUsers(@RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "5") int size, + Model model) { + final Page users = userService.findAllPages(page, size) + .map(UserDto::new); + model.addAttribute("users", users); + final int totalPages = users.getTotalPages(); + final List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .toList(); + model.addAttribute("pages", pageNumbers); + model.addAttribute("totalPages", totalPages); + return "users"; + } +} diff --git a/src/main/java/ip/labwork/user/controller/UserSignupMvcController.java b/src/main/java/ip/labwork/user/controller/UserSignupMvcController.java new file mode 100644 index 0000000..b581817 --- /dev/null +++ b/src/main/java/ip/labwork/user/controller/UserSignupMvcController.java @@ -0,0 +1,50 @@ +package ip.labwork.user.controller; + +import ip.labwork.user.model.User; +import ip.labwork.user.model.UserSignupDto; +import ip.labwork.user.service.UserService; +import ip.labwork.util.validation.ValidationException; +import jakarta.validation.Valid; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping(UserSignupMvcController.SIGNUP_URL) +public class UserSignupMvcController { + public static final String SIGNUP_URL = "/signup"; + + private final UserService userService; + + public UserSignupMvcController(UserService userService) { + this.userService = userService; + } + + @GetMapping + public String showSignupForm(Model model) { + model.addAttribute("userDto", new UserSignupDto()); + return "signup"; + } + + @PostMapping + public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto, + BindingResult bindingResult, + Model model) { + if (bindingResult.hasErrors()) { + model.addAttribute("errors", bindingResult.getAllErrors()); + return "signup"; + } + try { + final User user = userService.createUser( + userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm()); + return "redirect:/login?created=" + user.getLogin(); + } catch (ValidationException e) { + model.addAttribute("errors", e.getMessage()); + return "signup"; + } + } +} diff --git a/src/main/java/ip/labwork/user/model/User.java b/src/main/java/ip/labwork/user/model/User.java new file mode 100644 index 0000000..f977460 --- /dev/null +++ b/src/main/java/ip/labwork/user/model/User.java @@ -0,0 +1,74 @@ +package ip.labwork.user.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import java.util.Objects; + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + @Column(nullable = false, unique = true, length = 64) + @NotBlank + @Size(min = 3, max = 64) + private String login; + @Column(nullable = false, length = 64) + @NotBlank + @Size(min = 6, max = 64) + private String password; + private UserRole role; + + public User() { + } + + public User(String login, String password) { + this(login, password, UserRole.USER); + } + + public User(String login, String password, UserRole role) { + this.login = login; + this.password = password; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public UserRole getRole() { + return role; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(login, user.login); + } + + @Override + public int hashCode() { + return Objects.hash(id, login); + } +} \ No newline at end of file diff --git a/src/main/java/ip/labwork/user/model/UserDto.java b/src/main/java/ip/labwork/user/model/UserDto.java new file mode 100644 index 0000000..4ff02a6 --- /dev/null +++ b/src/main/java/ip/labwork/user/model/UserDto.java @@ -0,0 +1,25 @@ +package ip.labwork.user.model; + +public class UserDto { + private final long id; + private final String login; + private final UserRole role; + + public UserDto(User user) { + this.id = user.getId(); + this.login = user.getLogin(); + this.role = user.getRole(); + } + + public long getId() { + return id; + } + + public String getLogin() { + return login; + } + + public UserRole getRole() { + return role; + } +} diff --git a/src/main/java/ip/labwork/user/model/UserRole.java b/src/main/java/ip/labwork/user/model/UserRole.java new file mode 100644 index 0000000..38d32e6 --- /dev/null +++ b/src/main/java/ip/labwork/user/model/UserRole.java @@ -0,0 +1,20 @@ +package ip.labwork.user.model; + +import org.springframework.security.core.GrantedAuthority; + +public enum UserRole implements GrantedAuthority { + ADMIN, + USER; + + private static final String PREFIX = "ROLE_"; + + @Override + public String getAuthority() { + return PREFIX + this.name(); + } + + public static final class AsString { + public static final String ADMIN = PREFIX + "ADMIN"; + public static final String USER = PREFIX + "USER"; + } +} diff --git a/src/main/java/ip/labwork/user/model/UserSignupDto.java b/src/main/java/ip/labwork/user/model/UserSignupDto.java new file mode 100644 index 0000000..0d50351 --- /dev/null +++ b/src/main/java/ip/labwork/user/model/UserSignupDto.java @@ -0,0 +1,40 @@ +package ip.labwork.user.model; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class UserSignupDto { + @NotBlank + @Size(min = 3, max = 64) + private String login; + @NotBlank + @Size(min = 6, max = 64) + private String password; + @NotBlank + @Size(min = 6, max = 64) + private String passwordConfirm; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPasswordConfirm() { + return passwordConfirm; + } + + public void setPasswordConfirm(String passwordConfirm) { + this.passwordConfirm = passwordConfirm; + } +} diff --git a/src/main/java/ip/labwork/user/repository/UserRepository.java b/src/main/java/ip/labwork/user/repository/UserRepository.java new file mode 100644 index 0000000..8899a68 --- /dev/null +++ b/src/main/java/ip/labwork/user/repository/UserRepository.java @@ -0,0 +1,8 @@ +package ip.labwork.user.repository; + +import ip.labwork.user.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + User findOneByLoginIgnoreCase(String login); +} diff --git a/src/main/java/ip/labwork/user/service/UserService.java b/src/main/java/ip/labwork/user/service/UserService.java new file mode 100644 index 0000000..b74dd28 --- /dev/null +++ b/src/main/java/ip/labwork/user/service/UserService.java @@ -0,0 +1,67 @@ +package ip.labwork.user.service; + +import ip.labwork.user.model.User; +import ip.labwork.user.model.UserRole; +import ip.labwork.user.repository.UserRepository; +import ip.labwork.util.validation.ValidationException; +import ip.labwork.util.validation.ValidatorUtil; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Objects; + +@Service +public class UserService implements UserDetailsService { + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + private final ValidatorUtil validatorUtil; + + public UserService(UserRepository userRepository, + PasswordEncoder passwordEncoder, + ValidatorUtil validatorUtil) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.validatorUtil = validatorUtil; + } + + public Page findAllPages(int page, int size) { + return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); + } + + public User findByLogin(String login) { + return userRepository.findOneByLoginIgnoreCase(login); + } + + public User createUser(String login, String password, String passwordConfirm) { + return createUser(login, password, passwordConfirm, UserRole.USER); + } + + public User createUser(String login, String password, String passwordConfirm, UserRole role) { + if (findByLogin(login) != null) { + throw new ValidationException(String.format("User '%s' already exists", login)); + } + final User user = new User(login, passwordEncoder.encode(password), role); + validatorUtil.validate(user); + if (!Objects.equals(password, passwordConfirm)) { + throw new ValidationException("Passwords not equals"); + } + return userRepository.save(user); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final User userEntity = findByLogin(username); + if (userEntity == null) { + throw new UsernameNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User( + userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole())); + } +} diff --git a/src/main/java/ip/labwork/util/validation/ValidationException.java b/src/main/java/ip/labwork/util/validation/ValidationException.java index a1e6e3d..72ac011 100644 --- a/src/main/java/ip/labwork/util/validation/ValidationException.java +++ b/src/main/java/ip/labwork/util/validation/ValidationException.java @@ -3,7 +3,11 @@ package ip.labwork.util.validation; import java.util.Set; public class ValidationException extends RuntimeException { - public ValidationException(Set errors) { + public ValidationException(Set errors) { super(String.join("\n", errors)); } + + public ValidationException(String error) { + super(error); + } } diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 80229db..0b285c9 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -3,6 +3,7 @@ lang="ru" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6" > @@ -38,14 +39,18 @@ diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..890f629 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,35 @@ + + + +
+
+
+ Пользователь не найден или пароль указан не верно +
+
+ Выход успешно произведен +
+
+ Пользователь '' успешно создан +
+
+
+ +
+
+ +
+ + Регистрация +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/product.html b/src/main/resources/templates/product.html index 77a3663..e620022 100644 --- a/src/main/resources/templates/product.html +++ b/src/main/resources/templates/product.html @@ -3,6 +3,7 @@ lang="en" layout:decorate="~{default}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6" > @@ -12,6 +13,7 @@ type="button" class="btn btn-outline-dark text-center d-flex justify-content-md-center mx-2 mb-3" th:href="@{/product/edit}" + sec:authorize="hasRole('ROLE_ADMIN')" > Добавить @@ -39,13 +41,14 @@ Удалить Изменить Удалить diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..d774810 --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,33 @@ + + + +
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/users.html b/src/main/resources/templates/users.html new file mode 100644 index 0000000..816a667 --- /dev/null +++ b/src/main/resources/templates/users.html @@ -0,0 +1,37 @@ + + + +
+
+ + + + + + + + + + + + + + + + + +
#IDЛогинРоль
+
+ +
+ + \ No newline at end of file