Compare commits

...

10 Commits

Author SHA1 Message Date
cae356d4c6 Final Commit (Maybe) 2025-02-20 16:19:54 +03:00
ea3a05fca6 Changed project name 2025-02-20 16:11:31 +03:00
8d2d647054 More data 2025-02-20 15:48:35 +03:00
271287bc5e - Version change
- More DB data
2025-02-20 15:32:44 +03:00
54b8b99873 Changed tests 2025-02-20 15:04:25 +03:00
aaa550d718 412 Error code for change state request 2025-02-20 14:55:03 +03:00
d3390792a1 Improved user state changing 2025-02-20 14:43:21 +03:00
b37e4bce34 - Increased varchar limit from 100 to 250 on PhotoUrl
- Added profile update request for admin panel
2025-02-20 12:22:42 +03:00
34169e70d2 - Increased varchar limit from 100 to 250 on PhotoUrl
- Added profile update request for admin panel
2025-02-20 11:44:43 +03:00
6309648c40 Centered Teapot 2025-02-20 11:18:52 +03:00
14 changed files with 84 additions and 46 deletions

View File

@ -5,7 +5,7 @@ plugins {
} }
group = 'com.index' group = 'com.index'
version = '0.0.1-SNAPSHOT' version = '1.0.0-RELEASE'
java { java {
toolchain { toolchain {

View File

@ -1,5 +1,5 @@
FROM eclipse-temurin:21 FROM eclipse-temurin:21
LABEL authors="indx" LABEL authors="indx"
COPY ./build/libs/S-QR-Backend-0.0.1-SNAPSHOT.jar /opt/app/ COPY ./build/libs/S-Pass-Backend-1.0.0-RELEASE.jar /opt/app/
CMD ["java", "-jar", "/opt/app/S-QR-Backend-0.0.1-SNAPSHOT.jar"] CMD ["java", "-jar", "/opt/app/S-Pass-Backend-1.0.0-RELEASE.jar"]

View File

@ -1 +1 @@
rootProject.name = 'S-QR-Backend' rootProject.name = 'S-Pass-Backend'

View File

@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
@ -42,6 +41,7 @@ public class SecurityConfig {
.requestMatchers("/api/employee/{login}/{state}").hasAuthority("ADMIN") .requestMatchers("/api/employee/{login}/{state}").hasAuthority("ADMIN")
.requestMatchers("/api/employee/{login}").hasAuthority("ADMIN") .requestMatchers("/api/employee/{login}").hasAuthority("ADMIN")
.requestMatchers("/api/employee/all").hasAuthority("ADMIN") .requestMatchers("/api/employee/all").hasAuthority("ADMIN")
.requestMatchers("/api/employee/{login}/update").hasAuthority("ADMIN")
// Entrance for everyone // Entrance for everyone
.requestMatchers("/api/entrance").authenticated() .requestMatchers("/api/entrance").authenticated()

View File

@ -30,7 +30,7 @@ public class EmployeeController {
@ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "401", description = "Unauthorized"),
}) })
public ResponseEntity<Object> login() { public ResponseEntity<Object> login() {
return new ResponseEntity(HttpStatus.OK); return new ResponseEntity<>(HttpStatus.OK);
} }
@PostMapping("/profile") @PostMapping("/profile")
@ -68,17 +68,17 @@ public class EmployeeController {
return employeeService.deleteEmployee(login); return employeeService.deleteEmployee(login);
} }
@PatchMapping("/{login}/{state}") @PatchMapping("/{login}/change_state")
@Operation(description = "Enable/Disable user's ability to use QR code entrance. (ADMIN only) States: active / blocked", summary = "Enable/Disable QR") @Operation(description = "Enable/Disable user's ability to use QR code entrance. (ADMIN only)", summary = "Enable/Disable QR")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Modification Successful"), @ApiResponse(responseCode = "200", description = "Modification Successful"),
@ApiResponse(responseCode = "412", description = "User you're trying to block has ADMIN privileges"),
@ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "User not found"), @ApiResponse(responseCode = "404", description = "User not found"),
@ApiResponse(responseCode = "400", description = "State doesn't exist"),
}) })
public ResponseEntity<HttpStatusCode> changeState(@PathVariable String login, @PathVariable String state) { public ResponseEntity<HttpStatusCode> changeState(@PathVariable String login) {
return employeeService.changeState(login, state); return employeeService.changeState(login);
} }
@GetMapping("/all") @GetMapping("/all")
@ -96,7 +96,7 @@ public class EmployeeController {
@GetMapping("/{login}") @GetMapping("/{login}")
@Operation(description = "Get user's profile by login (ADMIN only)", summary = "Get user's profile by login") @Operation(description = "Get user's profile by login (ADMIN only)", summary = "Get user's profile by login")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Modification Successful"), @ApiResponse(responseCode = "200", description = "Request Successful"),
@ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "User not found"), @ApiResponse(responseCode = "404", description = "User not found"),
@ -105,4 +105,16 @@ public class EmployeeController {
return employeeService.getEmployeeByLogin(login); return employeeService.getEmployeeByLogin(login);
} }
@PatchMapping("/{login}/update")
@Operation(description = "Update user's profile (ADMIN only). Accepts name, position and photo_url properties", summary = "Update user's profile")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Modification Successful"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "User not found"),
})
public ResponseEntity<EmployeeDTO> updateEmployee(@PathVariable String login, @RequestBody EmployeeDTO updateDTO) {
return employeeService.updateEmployee(updateDTO, login);
}
} }

View File

@ -35,6 +35,7 @@ public class EntranceController {
@Operation(description = "Get user's last entries. Username is taken from Authentication", summary = "Get user's last entry") @Operation(description = "Get user's last entries. Username is taken from Authentication", summary = "Get user's last entry")
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request Successful."), @ApiResponse(responseCode = "200", description = "Request Successful."),
@ApiResponse(responseCode = "204", description = "No Entrances."),
@ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "401", description = "Unauthorized"),
}) })
public ResponseEntity<EntranceDTO> getLastEntrance() { public ResponseEntity<EntranceDTO> getLastEntrance() {

View File

@ -13,7 +13,8 @@ public interface EmployeeService {
ResponseEntity<EmployeeDTO> getUserInfo(Authentication auth); ResponseEntity<EmployeeDTO> getUserInfo(Authentication auth);
ResponseEntity<Object> openTheDoor(Long code, Authentication auth); ResponseEntity<Object> openTheDoor(Long code, Authentication auth);
ResponseEntity<HttpStatusCode> deleteEmployee(String login); ResponseEntity<HttpStatusCode> deleteEmployee(String login);
ResponseEntity<HttpStatusCode> changeState(String login, String state); ResponseEntity<HttpStatusCode> changeState(String login);
ResponseEntity<Page<EmployeeDTO>> getAllEmployees(Pageable pageable); ResponseEntity<Page<EmployeeDTO>> getAllEmployees(Pageable pageable);
ResponseEntity<EmployeeDTO> getEmployeeByLogin(String login); ResponseEntity<EmployeeDTO> getEmployeeByLogin(String login);
ResponseEntity<EmployeeDTO> updateEmployee(EmployeeDTO updateDTO, String login);
} }

View File

@ -39,7 +39,7 @@ public class EmployeeServiceImpl implements EmployeeService {
public ResponseEntity<Object> openTheDoor(Long code, Authentication auth) { public ResponseEntity<Object> openTheDoor(Long code, Authentication auth) {
try { try {
if (codeRepository.existsByValue(Long.valueOf(code))) { if (codeRepository.existsByValue(code)) {
Employee employee = employeeRepository.findByLogin(auth.getName()); Employee employee = employeeRepository.findByLogin(auth.getName());
if(employee.getIsQREnabled()) { if(employee.getIsQREnabled()) {
Entrance entrance = new Entrance(); Entrance entrance = new Entrance();
@ -86,26 +86,16 @@ public class EmployeeServiceImpl implements EmployeeService {
} }
@Override @Override
public ResponseEntity<HttpStatusCode> changeState(String login, String state) { public ResponseEntity<HttpStatusCode> changeState(String login) {
Employee e = employeeRepository.findByLogin(login); Employee e = employeeRepository.findByLogin(login);
if(e != null) { if(e != null) {
if (Objects.equals(e.getAuthorities().iterator().next().getAuthority(), "ADMIN")) { if (Objects.equals(e.getAuthorities().iterator().next().getAuthority(), "ADMIN")) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} }
else { else {
if(state.equals("active")) { e.setIsQREnabled(!e.getIsQREnabled());
e.setIsQREnabled(true); employeeRepository.save(e);
employeeRepository.save(e); return new ResponseEntity<>(HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.OK);
}
else if(state.equals("blocked")) {
e.setIsQREnabled(false);
employeeRepository.save(e);
return new ResponseEntity<>(HttpStatus.OK);
}
else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
} }
} }
else { else {
@ -128,4 +118,25 @@ public class EmployeeServiceImpl implements EmployeeService {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} }
} }
@Override
public ResponseEntity<EmployeeDTO> updateEmployee(EmployeeDTO updateDTO, String login) {
Employee e = employeeRepository.findByLogin(login);
if(e != null) {
if(updateDTO.getName() != null) {
e.setName(updateDTO.getName());
}
if (updateDTO.getPosition() != null) {
e.setPosition(updateDTO.getPosition());
}
if (updateDTO.getPhotoUrl() != null) {
e.setPhotoUrl(updateDTO.getPhotoUrl());
}
employeeRepository.save(e);
return new ResponseEntity<>(EmployeeMapper.convertToDTO(e), HttpStatus.OK);
}
else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
} }

View File

@ -41,8 +41,12 @@ public class EntranceServiceImpl implements EntranceService {
public ResponseEntity<EntranceDTO> getLastEntrance(Authentication auth) { public ResponseEntity<EntranceDTO> getLastEntrance(Authentication auth) {
Employee employee = employeeRepository.findByLogin(auth.getName()); Employee employee = employeeRepository.findByLogin(auth.getName());
List<EntranceDTO> entrances = employee.getEntrances().stream().map(EntranceMapper::convertToDTO).toList(); List<EntranceDTO> entrances = employee.getEntrances().stream().map(EntranceMapper::convertToDTO).toList();
System.out.println(entrances.getLast()); if(entrances.isEmpty()) {
return new ResponseEntity<>(entrances.getLast(), HttpStatus.OK); return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
else {
return new ResponseEntity<>(entrances.getLast(), HttpStatus.OK);
}
} }
@Override @Override

View File

@ -5,7 +5,7 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="2024-10-20--0002-employee" author="okalugin"> <changeSet id="2024-10-20--0002-employee-02" author="okalugin">
<preConditions onFail="MARK_RAN"> <preConditions onFail="MARK_RAN">
<not> <not>
@ -37,7 +37,7 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="photo_url" type="VARCHAR(100)"> <column name="photo_url" type="VARCHAR(250)">
<constraints nullable="true"/> <constraints nullable="true"/>
</column> </column>

View File

@ -1,6 +1,9 @@
id;value;name;entry_type value;name;entry_type
1;1234567890123456789;Главный Вход;Вход 820962709643279872;Главный Вход;Вход
2;9223372036854775807;Главный Выход;Выход 353264154792598510;Главный Выход;Выход
3;1122334455667788990;Задний вход;Вход 269199677577482211;Задний вход;Вход
4;998877665544332211;Выход с парковки;Выход 393339578854043555;Задний выход;Выход
5;5566778899001122334;Вход с улицы Пушкина;Вход 106684466846571407;Вход на парковку;Вход
604595281495436727;Выход с парковки;Выход
440569142882630461;Вход с улицы Пушкина;Вход
615262283026725501;Выход с улицы Пушкина;Выход
1 id value name entry_type
2 1 1234567890123456789 820962709643279872 Главный Вход Вход
3 2 9223372036854775807 353264154792598510 Главный Выход Выход
4 3 1122334455667788990 269199677577482211 Задний вход Вход
5 4 998877665544332211 393339578854043555 Выход с парковки Задний выход Выход
6 5 5566778899001122334 106684466846571407 Вход с улицы Пушкина Вход на парковку Вход
7 604595281495436727 Выход с парковки Выход
8 440569142882630461 Вход с улицы Пушкина Вход
9 615262283026725501 Выход с улицы Пушкина Выход

View File

@ -2,4 +2,7 @@ login;password;name;photo_url;position;is_enabled
pivanov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Иванов Петр Федорович;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Разработчик;true pivanov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Иванов Петр Федорович;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Разработчик;true
ipetrov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Петров Иван Константинович;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Аналитик;false ipetrov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Петров Иван Константинович;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Аналитик;false
asemenov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Семенов Анатолий Анатольевич;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Разработчик;true asemenov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Семенов Анатолий Анатольевич;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Разработчик;true
afedorov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Федоров Александр Сергеевич;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;Тестировщик;true afedorov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Федоров Александр Сергеевич;https://i.postimg.cc/R0tz9yFr/skala.jpg;Тестировщик;true
alimasov;$2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK;Андрей Лимасов;https://i.postimg.cc/L5zBsbnP/IMG-20250220-101919.jpg;Разработчик;true
admin12345;$2a$10$whV2pS0u.zGocamf2NdEEeBB2GylZxAA2ACz6RVCSrFxz1PJqnnEG;Админ Админов Админович;https://i.postimg.cc/R0tz9yFr/skala.jpg;Админ;true
user12345;$2a$10$mtmczxpEx7iZIEAvh8UrSOVUoJurFCIcuNXMTu5IpNnIyBR9uAV16;Райан Гослинг;https://i.postimg.cc/15kwCVL9/347817.jpg;Пользователь;true
1 login password name photo_url position is_enabled
2 pivanov $2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK Иванов Петр Федорович https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg Разработчик true
3 ipetrov $2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK Петров Иван Константинович https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg Аналитик false
4 asemenov $2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK Семенов Анатолий Анатольевич https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg Разработчик true
5 afedorov $2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK Федоров Александр Сергеевич https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg https://i.postimg.cc/R0tz9yFr/skala.jpg Тестировщик true
6 alimasov $2a$10$Jzb9I5eeHC0UIn/q5Rhq..wkI7KicBEZKB2u5BvnH8.n12d4alTOK Андрей Лимасов https://i.postimg.cc/L5zBsbnP/IMG-20250220-101919.jpg Разработчик true
7 admin12345 $2a$10$whV2pS0u.zGocamf2NdEEeBB2GylZxAA2ACz6RVCSrFxz1PJqnnEG Админ Админов Админович https://i.postimg.cc/R0tz9yFr/skala.jpg Админ true
8 user12345 $2a$10$mtmczxpEx7iZIEAvh8UrSOVUoJurFCIcuNXMTu5IpNnIyBR9uAV16 Райан Гослинг https://i.postimg.cc/15kwCVL9/347817.jpg Пользователь true

View File

@ -3,3 +3,6 @@ employee_id;authorities_id
2;1 2;1
3;1 3;1
4;1 4;1
5;2
6;2
7;1
1 employee_id authorities_id
3 2 1
4 3 1
5 4 1
6 5 2
7 6 2
8 7 1

View File

@ -42,7 +42,7 @@ class EmployeeControllerTests {
void doorOpen() throws Exception { void doorOpen() throws Exception {
this.mockMvc.perform( this.mockMvc.perform(
patch("/api/employee/open") patch("/api/employee/open")
.param("code", "1234567890123456789") .param("code", "820962709643279872")
.with(httpBasic("pivanov", "HelloWorld1234"))) .with(httpBasic("pivanov", "HelloWorld1234")))
.andDo(print()) .andDo(print())
.andExpect(status().isOk()); .andExpect(status().isOk());
@ -61,12 +61,12 @@ class EmployeeControllerTests {
@Test @Test
void lockAndUnlockUser() throws Exception { void lockAndUnlockUser() throws Exception {
this.mockMvc.perform( this.mockMvc.perform(
patch("/api/employee/ipetrov/blocked") patch("/api/employee/ipetrov/change_state")
.with(httpBasic("pivanov", "HelloWorld1234"))) .with(httpBasic("pivanov", "HelloWorld1234")))
.andDo(print()) .andDo(print())
.andExpect(status().isOk()); .andExpect(status().isOk());
this.mockMvc.perform( this.mockMvc.perform(
patch("/api/employee/ipetrov/active") patch("/api/employee/ipetrov/change_state")
.with(httpBasic("pivanov", "HelloWorld1234"))) .with(httpBasic("pivanov", "HelloWorld1234")))
.andDo(print()) .andDo(print())
.andExpect(status().isOk()); .andExpect(status().isOk());
@ -75,12 +75,12 @@ class EmployeeControllerTests {
@Test @Test
void lockAndUnlockUserNotFound() throws Exception { void lockAndUnlockUserNotFound() throws Exception {
this.mockMvc.perform( this.mockMvc.perform(
patch("/api/employee/PetrTestovich/blocked") patch("/api/employee/PetrTestovich/change_state")
.with(httpBasic("pivanov", "HelloWorld1234"))) .with(httpBasic("pivanov", "HelloWorld1234")))
.andDo(print()) .andDo(print())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());
this.mockMvc.perform( this.mockMvc.perform(
patch("/api/employee/PetrTestovich/active") patch("/api/employee/PetrTestovich/change_state")
.with(httpBasic("pivanov", "HelloWorld1234"))) .with(httpBasic("pivanov", "HelloWorld1234")))
.andDo(print()) .andDo(print())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());