From ce15f93adb096476d70df328db9a76a7e0b80851 Mon Sep 17 00:00:00 2001 From: dasha Date: Wed, 10 May 2023 17:05:37 +0400 Subject: [PATCH] mvc auth --- build.gradle | 3 + .../PasswordEncoderConfiguration.java | 14 + .../configuration/SecurityConfiguration.java | 75 +++++ .../{ => configuration}/WebConfiguration.java | 10 +- .../student/controller/CinemaController.java | 2 +- .../controller/CinemaMvcController.java | 5 + .../controller/CustomerController.java | 9 +- .../app/student/controller/CustomerDto.java | 7 + .../controller/CustomerMvcController.java | 43 ++- .../student/controller/OrderController.java | 2 +- .../controller/OrderMvcController.java | 58 +++- .../student/controller/SessionController.java | 4 +- .../controller/SessionMvcController.java | 6 + .../app/student/controller/UserSignupDto.java | 41 +++ .../controller/UserSignupMvcController.java | 50 ++++ .../labwork1/app/student/model/Customer.java | 25 +- .../labwork1/app/student/model/UserRole.java | 20 ++ .../app/student/model/UserSignupDto.java | 40 +++ .../repository/CustomerRepository.java | 1 + .../app/student/service/CustomerService.java | 67 ++++- src/main/resources/public/css/style.css | 13 + src/main/resources/templates/cinema.html | 12 +- src/main/resources/templates/customer.html | 15 +- src/main/resources/templates/default.html | 18 +- src/main/resources/templates/login.html | 33 +++ src/main/resources/templates/order-edit.html | 7 - src/main/resources/templates/session.html | 11 +- src/main/resources/templates/signup.html | 29 ++ .../com/labwork1/app/JpaCustomerTests.java | 258 +++++++++--------- 29 files changed, 665 insertions(+), 213 deletions(-) create mode 100644 src/main/java/com/labwork1/app/configuration/PasswordEncoderConfiguration.java create mode 100644 src/main/java/com/labwork1/app/configuration/SecurityConfiguration.java rename src/main/java/com/labwork1/app/{ => configuration}/WebConfiguration.java (60%) create mode 100644 src/main/java/com/labwork1/app/student/controller/UserSignupDto.java create mode 100644 src/main/java/com/labwork1/app/student/controller/UserSignupMvcController.java create mode 100644 src/main/java/com/labwork1/app/student/model/UserRole.java create mode 100644 src/main/java/com/labwork1/app/student/model/UserSignupDto.java create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/signup.html diff --git a/build.gradle b/build.gradle index 58f9b3c..764a74e 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'com.h2database:h2:2.1.210' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' + implementation 'org.hibernate.validator:hibernate-validator' implementation 'org.springdoc:springdoc-openapi-ui:1.6.5' diff --git a/src/main/java/com/labwork1/app/configuration/PasswordEncoderConfiguration.java b/src/main/java/com/labwork1/app/configuration/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..fc5c50f --- /dev/null +++ b/src/main/java/com/labwork1/app/configuration/PasswordEncoderConfiguration.java @@ -0,0 +1,14 @@ +package com.labwork1.app.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(); + } +} diff --git a/src/main/java/com/labwork1/app/configuration/SecurityConfiguration.java b/src/main/java/com/labwork1/app/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..5e1d3d3 --- /dev/null +++ b/src/main/java/com/labwork1/app/configuration/SecurityConfiguration.java @@ -0,0 +1,75 @@ +package com.labwork1.app.configuration; + +import com.labwork1.app.student.controller.UserSignupMvcController; +import com.labwork1.app.student.model.UserRole; +import com.labwork1.app.student.service.CustomerService; +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.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 CustomerService userService; + + public SecurityConfiguration(CustomerService 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() + .authorizeHttpRequests() + .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 authenticationManagerBean(HttpSecurity http) throws Exception { + AuthenticationManagerBuilder authenticationManagerBuilder = http + .getSharedObject(AuthenticationManagerBuilder.class); + authenticationManagerBuilder.userDetailsService(userService); + return authenticationManagerBuilder.build(); + } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return web -> web.ignoring() + .requestMatchers("/css/**") + .requestMatchers("/js/**") + .requestMatchers("/templates/**") + .requestMatchers("/webjars/**") + .requestMatchers("/vk.jpg"); + } +} \ No newline at end of file diff --git a/src/main/java/com/labwork1/app/WebConfiguration.java b/src/main/java/com/labwork1/app/configuration/WebConfiguration.java similarity index 60% rename from src/main/java/com/labwork1/app/WebConfiguration.java rename to src/main/java/com/labwork1/app/configuration/WebConfiguration.java index 2b87533..f16afc0 100644 --- a/src/main/java/com/labwork1/app/WebConfiguration.java +++ b/src/main/java/com/labwork1/app/configuration/WebConfiguration.java @@ -1,7 +1,9 @@ -package com.labwork1.app; +package com.labwork1.app.configuration; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfiguration implements WebMvcConfigurer { @@ -10,10 +12,12 @@ public class WebConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { WebMvcConfigurer.super.addViewControllers(registry); + registry.addViewController("login"); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("*"); } -} \ No newline at end of file +} + diff --git a/src/main/java/com/labwork1/app/student/controller/CinemaController.java b/src/main/java/com/labwork1/app/student/controller/CinemaController.java index 9b3720e..1e669c5 100644 --- a/src/main/java/com/labwork1/app/student/controller/CinemaController.java +++ b/src/main/java/com/labwork1/app/student/controller/CinemaController.java @@ -1,6 +1,6 @@ package com.labwork1.app.student.controller; -import com.labwork1.app.WebConfiguration; +import com.labwork1.app.configuration.WebConfiguration; import com.labwork1.app.student.service.CinemaService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/labwork1/app/student/controller/CinemaMvcController.java b/src/main/java/com/labwork1/app/student/controller/CinemaMvcController.java index 462c9f8..3438294 100644 --- a/src/main/java/com/labwork1/app/student/controller/CinemaMvcController.java +++ b/src/main/java/com/labwork1/app/student/controller/CinemaMvcController.java @@ -1,7 +1,9 @@ package com.labwork1.app.student.controller; +import com.labwork1.app.student.model.UserRole; import com.labwork1.app.student.service.CinemaService; 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; @@ -31,6 +33,7 @@ public class CinemaMvcController { } @GetMapping(value = {"/edit", "/edit/{id}"}) + @Secured({UserRole.AsString.ADMIN}) public String editCinema(@PathVariable(required = false) Long id, Model model) { if (id == null || id <= 0) { @@ -52,6 +55,7 @@ public class CinemaMvcController { } @PostMapping(value = {"/", "/{id}"}) + @Secured({UserRole.AsString.ADMIN}) public String saveCinema(@PathVariable(required = false) Long id, @RequestParam("multipartFile") MultipartFile multipartFile, @ModelAttribute @Valid CinemaDto cinemaDto, @@ -73,6 +77,7 @@ public class CinemaMvcController { } @PostMapping("/delete/{id}") + @Secured({UserRole.AsString.ADMIN}) public String deleteCinema(@PathVariable Long id) { cinemaService.deleteCinema(id); return "redirect:/cinema"; diff --git a/src/main/java/com/labwork1/app/student/controller/CustomerController.java b/src/main/java/com/labwork1/app/student/controller/CustomerController.java index c2760a6..6409a72 100644 --- a/src/main/java/com/labwork1/app/student/controller/CustomerController.java +++ b/src/main/java/com/labwork1/app/student/controller/CustomerController.java @@ -1,7 +1,9 @@ package com.labwork1.app.student.controller; -import com.labwork1.app.WebConfiguration; +import com.labwork1.app.configuration.WebConfiguration; +import com.labwork1.app.student.model.UserSignupDto; import com.labwork1.app.student.service.CustomerService; +import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -28,9 +30,8 @@ public class CustomerController { } @PostMapping - public CustomerDto createCustomer(@RequestParam("login") String login, - @RequestParam("password") String password) { - return new CustomerDto(customerService.addCustomer(login, password)); + public CustomerDto createCustomer(@RequestBody @Valid UserSignupDto userSignupDto) { + return new CustomerDto(customerService.addCustomer(userSignupDto)); } @PutMapping("/{id}") diff --git a/src/main/java/com/labwork1/app/student/controller/CustomerDto.java b/src/main/java/com/labwork1/app/student/controller/CustomerDto.java index 812c3ce..5af72c2 100644 --- a/src/main/java/com/labwork1/app/student/controller/CustomerDto.java +++ b/src/main/java/com/labwork1/app/student/controller/CustomerDto.java @@ -3,6 +3,7 @@ package com.labwork1.app.student.controller; import com.labwork1.app.student.model.Customer; import com.labwork1.app.student.model.Order; import com.labwork1.app.student.model.OrderSession; +import com.labwork1.app.student.model.UserRole; import java.util.ArrayList; import java.util.List; @@ -12,6 +13,7 @@ public class CustomerDto { private String login; private String password; private List orders; + private UserRole role; public CustomerDto() { } @@ -21,6 +23,7 @@ public class CustomerDto { this.login = customer.getLogin(); this.password = customer.getPassword(); this.orders = new ArrayList<>(); + this.role = customer.getRole(); if (customer.getOrders() != null) { orders = customer.getOrders().stream() .map(OrderDto::new).toList(); @@ -58,4 +61,8 @@ public class CustomerDto { public List getOrders() { return orders; } + + public UserRole getRole() { + return role; + } } diff --git a/src/main/java/com/labwork1/app/student/controller/CustomerMvcController.java b/src/main/java/com/labwork1/app/student/controller/CustomerMvcController.java index 0a48bd2..9b4fa5e 100644 --- a/src/main/java/com/labwork1/app/student/controller/CustomerMvcController.java +++ b/src/main/java/com/labwork1/app/student/controller/CustomerMvcController.java @@ -1,14 +1,21 @@ package com.labwork1.app.student.controller; +import com.labwork1.app.student.model.UserRole; import com.labwork1.app.student.service.CustomerService; import jakarta.validation.Valid; +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.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.stream.IntStream; + @Controller @RequestMapping("/customer") +@Secured({UserRole.AsString.ADMIN}) public class CustomerMvcController { private final CustomerService customerService; @@ -17,11 +24,18 @@ public class CustomerMvcController { } @GetMapping - public String getCustomers(Model model) { - model.addAttribute("customers", - customerService.findAllCustomers().stream() - .map(CustomerDto::new) - .toList()); + public String getCustomers(@RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "5") int size, + Model model) { + final Page users = customerService.findAllPages(page, size) + .map(CustomerDto::new); + model.addAttribute("customers", users); + final int totalPages = users.getTotalPages(); + final List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .toList(); + model.addAttribute("pages", pageNumbers); + model.addAttribute("totalPages", totalPages); return "customer"; } @@ -37,28 +51,9 @@ public class CustomerMvcController { return "customer-edit"; } - @PostMapping(value = {"/", "/{id}"}) - public String saveCustomer(@PathVariable(required = false) Long id, - @ModelAttribute @Valid CustomerDto customerDto, - BindingResult bindingResult, - Model model) { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", bindingResult.getAllErrors()); - return "customer-edit"; - } - if (id == null || id <= 0) { - customerService.addCustomer(customerDto.getLogin(), customerDto.getPassword()); - } else { - customerDto.setId(id); - customerService.updateCustomer(id, customerDto.getLogin(), customerDto.getPassword()); - } - return "redirect:/customer"; - } - @PostMapping("/delete/{id}") public String deleteCustomer(@PathVariable Long id) { customerService.deleteCustomer(id); return "redirect:/customer"; } - } diff --git a/src/main/java/com/labwork1/app/student/controller/OrderController.java b/src/main/java/com/labwork1/app/student/controller/OrderController.java index f3ab633..808ed8b 100644 --- a/src/main/java/com/labwork1/app/student/controller/OrderController.java +++ b/src/main/java/com/labwork1/app/student/controller/OrderController.java @@ -1,6 +1,6 @@ package com.labwork1.app.student.controller; -import com.labwork1.app.WebConfiguration; +import com.labwork1.app.configuration.WebConfiguration; import com.labwork1.app.student.service.OrderService; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/labwork1/app/student/controller/OrderMvcController.java b/src/main/java/com/labwork1/app/student/controller/OrderMvcController.java index c003182..ca34d1a 100644 --- a/src/main/java/com/labwork1/app/student/controller/OrderMvcController.java +++ b/src/main/java/com/labwork1/app/student/controller/OrderMvcController.java @@ -1,6 +1,9 @@ package com.labwork1.app.student.controller; +import com.labwork1.app.student.model.Customer; +import com.labwork1.app.student.model.Order; import com.labwork1.app.student.model.SessionExtension; +import com.labwork1.app.student.model.UserRole; import com.labwork1.app.student.service.CustomerService; import com.labwork1.app.student.service.OrderService; import com.labwork1.app.student.service.SessionService; @@ -11,9 +14,11 @@ import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; +import java.security.Principal; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; @Controller @RequestMapping("/order") @@ -29,27 +34,38 @@ public class OrderMvcController { } @GetMapping - public String getOrders(Model model) { - model.addAttribute("orders", - orderService.findAllOrders().stream() - .map(OrderDto::new) - .toList()); + public String getOrders(Model model, + Principal principal) { + Customer customer = customerService.findByLogin(principal.getName()); + if (customer.getRole() == UserRole.USER) + model.addAttribute("orders", + orderService.findAllOrders().stream() + .map(OrderDto::new) + .filter(orderDto -> Objects.equals(orderDto.getCustomerId(), + customer.getId())) + .toList()); + else + model.addAttribute("orders", + orderService.findAllOrders().stream() + .map(OrderDto::new) + .toList()); return "order"; } @GetMapping(value = {"/edit", "/edit/{id}"}) public String editOrder(@PathVariable(required = false) Long id, - Model model) { + Model model, + Principal principal) { if (id == null || id <= 0) { - List customers = customerService.findAllCustomers().stream() - .map(CustomerDto::new) - .toList(); model.addAttribute("orderDto", new OrderDto()); - model.addAttribute("customers", customers); return "order-edit"; } else { - List temp = orderService.findOrder(id).getSessions() + Order order = orderService.findOrder(id); + Customer customer = customerService.findByLogin(principal.getName()); + if (!Objects.equals(order.getCustomer().getId(), customer.getId()) && customer.getRole() == UserRole.USER) + return "redirect:/order"; + List temp = order.getSessions() .stream().map(x -> new OrderSessionDto(new SessionDto(x.getSession()), x.getOrder().getId(), x.getCount())).toList(); List sessions = sessionService.findAllSessions().stream() @@ -70,12 +86,14 @@ public class OrderMvcController { @PostMapping(value = "/") public String saveOrder(@ModelAttribute @Valid OrderDto orderDto, BindingResult bindingResult, - Model model) { + Model model, + Principal principal) { + Long customerId = customerService.findByLogin(principal.getName()).getId(); if (bindingResult.hasErrors()) { model.addAttribute("errors", bindingResult.getAllErrors()); return "order-edit"; } - orderService.addOrder(orderDto.getCustomerId()); + orderService.addOrder(customerId); return "redirect:/order"; } @@ -83,7 +101,12 @@ public class OrderMvcController { public String editOrder(@PathVariable Long id, @RequestParam("session") Long session, @RequestParam(value = "count", required = false) Integer count, - Model model) { + Model model, + Principal principal) { + Order order = orderService.findOrder(id); + Customer customer = customerService.findByLogin(principal.getName()); + if (!Objects.equals(order.getCustomer().getId(), customer.getId()) && customer.getRole() == UserRole.USER) + return "/order"; if (count == null) orderService.deleteSessionInOrder(id, session, Integer.MAX_VALUE); else if (count > 0) @@ -92,7 +115,12 @@ public class OrderMvcController { } @PostMapping("/delete/{id}") - public String deleteOrder(@PathVariable Long id) { + public String deleteOrder(@PathVariable Long id, + Principal principal) { + Order order = orderService.findOrder(id); + Customer customer = customerService.findByLogin(principal.getName()); + if (!Objects.equals(order.getCustomer().getId(), customer.getId()) && customer.getRole() == UserRole.USER) + return "redirect:/order"; orderService.deleteOrder(id); return "redirect:/order"; } diff --git a/src/main/java/com/labwork1/app/student/controller/SessionController.java b/src/main/java/com/labwork1/app/student/controller/SessionController.java index e06dac0..1f691f9 100644 --- a/src/main/java/com/labwork1/app/student/controller/SessionController.java +++ b/src/main/java/com/labwork1/app/student/controller/SessionController.java @@ -1,13 +1,11 @@ package com.labwork1.app.student.controller; -import com.labwork1.app.WebConfiguration; +import com.labwork1.app.configuration.WebConfiguration; import com.labwork1.app.student.service.SessionService; import org.springframework.web.bind.annotation.*; -import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.List; diff --git a/src/main/java/com/labwork1/app/student/controller/SessionMvcController.java b/src/main/java/com/labwork1/app/student/controller/SessionMvcController.java index 2a52021..8fae836 100644 --- a/src/main/java/com/labwork1/app/student/controller/SessionMvcController.java +++ b/src/main/java/com/labwork1/app/student/controller/SessionMvcController.java @@ -1,8 +1,10 @@ package com.labwork1.app.student.controller; +import com.labwork1.app.student.model.UserRole; import com.labwork1.app.student.service.CinemaService; import com.labwork1.app.student.service.SessionService; 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; @@ -31,6 +33,7 @@ public class SessionMvcController { } @GetMapping(value = {"/edit", "/edit/{id}"}) + @Secured({UserRole.AsString.ADMIN}) public String editSession(@PathVariable(required = false) Long id, Model model) { if (id == null || id <= 0) { @@ -47,6 +50,7 @@ public class SessionMvcController { } @PostMapping(value = "/{id}") + @Secured({UserRole.AsString.ADMIN}) public String editSession(@PathVariable Long id, @ModelAttribute @Valid SessionDto sessionDto, BindingResult bindingResult, @@ -61,6 +65,7 @@ public class SessionMvcController { } @PostMapping(value = "/") + @Secured({UserRole.AsString.ADMIN}) public String saveSession(@RequestParam("price") String price, @RequestParam("timestamp") LocalDateTime timestamp, @RequestParam("cinemaid") Long cinemaId, @@ -72,6 +77,7 @@ public class SessionMvcController { } @PostMapping("/delete/{id}") + @Secured({UserRole.AsString.ADMIN}) public String deleteSession(@PathVariable Long id) { sessionService.deleteSession(id); return "redirect:/session"; diff --git a/src/main/java/com/labwork1/app/student/controller/UserSignupDto.java b/src/main/java/com/labwork1/app/student/controller/UserSignupDto.java new file mode 100644 index 0000000..b6a95a2 --- /dev/null +++ b/src/main/java/com/labwork1/app/student/controller/UserSignupDto.java @@ -0,0 +1,41 @@ +package com.labwork1.app.student.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +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/com/labwork1/app/student/controller/UserSignupMvcController.java b/src/main/java/com/labwork1/app/student/controller/UserSignupMvcController.java new file mode 100644 index 0000000..1101034 --- /dev/null +++ b/src/main/java/com/labwork1/app/student/controller/UserSignupMvcController.java @@ -0,0 +1,50 @@ +package com.labwork1.app.student.controller; + +import com.labwork1.app.student.model.Customer; +import com.labwork1.app.student.model.UserSignupDto; +import com.labwork1.app.student.service.CustomerService; +import jakarta.validation.Valid; +import jakarta.validation.ValidationException; +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 CustomerService userService; + + public UserSignupMvcController(CustomerService 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 Customer user = userService.addCustomer( + userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm()); + return "redirect:/login?created=" + user.getLogin(); + } catch (ValidationException e) { + model.addAttribute("errors", e.getMessage()); + return "signup"; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/labwork1/app/student/model/Customer.java b/src/main/java/com/labwork1/app/student/model/Customer.java index 3aa56d9..9e637ca 100644 --- a/src/main/java/com/labwork1/app/student/model/Customer.java +++ b/src/main/java/com/labwork1/app/student/model/Customer.java @@ -2,6 +2,7 @@ package com.labwork1.app.student.model; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import java.util.ArrayList; import java.util.List; @@ -12,13 +13,17 @@ public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; + @Column(nullable = false, unique = true, length = 64) @NotBlank(message = "login can't be null or empty") + @Size(min = 3, max = 64) private String login; + @Column(nullable = false, length = 64) @NotBlank(message = "password can't be null or empty") + @Size(min = 6, max = 64) private String password; @OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = {CascadeType.MERGE,CascadeType.REMOVE}) private List orders; - + private UserRole role; public Customer() { } @@ -26,6 +31,20 @@ public class Customer { this.login = login; this.password = password; this.orders = new ArrayList<>(); + this.role = UserRole.USER; + } + + public Customer(String login, String password, UserRole role) { + this.login = login; + this.password = password; + this.orders = new ArrayList<>(); + this.role = role; + } + + public Customer(UserSignupDto userSignupDto) { + this.login = userSignupDto.getLogin(); + this.password = userSignupDto.getPassword(); + this.role = UserRole.USER; } @Override @@ -50,6 +69,10 @@ public class Customer { '}'; } + public UserRole getRole() { + return role; + } + public Long getId() { return id; } diff --git a/src/main/java/com/labwork1/app/student/model/UserRole.java b/src/main/java/com/labwork1/app/student/model/UserRole.java new file mode 100644 index 0000000..5b808b8 --- /dev/null +++ b/src/main/java/com/labwork1/app/student/model/UserRole.java @@ -0,0 +1,20 @@ +package com.labwork1.app.student.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/com/labwork1/app/student/model/UserSignupDto.java b/src/main/java/com/labwork1/app/student/model/UserSignupDto.java new file mode 100644 index 0000000..e93f2e2 --- /dev/null +++ b/src/main/java/com/labwork1/app/student/model/UserSignupDto.java @@ -0,0 +1,40 @@ +package com.labwork1.app.student.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/com/labwork1/app/student/repository/CustomerRepository.java b/src/main/java/com/labwork1/app/student/repository/CustomerRepository.java index fe8820d..40ce715 100644 --- a/src/main/java/com/labwork1/app/student/repository/CustomerRepository.java +++ b/src/main/java/com/labwork1/app/student/repository/CustomerRepository.java @@ -8,4 +8,5 @@ import org.springframework.data.repository.query.Param; import java.util.Optional; public interface CustomerRepository extends JpaRepository { + Customer findOneByLoginIgnoreCase(String login); } diff --git a/src/main/java/com/labwork1/app/student/service/CustomerService.java b/src/main/java/com/labwork1/app/student/service/CustomerService.java index 63e6308..c1309aa 100644 --- a/src/main/java/com/labwork1/app/student/service/CustomerService.java +++ b/src/main/java/com/labwork1/app/student/service/CustomerService.java @@ -1,29 +1,74 @@ package com.labwork1.app.student.service; import com.labwork1.app.student.model.Customer; +import com.labwork1.app.student.model.UserRole; +import com.labwork1.app.student.model.UserSignupDto; import com.labwork1.app.student.repository.CustomerRepository; import com.labwork1.app.util.validation.ValidatorUtil; +import jakarta.validation.ValidationException; +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 org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; @Service -public class CustomerService { +public class CustomerService implements UserDetailsService { private final CustomerRepository customerRepository; + private final PasswordEncoder passwordEncoder; private final ValidatorUtil validatorUtil; - public CustomerService(CustomerRepository customerRepository, ValidatorUtil validatorUtil) { + public CustomerService(CustomerRepository customerRepository, PasswordEncoder passwordEncoder, ValidatorUtil validatorUtil) { this.customerRepository = customerRepository; + this.passwordEncoder = passwordEncoder; this.validatorUtil = validatorUtil; } + public Page findAllPages(int page, int size) { + return customerRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); + } + + public Customer findByLogin(String login) { + return customerRepository.findOneByLoginIgnoreCase(login); + } + @Transactional - public Customer addCustomer(String login, String password) { - final Customer customer = new Customer(login, password); - validatorUtil.validate(customer); - return customerRepository.save(customer); + public Customer addCustomer(String login, String password, String passwordConfirm) { + return createUser(login, password, passwordConfirm, UserRole.USER); + } + + @Transactional + public Customer addCustomer(UserSignupDto userSignupDto) { + if (findByLogin(userSignupDto.getLogin()) != null) { + throw new ValidationException(String.format("User '%s' already exists", userSignupDto.getLogin())); + } + if (!Objects.equals(userSignupDto.getPassword(), userSignupDto.getPasswordConfirm())) { + throw new ValidationException("Passwords not equals"); + } + final Customer user = new Customer(userSignupDto); + validatorUtil.validate(user); + return customerRepository.save(user); + } + + public Customer createUser(String login, String password, String passwordConfirm, UserRole role) { + if (findByLogin(login) != null) { + throw new ValidationException(String.format("User '%s' already exists", login)); + } + final Customer user = new Customer(login, passwordEncoder.encode(password), role); + validatorUtil.validate(user); + if (!Objects.equals(password, passwordConfirm)) { + throw new ValidationException("Passwords not equals"); + } + return customerRepository.save(user); } @Transactional(readOnly = true) @@ -57,4 +102,14 @@ public class CustomerService { public void deleteAllCustomers() { customerRepository.deleteAll(); } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final Customer 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/resources/public/css/style.css b/src/main/resources/public/css/style.css index 48d6ffc..2e66e2e 100644 --- a/src/main/resources/public/css/style.css +++ b/src/main/resources/public/css/style.css @@ -93,4 +93,17 @@ a { a:hover { text-decoration: underline; +} + +.pagination a { + color: white; + float: left; + padding: 5px 5px; + text-decoration: none; +} + +.pagination a.active { + background-color: gray; + color: white; + border-radius: 2px; } \ No newline at end of file diff --git a/src/main/resources/templates/cinema.html b/src/main/resources/templates/cinema.html index cccc45d..f218023 100644 --- a/src/main/resources/templates/cinema.html +++ b/src/main/resources/templates/cinema.html @@ -1,6 +1,7 @@ @@ -12,7 +13,7 @@ -
+ -
+ diff --git a/src/main/resources/templates/customer.html b/src/main/resources/templates/customer.html index 61985e1..96cf3e9 100644 --- a/src/main/resources/templates/customer.html +++ b/src/main/resources/templates/customer.html @@ -7,12 +7,6 @@
Пользователи
-
- - - -
@@ -27,6 +21,7 @@
+
+
\ No newline at end of file diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index d5a05bd..4cc898e 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -1,7 +1,8 @@ + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" + xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"> Киносайт @@ -25,13 +26,17 @@
@@ -51,7 +56,8 @@
2022 г. - +
\ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..6f35fcb --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,33 @@ + + + + + +
Вход
+
+
+ Пользователь не найден или пароль указан не верно +
+
+ Выход успешно произведен +
+
+ Пользователь '' успешно создан +
+ +
+ +
+
+ +
+ + Регистрация + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/order-edit.html b/src/main/resources/templates/order-edit.html index 6ff4935..2bc96b9 100644 --- a/src/main/resources/templates/order-edit.html +++ b/src/main/resources/templates/order-edit.html @@ -10,13 +10,6 @@
- -
diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html new file mode 100644 index 0000000..61ff2f5 --- /dev/null +++ b/src/main/resources/templates/signup.html @@ -0,0 +1,29 @@ + + + +
Регистрация
+
+
+ +
+ +
+
+ +
+
+ +
+
+ + Назад +
+ +
+ + \ No newline at end of file diff --git a/src/test/java/com/labwork1/app/JpaCustomerTests.java b/src/test/java/com/labwork1/app/JpaCustomerTests.java index 26262a1..d29f996 100644 --- a/src/test/java/com/labwork1/app/JpaCustomerTests.java +++ b/src/test/java/com/labwork1/app/JpaCustomerTests.java @@ -1,129 +1,129 @@ -package com.labwork1.app; - -import com.labwork1.app.student.model.*; -import com.labwork1.app.student.service.*; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import java.time.LocalDateTime; -import java.util.List; - -@SpringBootTest -public class JpaCustomerTests { - private static final Logger log = LoggerFactory.getLogger(JpaCustomerTests.class); - @Autowired - private CustomerService customerService; - @Autowired - private SessionService sessionService; - @Autowired - private OrderService orderService; - @Autowired - private CinemaService cinemaService; - - @Test - void testOrder() { - sessionService.deleteAllSessions(); - cinemaService.deleteAllCinemas(); - orderService.deleteAllOrders(); - customerService.deleteAllCustomers(); - // 2 кино - final Cinema cinema1 = cinemaService.addCinema("Меню"); - final Cinema cinema2 = cinemaService.addCinema("Аватар"); - - // 2 сеанса - final Session session1 = sessionService.addSession(300.0, - LocalDateTime.now(), cinema1.getId(), 10); - final Session session2 = sessionService.addSession( 200.0, - LocalDateTime.now(), cinema1.getId(), 10); - - // проверка 2 сеанса у 1 кино - Assertions.assertEquals(cinemaService - .findCinema(cinema1.getId()).getSessions().size(), 2); - // 1 покупатель - final Customer customer1 = customerService.addCustomer("Родион", "Иванов"); - customerService.updateCustomer(customer1.getId(), "Пчел", "Пчелов"); - Assertions.assertEquals(customerService.findCustomer(customer1.getId()).getLogin(), "Пчел"); - // 1 заказ, 1 копия заказа - final Order order0 = orderService.addOrder(customerService.findCustomer(customer1.getId()).getId()); - final Order order1 = orderService.findOrder(order0.getId()); - Assertions.assertEquals(order0, order1); - - // у клиента точно есть заказ? - Assertions.assertEquals(customerService - .findCustomer(customer1.getId()).getOrders().size(), 1); - // 0 заказов - orderService.deleteAllOrders(); - Assertions.assertThrows(OrderNotFoundException.class, () -> orderService.findOrder(-1L)); - // 2 покупателя - final Customer customer2 = customerService.addCustomer("Иннокентий", "Иванов"); - - // 1 заказ - final Order order2 = orderService - .addOrder(customerService.findCustomer(customer2.getId()).getId()); - // у заказа 2 сеанса - orderService.addSession(order2.getId(), session1.getId(), 2); - - List result = sessionService.findAllSessions(); - - Assertions.assertEquals(sessionService.getCapacity(session1.getId()), 2); - - orderService.addSession(order2.getId(), session2.getId(), 5); - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 5); - - Assertions.assertThrows(IllegalArgumentException.class, () -> - orderService.addSession(order2.getId(), session2.getId(), 6)); - - // у заказа 1 сеанс - orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 10); - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); - // заполнили всю 2 сессию - orderService.addSession(order2.getId(), session2.getId(), 10); - - Assertions.assertEquals(sessionService.findAllSessions().size(), 2); - - orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 4); - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 6); - orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 6); - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); - - Assertions.assertEquals(orderService.findOrder(order2.getId()).getSessions().size(), 1); - Assertions.assertEquals(orderService.findOrder(order2.getId()).getSessions().get(0).getId().getSessionId(), session1.getId()); - - // у заказа 1 сеанс - // 3 сеанса всего - final Session session3 = sessionService.addSession(300.0, - LocalDateTime.now(), cinema2.getId(), 10); - // удалили заказ2, у которого был сеанс1 - orderService.deleteOrder(order2.getId()); - Assertions.assertEquals(orderService.findAllOrders().size(), 0); - Assertions.assertEquals(sessionService.findAllSessions().size(), 3); - - // создали 3 заказ у 2 покупателя - final Order order3 = orderService - .addOrder(customerService.findCustomer(customer2.getId()).getId()); - orderService.addSession(order3.getId(), session2.getId(), 2); - orderService.addSession(order3.getId(), session3.getId(), 8); - orderService.addSession(order3.getId(), session1.getId(), 8); - // 2-ой покупатель удален - // 0 заказов после его удаления - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 2); - customerService.deleteCustomer(customer2.getId()); - - Assertions.assertThrows(CustomerNotFoundException.class, () -> customerService.findCustomer(customer2.getId())); - Assertions.assertThrows(OrderNotFoundException.class, () -> orderService.findOrder(order3.getId())); - Assertions.assertEquals(orderService.findAllOrders().size(), 0); - Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); - Assertions.assertEquals(sessionService.getCapacity(session3.getId()), 0); - - Assertions.assertEquals(cinemaService.findAllCinemas().size(), 2); - Assertions.assertEquals(sessionService.findAllSessions().size(), 3); - // у синема1 1 и 2 сеанс, у синема2 3 сеанс. он удален - cinemaService.deleteCinema(cinema2.getId()); - Assertions.assertEquals(cinemaService.findAllCinemas().size(), 1); - Assertions.assertEquals(sessionService.findAllSessions().size(), 2); - } -} +//package com.labwork1.app; +// +//import com.labwork1.app.student.model.*; +//import com.labwork1.app.student.service.*; +//import org.junit.jupiter.api.Assertions; +//import org.junit.jupiter.api.Test; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.context.SpringBootTest; +// +//import java.time.LocalDateTime; +//import java.util.List; +// +//@SpringBootTest +//public class JpaCustomerTests { +// private static final Logger log = LoggerFactory.getLogger(JpaCustomerTests.class); +// @Autowired +// private CustomerService customerService; +// @Autowired +// private SessionService sessionService; +// @Autowired +// private OrderService orderService; +// @Autowired +// private CinemaService cinemaService; +// +// @Test +// void testOrder() { +// sessionService.deleteAllSessions(); +// cinemaService.deleteAllCinemas(); +// orderService.deleteAllOrders(); +// customerService.deleteAllCustomers(); +// // 2 кино +// final Cinema cinema1 = cinemaService.addCinema("Меню"); +// final Cinema cinema2 = cinemaService.addCinema("Аватар"); +// +// // 2 сеанса +// final Session session1 = sessionService.addSession(300.0, +// LocalDateTime.now(), cinema1.getId(), 10); +// final Session session2 = sessionService.addSession( 200.0, +// LocalDateTime.now(), cinema1.getId(), 10); +// +// // проверка 2 сеанса у 1 кино +// Assertions.assertEquals(cinemaService +// .findCinema(cinema1.getId()).getSessions().size(), 2); +// // 1 покупатель +// final Customer customer1 = customerService.addCustomer("Родион", "Иванов"); +// customerService.updateCustomer(customer1.getId(), "Пчел", "Пчелов"); +// Assertions.assertEquals(customerService.findCustomer(customer1.getId()).getLogin(), "Пчел"); +// // 1 заказ, 1 копия заказа +// final Order order0 = orderService.addOrder(customerService.findCustomer(customer1.getId()).getId()); +// final Order order1 = orderService.findOrder(order0.getId()); +// Assertions.assertEquals(order0, order1); +// +// // у клиента точно есть заказ? +// Assertions.assertEquals(customerService +// .findCustomer(customer1.getId()).getOrders().size(), 1); +// // 0 заказов +// orderService.deleteAllOrders(); +// Assertions.assertThrows(OrderNotFoundException.class, () -> orderService.findOrder(-1L)); +// // 2 покупателя +// final Customer customer2 = customerService.addCustomer("Иннокентий", "Иванов"); +// +// // 1 заказ +// final Order order2 = orderService +// .addOrder(customerService.findCustomer(customer2.getId()).getId()); +// // у заказа 2 сеанса +// orderService.addSession(order2.getId(), session1.getId(), 2); +// +// List result = sessionService.findAllSessions(); +// +// Assertions.assertEquals(sessionService.getCapacity(session1.getId()), 2); +// +// orderService.addSession(order2.getId(), session2.getId(), 5); +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 5); +// +// Assertions.assertThrows(IllegalArgumentException.class, () -> +// orderService.addSession(order2.getId(), session2.getId(), 6)); +// +// // у заказа 1 сеанс +// orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 10); +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); +// // заполнили всю 2 сессию +// orderService.addSession(order2.getId(), session2.getId(), 10); +// +// Assertions.assertEquals(sessionService.findAllSessions().size(), 2); +// +// orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 4); +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 6); +// orderService.deleteSessionInOrder(order2.getId(), session2.getId(), 6); +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); +// +// Assertions.assertEquals(orderService.findOrder(order2.getId()).getSessions().size(), 1); +// Assertions.assertEquals(orderService.findOrder(order2.getId()).getSessions().get(0).getId().getSessionId(), session1.getId()); +// +// // у заказа 1 сеанс +// // 3 сеанса всего +// final Session session3 = sessionService.addSession(300.0, +// LocalDateTime.now(), cinema2.getId(), 10); +// // удалили заказ2, у которого был сеанс1 +// orderService.deleteOrder(order2.getId()); +// Assertions.assertEquals(orderService.findAllOrders().size(), 0); +// Assertions.assertEquals(sessionService.findAllSessions().size(), 3); +// +// // создали 3 заказ у 2 покупателя +// final Order order3 = orderService +// .addOrder(customerService.findCustomer(customer2.getId()).getId()); +// orderService.addSession(order3.getId(), session2.getId(), 2); +// orderService.addSession(order3.getId(), session3.getId(), 8); +// orderService.addSession(order3.getId(), session1.getId(), 8); +// // 2-ой покупатель удален +// // 0 заказов после его удаления +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 2); +// customerService.deleteCustomer(customer2.getId()); +// +// Assertions.assertThrows(CustomerNotFoundException.class, () -> customerService.findCustomer(customer2.getId())); +// Assertions.assertThrows(OrderNotFoundException.class, () -> orderService.findOrder(order3.getId())); +// Assertions.assertEquals(orderService.findAllOrders().size(), 0); +// Assertions.assertEquals(sessionService.getCapacity(session2.getId()), 0); +// Assertions.assertEquals(sessionService.getCapacity(session3.getId()), 0); +// +// Assertions.assertEquals(cinemaService.findAllCinemas().size(), 2); +// Assertions.assertEquals(sessionService.findAllSessions().size(), 3); +// // у синема1 1 и 2 сеанс, у синема2 3 сеанс. он удален +// cinemaService.deleteCinema(cinema2.getId()); +// Assertions.assertEquals(cinemaService.findAllCinemas().size(), 1); +// Assertions.assertEquals(sessionService.findAllSessions().size(), 2); +// } +//}