This commit is contained in:
dasha 2023-05-10 17:05:37 +04:00
parent 038911a784
commit ce15f93adb
29 changed files with 665 additions and 213 deletions

View File

@ -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'

View File

@ -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();
}
}

View File

@ -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");
}
}

View File

@ -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("*");
}
}
}

View File

@ -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.*;

View File

@ -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";

View File

@ -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}")

View File

@ -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<OrderDto> 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<OrderDto> getOrders() {
return orders;
}
public UserRole getRole() {
return role;
}
}

View File

@ -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<CustomerDto> users = customerService.findAllPages(page, size)
.map(CustomerDto::new);
model.addAttribute("customers", users);
final int totalPages = users.getTotalPages();
final List<Integer> 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";
}
}

View File

@ -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.*;

View File

@ -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<CustomerDto> customers = customerService.findAllCustomers().stream()
.map(CustomerDto::new)
.toList();
model.addAttribute("orderDto", new OrderDto());
model.addAttribute("customers", customers);
return "order-edit";
} else {
List<OrderSessionDto> 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<OrderSessionDto> temp = order.getSessions()
.stream().map(x -> new OrderSessionDto(new SessionDto(x.getSession()),
x.getOrder().getId(), x.getCount())).toList();
List<SessionDto> 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";
}

View File

@ -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;

View File

@ -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";

View File

@ -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;
}
}

View File

@ -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";
}
}
}

View File

@ -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<Order> 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;
}

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -8,4 +8,5 @@ import org.springframework.data.repository.query.Param;
import java.util.Optional;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Customer findOneByLoginIgnoreCase(String login);
}

View File

@ -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<Customer> 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()));
}
}

View File

@ -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;
}

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"
layout:decorate="~{default}">
<head>
</head>
@ -12,7 +13,7 @@
<input class="form-control m-1" type="text" name="request" required="true"/>
<button class="btn btn-secondary m-1" type="submit"><i class="fa-solid fa-magnifying-glass"></i></button>
</form>
<div class="d-flex justify-content-end">
<div class="d-flex justify-content-end" sec:authorize="hasRole('ROLE_ADMIN')">
<a type="button" class="btn btn-success m-1"
th:href="@{/cinema/edit}">
<i class="fa-solid fa-plus"></i>
@ -36,16 +37,19 @@
<div class="rounded p-1 mx-2 green-mark">9.2</div>
<div>
<a type="button" class="m-1 btn btn-primary"
th:href="@{/cinema/edit/{id}(id=${cinema.id})}">
th:href="@{/cinema/edit/{id}(id=${cinema.id})}"
sec:authorize="hasRole('ROLE_ADMIN')">
<i class="fa fa-pencil"></i>
</a>
<a type="button" class="m-1 btn btn-danger"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${cinema.id}').click()|">
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${cinema.id}').click()|"
sec:authorize="hasRole('ROLE_ADMIN')">
<i class="fa fa-trash"></i>
</a>
</div>
</div>
<form th:action="@{/cinema/delete/{id}(id=${cinema.id})}" method="post">
<form th:action="@{/cinema/delete/{id}(id=${cinema.id})}" method="post"
sec:authorize="hasRole('ROLE_ADMIN')">
<button th:id="'remove-' + ${cinema.id}" type="submit" style="display: none">
Удалить
</button>

View File

@ -7,12 +7,6 @@
<body>
<div layout:fragment="content_header">Пользователи</div>
<div layout:fragment="content">
<div class="d-flex justify-content-end">
<a class="btn btn-success button-fixed"
th:href="@{/customer/edit}">
<i class="fa-solid fa-plus"></i>
</a>
</div>
<table class="table text-white" id="tbl-items">
<thead>
<tr>
@ -27,6 +21,7 @@
<td th:text="${customer.id}"/>
<td th:text="${customer.login}"/>
<td th:text="${customer.password}"/>
<td th:text="${customer.role}"/>
<td>
<div>
<a type="button" class="m-1 btn btn-primary" th:href="@{/customer/edit/{id}(id=${customer.id})}">
@ -46,6 +41,14 @@
</tr>
</tbody>
</table>
<div th:if="${totalPages > 0}" class="pagination">
<span style="float: left; padding: 5px 5px;">Страницы:</span>
<a th:each="page : ${pages}"
th:href="@{/customer(page=${page}, size=${customers.size})}"
th:text="${page}"
th:class="${page == customers.number + 1} ? active">
</a>
</div>
</div>
</body>
</html>

View File

@ -1,7 +1,8 @@
<!DOCTYPE html>
<html lang="ru"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
<meta charset="UTF-8"/>
<title>Киносайт</title>
@ -25,13 +26,17 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse menu" id="navbarNav">
<ul class="navbar-nav">
<ul class="navbar-nav" sec:authorize="!isAuthenticated()">
<a class="nav-link" href="/login">Вход</a>
</ul>
<ul class="navbar-nav" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/">Главная</a>
<a class="nav-link" href="/cinema">Фильмы</a>
<a class="nav-link" href="/customer">Регистрация</a>
<a sec:authorize="hasRole('ROLE_ADMIN')" class="nav-link" href="/customer">Пользователи</a>
<a class="nav-link" href="/order">Заказы</a>
<a class="nav-link" href="/session">Сеансы</a>
<a class="nav-link" href="/h2-console/" target="_blank">Консоль H2</a>
<a class="nav-link" href="/h2-console/" target="_blank" sec:authorize="hasRole('ROLE_ADMIN')">Консоль H2</a>
<a class="nav-link" href="/logout">Выход</a>
</ul>
</div>
</div>
@ -51,7 +56,8 @@
<th:block layout:fragment="scripts">
</th:block>
<footer class="d-flex align-items-center fw-bold fs-4 p-2 ps-5">2022 г.
<nav class="d-flex justify-content-center flex-grow-1"><a href="https://vk.com/id0" target="_blank"><img
class="icon" src="/vk.jpg" alt="VK"/></a></nav>
<nav class="d-flex justify-content-center flex-grow-1"><a href="https://vk.com/id0" target="_blank">
<img class="icon" src="/vk.jpg" alt="VK"/></a>
</nav>
</footer>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<head>
</head>
<div layout:fragment="content_header">Вход</div>
<div layout:fragment="content">
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
Пользователь не найден или пароль указан не верно
</div>
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
Выход успешно произведен
</div>
<div th:if="${param.created}" class="alert alert-success margin-bottom">
Пользователь '<span th:text="${param.created}"></span>' успешно создан
</div>
<form th:action="@{/login}" method="post">
<div class="mb-3">
<input type="text" name="username" id="username" class="form-control"
placeholder="Логин" required="true" autofocus="true"/>
</div>
<div class="mb-3">
<input type="password" name="password" id="password" class="form-control"
placeholder="Пароль" required="true"/>
</div>
<button type="submit" class="btn btn-success button-fixed">Войти</button>
<a class="btn btn-primary button-fixed" href="/signup">Регистрация</a>
</form>
</div>
</body>
</html>

View File

@ -10,13 +10,6 @@
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/order/{id}(id=${id})}" th:object="${orderDto}" method="post" th:if="${id == null}">
<div class="mb-3">
<label for="customer" class="form-label">Пользователь</label>
<select id="customer" class="form-select" th:field="${orderDto.customerId}" required="true">
<option th:each="value: ${customers}"
th:value="${value.id}"
th:text="${value.login}">
</option>
</select>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">

View File

@ -1,13 +1,14 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content_header" th:text="'Сеансы'"></div>
<div layout:fragment="content">
<div class="d-flex justify-content-end">
<div class="d-flex justify-content-end" sec:authorize="hasRole('ROLE_ADMIN')">
<a class="btn btn-success button-fixed"
th:href="@{/session/edit}">
<i class="fa-solid fa-plus"></i>
@ -35,15 +36,19 @@
<td th:text="${item.maxCount}"/>
<td>
<div>
<a type="button" class="m-1 btn btn-primary" th:href="@{/session/edit/{id}(id=${item.id})}">
<a type="button" class="m-1 btn btn-primary"
th:href="@{/session/edit/{id}(id=${item.id})}"
sec:authorize="hasRole('ROLE_ADMIN')">
<i class="fa fa-pencil"></i>
</a>
<a type="button" class="m-1 btn btn-danger"
sec:authorize="hasRole('ROLE_ADMIN')"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${item.id}').click()|">
<i class="fa fa-trash"></i>
</a>
</div>
<form th:action="@{/session/delete/{id}(id=${item.id})}" method="post">
<form th:action="@{/session/delete/{id}(id=${item.id})}" method="post"
sec:authorize="hasRole('ROLE_ADMIN')">
<button th:id="'remove-' + ${item.id}" type="submit" style="display: none">
Удалить
</button>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div layout:fragment="content_header">Регистрация</div>
<div layout:fragment="content">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<form action="#" th:action="@{/signup}" th:object="${userDto}" method="post">
<div class="mb-3">
<input type="text" class="form-control" th:field="${userDto.login}"
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.password}"
placeholder="Пароль" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.passwordConfirm}"
placeholder="Пароль (подтверждение)" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">Создать</button>
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -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<SessionExtension> 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<SessionExtension> 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);
// }
//}