diff --git a/pom.xml b/pom.xml index e965168..2d5d992 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,10 @@ springdoc-openapi-starter-webmvc-ui 2.8.8 + + org.springframework.boot + spring-boot-starter-security + diff --git a/src/main/java/com/example/nto/config/WebSecurityConfig.java b/src/main/java/com/example/nto/config/WebSecurityConfig.java new file mode 100644 index 0000000..508d772 --- /dev/null +++ b/src/main/java/com/example/nto/config/WebSecurityConfig.java @@ -0,0 +1,33 @@ +package com.example.nto.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .authorizeHttpRequests((authorize) -> authorize + .requestMatchers("/h2-console").permitAll() + .requestMatchers("/register").permitAll() + .anyRequest().authenticated() + ); + return http.build(); + } +} diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 7f73702..8f7d364 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -2,6 +2,7 @@ package com.example.nto.controller; import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.controller.dto.EmployeeRegisterDto; import com.example.nto.service.EmployeeService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -14,16 +15,20 @@ public class EmployeeController { private final EmployeeService employeeService; - @GetMapping("/{code}/auth") + @GetMapping("/{username}/auth") @ResponseStatus(code = HttpStatus.OK) - public void login(@PathVariable String code) { - employeeService.auth(code); + public void login(@PathVariable String username) { + employeeService.auth(username); } - @GetMapping("/{code}/info") + @GetMapping("/{username}/info") @ResponseStatus(code = HttpStatus.OK) - public EmployeeDto getByCode(@PathVariable String code) { - return employeeService.getByCode(code); + public EmployeeDto getByCode(@PathVariable String username) { + return employeeService.getByUsername(username); } + @GetMapping("/register") + @ResponseStatus(code = HttpStatus.CREATED) + public void register(EmployeeRegisterDto employeeRegisterDto) {employeeService.register(employeeRegisterDto);} + } diff --git a/src/main/java/com/example/nto/controller/dto/EmployeeRegisterDto.java b/src/main/java/com/example/nto/controller/dto/EmployeeRegisterDto.java new file mode 100644 index 0000000..9dbfbb4 --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/EmployeeRegisterDto.java @@ -0,0 +1,16 @@ +package com.example.nto.controller.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EmployeeRegisterDto { + private String name; + private String username; + private String password; +} diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java index e854a92..67be86c 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -5,7 +5,10 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; import java.util.List; @Data @@ -23,12 +26,16 @@ public class Employee { @Column(name = "name") private String name; - @Column(name = "code") - private String code; + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; @Column(name = "photo_url") private String photoUrl; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List bookingList; + } diff --git a/src/main/java/com/example/nto/exception/EmployeeAlreadyExistsException.java b/src/main/java/com/example/nto/exception/EmployeeAlreadyExistsException.java new file mode 100644 index 0000000..2c8c98f --- /dev/null +++ b/src/main/java/com/example/nto/exception/EmployeeAlreadyExistsException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class EmployeeAlreadyExistsException extends RuntimeException { + public EmployeeAlreadyExistsException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/PasswordNotCorrectException.java b/src/main/java/com/example/nto/exception/PasswordNotCorrectException.java new file mode 100644 index 0000000..97b00d2 --- /dev/null +++ b/src/main/java/com/example/nto/exception/PasswordNotCorrectException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class PasswordNotCorrectException extends RuntimeException { + public PasswordNotCorrectException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java index 234a158..f95660e 100644 --- a/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java @@ -1,8 +1,6 @@ package com.example.nto.exception.handler; -import com.example.nto.exception.BookingAlreadyExistsException; -import com.example.nto.exception.EmployeeNotFoundException; -import com.example.nto.exception.PlaceNotFoundException; +import com.example.nto.exception.*; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -25,8 +23,19 @@ public class GlobalExceptionHandler { return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } + @ExceptionHandler(EmployeeAlreadyExistsException.class) + public ResponseEntity handleEmployeeAlreadyExistsException(EmployeeAlreadyExistsException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT); + } + + @ExceptionHandler(PasswordNotCorrectException.class) + public ResponseEntity handlePasswordNotCorrectException(PasswordNotCorrectException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT); + } + @ExceptionHandler(Exception.class) public ResponseEntity handleGenericException(Exception e) { return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } + } diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index d845a04..0378807 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -8,5 +8,5 @@ import java.util.Optional; public interface EmployeeRepository extends JpaRepository { @EntityGraph(attributePaths = {"bookingList", "bookingList.place"}) - Optional findByCode(String code); + Optional findByUsername(String username); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index 83144af..5f221a7 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,9 +1,12 @@ package com.example.nto.service; import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.controller.dto.EmployeeRegisterDto; public interface EmployeeService { - EmployeeDto getByCode(String code); + EmployeeDto getByUsername(String username); - void auth(String code); + void auth(String username); + + void register(EmployeeRegisterDto employeeRegisterDto); } diff --git a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java index ffc4f86..7ad558d 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -37,8 +37,8 @@ public class BookingServiceImpl implements BookingService { @Override @Transactional(readOnly = true) - public Map> getFreePlace(String code) { - employeeService.auth(code); + public Map> getFreePlace(String username) { + employeeService.auth(username); List allPlaces = placeRepository.findAll(); @@ -72,15 +72,15 @@ public class BookingServiceImpl implements BookingService { @Override @Transactional - public Booking create(String code, BookingCreateDto bookingCreateDto) { + public Booking create(String username, BookingCreateDto bookingCreateDto) { LocalDate date = bookingCreateDto.getDate(); LocalDate today = LocalDate.now(ZoneId.systemDefault()); if (date.isBefore(today) || date.isAfter(today.plusDays(daysAhead))) { throw new IllegalArgumentException("Date is out of booking window"); } - Employee employee = employeeRepository.findByCode(code) - .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!")); + Employee employee = employeeRepository.findByUsername(username) + .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + username + " code not found!")); long placeId = bookingCreateDto.getPlaceId(); Place place = placeRepository.findById(placeId) diff --git a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java index 3085dc2..4e045ff 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,10 +1,15 @@ package com.example.nto.service.impl; import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.controller.dto.EmployeeRegisterDto; +import com.example.nto.entity.Employee; +import com.example.nto.exception.EmployeeAlreadyExistsException; import com.example.nto.exception.EmployeeNotFoundException; +import com.example.nto.exception.PasswordNotCorrectException; import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -13,19 +18,40 @@ import org.springframework.transaction.annotation.Transactional; public class EmployeeServiceImpl implements EmployeeService { private final EmployeeRepository employeeRepository; + private PasswordEncoder passwordEncoder; @Override @Transactional(readOnly = true) - public EmployeeDto getByCode(String code) { - return employeeRepository.findByCode(code).map(EmployeeDto::toDto) - .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!")); + public EmployeeDto getByUsername(String username) { + return employeeRepository.findByUsername(username).map(EmployeeDto::toDto) + .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + username + " code not found!")); } @Override @Transactional(readOnly = true) - public void auth(String code) { - if (employeeRepository.findByCode(code).isEmpty()) { - throw new EmployeeNotFoundException("Employee with " + code + " code not found!"); + public void auth(String username) { + if (employeeRepository.findByUsername(username).isEmpty()) { + throw new EmployeeNotFoundException("Employee with " + username + " username not found!"); } } + + @Override + public void register(EmployeeRegisterDto employeeRegisterDto) { + if (employeeRepository.findByUsername(employeeRegisterDto.getUsername()).isPresent()){ + throw new EmployeeAlreadyExistsException("Employee with " + employeeRegisterDto.getUsername() + " username already exist"); + }; + + Employee employee = new Employee(); + + if (passwordEncoder.encode(employee.getPassword()).length() < 8) { + throw new PasswordNotCorrectException("The password is too short!!!"); + } + + employee.setName(employeeRegisterDto.getName()); + employee.setUsername(employeeRegisterDto.getUsername()); + employee.setPassword(passwordEncoder.encode(employeeRegisterDto.getPassword())); + + employeeRepository.save(employee); + } + } diff --git a/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml b/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml index d1f92f8..bc84754 100644 --- a/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml +++ b/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml @@ -21,10 +21,14 @@ - + + + + + diff --git a/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv b/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv index 87ddc6b..e3b3fe8 100644 --- a/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv +++ b/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv @@ -1,5 +1,5 @@ -name;code;photo_url -Ivanov Ivan;1111;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Petrov Petr;2222;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Kozlov Oleg;3333;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Smirnova Anna;4444;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg \ No newline at end of file +name;username;password;photo_url +Ivanov Ivan;iivanov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg +Petrov Petr;ppetrov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg +Kozlov Oleg;okozlov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg +Smirnova Anna;asmirnova;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg \ No newline at end of file