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