Visit entity; Bugfix; Added password

This commit is contained in:
Universall 2025-02-19 11:42:31 +03:00
parent 15320350e3
commit 52f900ec4d
24 changed files with 289 additions and 56 deletions

View File

@ -2,15 +2,29 @@ package com.displaynone.acss.components.acs;
import com.displaynone.acss.components.acs.code.CodeModel;
import com.displaynone.acss.components.acs.code.service.CodeService;
import com.displaynone.acss.components.acs.visit.VisitModel;
import com.displaynone.acss.components.acs.visit.service.VisitService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@RequiredArgsConstructor
public class ACSComponent {
private final CodeService codeService;
private final VisitService visitService;
public Optional<CodeModel> getCodeById(Long codeId) {
return codeService.getCodeById(codeId);
}
public CodeModel getCodeByIdStrict(Long codeId) {
return codeService.getCodeByIdStrict(codeId);
}
public Optional<CodeModel> getCodeByValue(Long value) {
return codeService.getCodeByValue(value);
@ -20,7 +34,27 @@ public class ACSComponent {
return codeService.getCodeByValueStrict(value);
}
public boolean isValid(Long code) {
return codeService.isValid(code);
public boolean isCodeIdValid(Long codeId) {
return codeService.isValid(codeId);
}
public boolean isCodeValid(Long codeId) {
return codeService.isCodeIdValid(codeId);
}
public List<VisitModel> getVisitsByCodeId(Long codeId) {
return visitService.getByCodeId(codeId);
}
public List<VisitModel> getVisitsByUserId(Long userId) {
return visitService.getByUserId(userId);
}
public Page<VisitModel> getVisitsByCodeIdPaginated(Long codeId, Pageable pageable) {
return visitService.getVisitsByCodeIdPaginated(codeId, pageable);
}
public Page<VisitModel> getVisitsByUserIdPaginated(Long userId, Pageable pageable) {
return visitService.getVisitsByUserIdPaginated(userId, pageable);
}
}

View File

@ -1,8 +1,5 @@
package com.displaynone.acss.components.acs.code;
import com.displaynone.acss.components.auth.models.role.RoleDTO;
import com.displaynone.acss.components.auth.models.role.RoleMapper;
import com.displaynone.acss.components.auth.models.role.RoleModel;
import com.displaynone.acss.utils.GlobalUtils;
import lombok.experimental.UtilityClass;
@ -20,7 +17,7 @@ public class CodeMapper {
return dto;
}
public List<RoleDTO> convertAllToDTO(Collection<RoleModel> models) {
return GlobalUtils.convertAllToDTO(models, RoleMapper::convertToDTO);
public List<CodeDTO> convertAllToDTO(Collection<CodeModel> models) {
return GlobalUtils.convertAllToDTO(models, CodeMapper::convertToDTO);
}
}

View File

@ -16,5 +16,7 @@ public class CodeModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
private long codeValue;
}

View File

@ -5,9 +5,15 @@ import com.displaynone.acss.components.acs.code.CodeModel;
import java.util.Optional;
public interface CodeService {
Optional<CodeModel> getCodeById(Long codeId);
CodeModel getCodeByIdStrict(Long codeId);
Optional<CodeModel> getCodeByValue(Long value);
CodeModel getCodeByValueStrict(Long value);
boolean isCodeIdValid(Long codeID);
boolean isValid(Long code);
}

View File

@ -14,6 +14,18 @@ import java.util.Optional;
public class CodeServiceImpl implements CodeService {
private final CodeRepository codeRepository;
@Override
public Optional<CodeModel> getCodeById(Long codeId) {
return codeRepository.findById(codeId);
}
@Override
public CodeModel getCodeByIdStrict(Long codeId) {
Optional<CodeModel> model = codeRepository.findById(codeId);
if (model.isEmpty()) throw new NotFoundHTTPException("Code not found");
return model.get();
}
@Override
public Optional<CodeModel> getCodeByValue(Long value) {
return codeRepository.findByCodeValue(value);
@ -26,6 +38,11 @@ public class CodeServiceImpl implements CodeService {
return model.get();
}
@Override
public boolean isCodeIdValid(Long codeID) {
return getCodeById(codeID).isPresent();
}
@Override
public boolean isValid(Long code) {
return getCodeByValue(code).isPresent();

View File

@ -0,0 +1,13 @@
package com.displaynone.acss.components.acs.visit;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class VisitDTO {
private Long id;
private Long userId;
private Long codeId;
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,25 @@
package com.displaynone.acss.components.acs.visit;
import com.displaynone.acss.utils.GlobalUtils;
import lombok.experimental.UtilityClass;
import java.util.Collection;
import java.util.List;
@UtilityClass
public class VisitMapper {
public VisitDTO convertToDTO(VisitModel model) {
VisitDTO dto = new VisitDTO();
dto.setId(model.getId());
dto.setUserId(model.getUserId());
dto.setCodeId(model.getCodeId());
dto.setCreatedAt(model.getCreatedAt());
return dto;
}
public List<VisitDTO> convertAllToDTO(Collection<VisitModel> models) {
return GlobalUtils.convertAllToDTO(models, VisitMapper::convertToDTO);
}
}

View File

@ -0,0 +1,30 @@
package com.displaynone.acss.components.acs.visit;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Entity
@Data
@Builder
@Table(name = "visits")
@NoArgsConstructor
@AllArgsConstructor
public class VisitModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long userId;
@Column(nullable = false)
private Long codeId;
@Column(nullable = false)
private LocalDateTime createdAt;
}

View File

@ -0,0 +1,17 @@
package com.displaynone.acss.components.acs.visit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface VisitRepository extends JpaRepository<VisitModel, Long> {
List<VisitModel> findByCodeId(Long codeId);
List<VisitModel> findByUserId(Long userId);
Page<VisitModel> findByCodeId(Long codeId, Pageable page);
Page<VisitModel> findByUserId(Long userId, Pageable page);
}

View File

@ -0,0 +1,17 @@
package com.displaynone.acss.components.acs.visit.service;
import com.displaynone.acss.components.acs.visit.VisitModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
public interface VisitService {
List<VisitModel> getByCodeId(Long code);
List<VisitModel> getByUserId(Long userID);
Page<VisitModel> getVisitsByCodeIdPaginated(Long codeId, Pageable pageable);
Page<VisitModel> getVisitsByUserIdPaginated(Long userId, Pageable pageable);
}

View File

@ -0,0 +1,36 @@
package com.displaynone.acss.components.acs.visit.service;
import com.displaynone.acss.components.acs.visit.VisitModel;
import com.displaynone.acss.components.acs.visit.VisitRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@RequiredArgsConstructor
public class VisitServiceImpl implements VisitService {
private final VisitRepository visitRepository;
@Override
public List<VisitModel> getByCodeId(Long code) {
return visitRepository.findByCodeId(code);
}
@Override
public List<VisitModel> getByUserId(Long userId) {
return visitRepository.findByUserId(userId);
}
@Override
public Page<VisitModel> getVisitsByCodeIdPaginated(Long codeId, Pageable pageable) {
return visitRepository.findByCodeId(codeId, pageable);
}
@Override
public Page<VisitModel> getVisitsByUserIdPaginated(Long userId, Pageable pageable) {
return visitRepository.findByUserId(userId, pageable);
}
}

View File

@ -13,6 +13,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.Optional;
@ -22,6 +23,7 @@ import java.util.Optional;
public class AuthComponent implements UserDetailsService {
private JWTUtils jwtUtils;
private final UserService userService;
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
@Autowired
public AuthComponent(JWTUtils jwtUtils, UserService userService) {
@ -63,9 +65,10 @@ public class AuthComponent implements UserDetailsService {
return _generateTokenPair(userModel);
}
public Pair<UserModel, AuthTokenPair> authenticate(@NonNull String login) {
UserModel userModel = getUserByLoginStrict(login);
return Pair.of(userModel, authenticate(userModel));
public Pair<UserModel, AuthTokenPair> authenticate(@NonNull String login, @NonNull String password) {
UserModel userModel = userService.findByLoginStrict(login);
if (passwordEncoder.matches(password, userModel.getPassword())) return Pair.of(userModel, authenticate(userModel));
throw new UnauthorizedHTTPException("Wrong auth credentials");
}
public AuthTokenPair authenticate(@NonNull UserDetails user) {

View File

@ -16,7 +16,6 @@ public class UserMapper {
dto.setName(model.getName());
dto.setPhoto(model.getPhoto());
dto.setPosition(model.getPosition());
dto.setLastVisit(model.getLastVisit());
dto.setRoles(model.getRoles());
return dto;

View File

@ -7,7 +7,6 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
@ -26,6 +25,9 @@ public class UserModel implements UserDetails {
@Column(unique = true, nullable = false)
private String login;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String name;
@ -35,9 +37,6 @@ public class UserModel implements UserDetails {
@Column(nullable = false)
private String position;
@Column(nullable = false)
private LocalDateTime lastVisit;
@EqualsAndHashCode.Exclude
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
@ -59,11 +58,6 @@ public class UserModel implements UserDetails {
return login;
}
@Override
public String getPassword() {
return "";
}
@Override
public boolean isAccountNonExpired() {
return true;

View File

@ -1,24 +1,64 @@
package com.displaynone.acss.controllers.acs;
import com.displaynone.acss.components.acs.ACSComponent;
import com.displaynone.acss.components.acs.visit.VisitDTO;
import com.displaynone.acss.components.acs.visit.VisitMapper;
import com.displaynone.acss.components.auth.AuthComponent;
import com.displaynone.acss.components.auth.models.user.UserModel;
import com.displaynone.acss.exception.generics.ForbiddenHTTPException;
import com.displaynone.acss.exception.generics.NotFoundHTTPException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/acs")
@RequiredArgsConstructor
public class ACSController {
private final ACSComponent acsComponent;
private final AuthComponent authComponent;
@PostMapping("/open")
public ResponseEntity<Void> open(@RequestBody OpenRB body) {
public ResponseEntity<Void> open(@RequestBody OpenRQB body) {
Long code = body.getCode();
if (!acsComponent.isValid(code)) throw new ForbiddenHTTPException("Invalid code");
if (!acsComponent.isCodeValid(code)) throw new ForbiddenHTTPException("Invalid code");
return ResponseEntity.ok(null);
}
@GetMapping("visits/me")
public ResponseEntity<Page<VisitDTO>> getMyVisitsPaginated(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
Pageable pageable = PageRequest.of(page, size);
UserModel user = (UserModel) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return ResponseEntity.ok(acsComponent.getVisitsByUserIdPaginated(user.getId(), pageable).map(VisitMapper::convertToDTO));
}
@GetMapping("visits/login/{login}")
public ResponseEntity<Page<VisitDTO>> getUserVisitsPaginated(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@PathVariable String login
) {
Pageable pageable = PageRequest.of(page, size);
UserModel user = authComponent.getUserByLoginStrict(login);
return ResponseEntity.ok(acsComponent.getVisitsByUserIdPaginated(user.getId(), pageable).map(VisitMapper::convertToDTO));
}
@GetMapping("visits/code/{codeId}")
public ResponseEntity<Page<VisitDTO>> getUserVisitsPaginated(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@PathVariable Long codeId
) {
Pageable pageable = PageRequest.of(page, size);
if (!acsComponent.isCodeIdValid(codeId)) throw new NotFoundHTTPException("Invalid code id");
return ResponseEntity.ok(acsComponent.getVisitsByCodeIdPaginated(codeId, pageable).map(VisitMapper::convertToDTO));
}
}

View File

@ -3,6 +3,6 @@ package com.displaynone.acss.controllers.acs;
import lombok.Data;
@Data
public class OpenRB {
public class OpenRQB {
private Long code;
}

View File

@ -7,7 +7,6 @@ import com.displaynone.acss.exception.generics.BadRequestHTTPException;
import com.displaynone.acss.exception.generics.NotFoundHTTPException;
import com.displaynone.acss.exception.generics.UnauthorizedHTTPException;
import com.displaynone.acss.utils.Pair;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@ -22,14 +21,16 @@ public class AuthController {
private final AuthComponent authComponent;
@PostMapping("/login")
public ResponseEntity<AuthTokenPair> login(@RequestBody LoginRB body) {
public ResponseEntity<AuthTokenPair> login(@RequestBody LoginRQB body) {
String login = body.getLogin();
if (login == null) throw new BadRequestHTTPException("Login not specified");
String password = body.getPassword();
if (login == null | password == null) throw new BadRequestHTTPException("Auth credentials not specified");
Pair<UserModel, AuthTokenPair> authInfo;
try {
authInfo = authComponent.authenticate(login);
authInfo = authComponent.authenticate(login, password);
} catch (NotFoundHTTPException e) {
throw new UnauthorizedHTTPException(e.getMessage());
}
@ -38,7 +39,7 @@ public class AuthController {
}
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestBody TokenRefreshRB body) {
public ResponseEntity<?> refreshToken(@RequestBody TokenRefreshRQB body) {
String requestRefreshToken = body.getRefreshToken();
if (requestRefreshToken == null) throw new BadRequestHTTPException("Refresh token not specified");

View File

@ -1,8 +0,0 @@
package com.displaynone.acss.controllers.auth;
import lombok.Data;
@Data
public class LoginRB {
private String login;
}

View File

@ -0,0 +1,9 @@
package com.displaynone.acss.controllers.auth;
import lombok.Data;
@Data
public class LoginRQB {
private final String login;
private final String password;
}

View File

@ -3,6 +3,6 @@ package com.displaynone.acss.controllers.auth;
import lombok.Data;
@Data
public class TokenRefreshRB {
public class TokenRefreshRQB {
public String refreshToken;
}

View File

@ -1,4 +1,4 @@
server.port=8085
server.port=8086
spring.application.name=ACSS

View File

@ -12,11 +12,12 @@
<column name="login" type="VARCHAR(100)">
<constraints unique="true" nullable="false"/>
</column>
<column name="password" type="VARCHAR(300)">
<constraints nullable="false"/>
</column>
<column name="name" type="VARCHAR(100)"/>
<column name="photo" type="VARCHAR(200)"/>
<column name="position" type="VARCHAR(100)"/>
<column name="last_visit" type="DATETIME"/>
<column name="password" type="VARCHAR(300)"/>
</createTable>
</changeSet>
</databaseChangeLog>

View File

@ -1,11 +1,11 @@
id;login;name;photo;position;last_visit
1;john_doe;John Doe;https://dummyimage.com/600x400/ff0/000;Software Engineer;2025-02-16T09:00:00
2;jane_smith;Jane Smith;https://dummyimage.com/600x400/ff0/000;Product Manager;2025-02-15T10:30:00
3;bob_jones;Bob Jones;https://dummyimage.com/600x400/ff0/000;QA Engineer;2025-02-14T11:45:00
4;alice_johnson;Alice Johnson;https://dummyimage.com/600x400/ff0/000;UX Designer;2025-02-13T08:15:00
5;charlie_brown;Charlie Brown;https://dummyimage.com/600x400/ff0/000;DevOps Engineer;2025-02-12T14:00:00
6;diana_ross;Diana Ross;https://dummyimage.com/600x400/ff0/000;HR Manager;2025-02-11T13:25:00
7;edward_norton;Edward Norton;https://dummyimage.com/600x400/ff0/000;Backend Developer;2025-02-10T16:50:00
8;fiona_green;Fiona Green;https://dummyimage.com/600x400/ff0/000;Frontend Developer;2025-02-09T12:10:00
9;george_hill;George Hill;https://dummyimage.com/600x400/ff0/000;Data Scientist;2025-02-08T09:30:00
10;helen_white;Helen White;https://dummyimage.com/600x400/ff0/000;Project Manager;2025-02-07T17:20:00
id;login;name;photo;position;password
1;user;Default User;https://dummyimage.com/600x400/ff0/000;Software Engineer;$2a$12$CUo06gR1qTWC9JB2o1HnXuTK2U5yLIdCPprVWgY8jdB9l.xhbAoQK
2;admin;Admin User;https://dummyimage.com/600x400/ff0/000;Product Manager;$2a$12$e0djVwP9MBeFd9aZia33W.u1Yiq3gDpVNECXn7I.KgvjPr0eoeWwS
3;bob_jones;Bob Jones;https://dummyimage.com/600x400/ff0/000;QA Engineer;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
4;alice_johnson;Alice Johnson;https://dummyimage.com/600x400/ff0/000;UX Designer;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
5;charlie_brown;Charlie Brown;https://dummyimage.com/600x400/ff0/000;DevOps Engineer;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
6;diana_ross;Diana Ross;https://dummyimage.com/600x400/ff0/000;HR Manager;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
7;edward_norton;Edward Norton;https://dummyimage.com/600x400/ff0/000;Backend Developer;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
8;fiona_green;Fiona Green;https://dummyimage.com/600x400/ff0/000;Frontend Developer;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
9;george_hill;George Hill;https://dummyimage.com/600x400/ff0/000;Data Scientist;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
10;helen_white;Helen White;https://dummyimage.com/600x400/ff0/000;Project Manager;$2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.

1 id login name photo position last_visit password
2 1 john_doe user John Doe Default User https://dummyimage.com/600x400/ff0/000 Software Engineer 2025-02-16T09:00:00 $2a$12$CUo06gR1qTWC9JB2o1HnXuTK2U5yLIdCPprVWgY8jdB9l.xhbAoQK
3 2 jane_smith admin Jane Smith Admin User https://dummyimage.com/600x400/ff0/000 Product Manager 2025-02-15T10:30:00 $2a$12$e0djVwP9MBeFd9aZia33W.u1Yiq3gDpVNECXn7I.KgvjPr0eoeWwS
4 3 bob_jones Bob Jones https://dummyimage.com/600x400/ff0/000 QA Engineer 2025-02-14T11:45:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
5 4 alice_johnson Alice Johnson https://dummyimage.com/600x400/ff0/000 UX Designer 2025-02-13T08:15:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
6 5 charlie_brown Charlie Brown https://dummyimage.com/600x400/ff0/000 DevOps Engineer 2025-02-12T14:00:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
7 6 diana_ross Diana Ross https://dummyimage.com/600x400/ff0/000 HR Manager 2025-02-11T13:25:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
8 7 edward_norton Edward Norton https://dummyimage.com/600x400/ff0/000 Backend Developer 2025-02-10T16:50:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
9 8 fiona_green Fiona Green https://dummyimage.com/600x400/ff0/000 Frontend Developer 2025-02-09T12:10:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
10 9 george_hill George Hill https://dummyimage.com/600x400/ff0/000 Data Scientist 2025-02-08T09:30:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.
11 10 helen_white Helen White https://dummyimage.com/600x400/ff0/000 Project Manager 2025-02-07T17:20:00 $2a$12$b//P422i15PDoij6g4SOluIUol7X3E0eFttGMGlGCJuBmZFE6nMw.

View File

@ -1,6 +1,6 @@
user_id;role_id
1;2
2;2
2;1
3;2
4;1
5;2

1 user_id role_id
2 1 2
3 2 2 1
4 3 2
5 4 1
6 5 2