Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/org/example/crm/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class SecurityConfig {
"/api/v1/auth/login",
"/api/v1/organization/name",
"/api/v1/auth/refresh-token",
"/api/v1/auth/select-organization",
"/v3/api-docs/**",
"/v3/api-docs",
"/swagger-ui/**",
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/example/crm/controller/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ public ResponseEntity<LoginResponse> login(
return ResponseEntity.ok(loginResponseResponseEntity);
}

@PostMapping("/select-organization")
public ResponseEntity<LoginResponse> selectOrganization(
@RequestParam String organizationId,
@RequestBody LoginRequest request,
HttpServletResponse response
) {
LoginResponse loginResponseResponseEntity = authService.selectOrganization(organizationId, request,response);
return ResponseEntity.ok(loginResponseResponseEntity);
}

@PostMapping("/refresh-token")
public ResponseEntity<LoginResponse> refreshToken(HttpServletRequest request,
HttpServletResponse response) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/example/crm/entity/dto/IdNameDto.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.example.crm.entity.dto;

import lombok.Builder;

@Builder
public record IdNameDto(
String id,
String name
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/org/example/crm/entity/login/LoginRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,4 @@ public class LoginRequest {

@NotBlank(message = "Password is required")
private String password;

@NotBlank(message = "Organization ID is required")
private String organizationId;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.example.crm.entity.login;

import lombok.*;
import org.example.crm.entity.dto.IdNameDto;

import java.util.List;

@Getter
@Setter
Expand All @@ -9,5 +12,7 @@
@Builder
public class LoginResponse {
private String token;
private long expiry;
private Long expiry;
private boolean requiresOrganizationSelection;
private List<IdNameDto> organizations;
}
2 changes: 1 addition & 1 deletion src/main/java/org/example/crm/entity/model/Student.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class Student extends BaseEntity {
private BigDecimal balance;

@ManyToOne(optional = false, cascade = CascadeType.PERSIST)
@JoinColumn(name = "user_id", referencedColumnName = "id", unique = true)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;

}
15 changes: 9 additions & 6 deletions src/main/java/org/example/crm/initializer/DataInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

@Component
@RequiredArgsConstructor
//implements CommandLineRunner
public class DataInitializer {
//
public class DataInitializer implements CommandLineRunner {

final UserRepository userRepository;
final TeacherRepository teacherRepository;
Expand All @@ -34,15 +34,18 @@ public class DataInitializer {
final BranchRepository branchRepository;
final EntityManager entityManager;
Organization organization = new Organization("org", "phone", "email", "website");
Organization organization1 = new Organization("org1", "phone1", "email1", "website1");


// @Override

@Override
@Transactional
public void run(String... args) {

// if (userRepository.count() > 0) return;

organizationRepository.save(organization);
organizationRepository.save(organization1);
String encodedPassword = passwordEncoder.encode("root1234");


Expand Down Expand Up @@ -187,9 +190,9 @@ public void run(String... args) {
studentRepository.save(student1);

Student student2 = new Student();
student2.setOrganizationId(organization.getId());
student2.setUser(studentUser2);
student2.setParentPhone("+998901234581");
student2.setOrganizationId(organization1.getId());
student2.setUser(studentUser1);
student2.setParentPhone("+998901234580");
studentRepository.save(student2);

Student student3 = new Student();
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/org/example/crm/repository/StudentRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,22 @@ AnalyticStudentProjection getAnalyticStudent(String organizationId,

@Query("select s.organizationId from Student s where s.user.id=:userId and s.organizationId=:orgId")
Optional<String> findOrgId(@Param("userId") String id, @Param("orgId") String organizationId);


@Query("""
select count(s.id)
from Student s
where s.user.id=:userId
""")
Long countStudentsByUser_Id(String id);


@Query("""
select s
from Student s
where s.user.id=:userId
""")
List<Student> findAllByUserId(String userId);

Optional<Student> findStudentByOrganizationIdAndUserId(String organizationId, String userId);
}
77 changes: 66 additions & 11 deletions src/main/java/org/example/crm/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.crm.config.JwtUtils;
import org.example.crm.entity.dto.IdNameDto;
import org.example.crm.entity.dto.user.UserDto;
import org.example.crm.entity.enums.Role;
import org.example.crm.entity.login.LoginRequest;
import org.example.crm.entity.login.LoginResponse;
import org.example.crm.entity.login.TokenDto;
import org.example.crm.entity.model.Student;
import org.example.crm.entity.model.User;
import org.example.crm.entity.request.ChangePasswordRequest;
import org.example.crm.exceptions.ErrorCodes;
import org.example.crm.exceptions.ErrorType;
import org.example.crm.exceptions.RestException;
import org.example.crm.mapper.UserMapper;
import org.example.crm.repository.OrganizationRepository;
import org.example.crm.repository.StudentRepository;
import org.example.crm.repository.UserRepository;
import org.example.crm.validator.UserValidator;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -30,6 +33,7 @@
import org.springframework.validation.annotation.Validated;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Slf4j
Expand All @@ -44,6 +48,7 @@ public class AuthService {
private final UserMapper userMapper;
private final UserValidator userValidator;
final OrganizationRepository organizationRepository;
final StudentRepository studentRepository;

@Value("${jwt.refresh.token.expire.date:86400}")
private Long refreshTokenExpiration;
Expand All @@ -61,19 +66,35 @@ public LoginResponse getLoginResponseResponseEntity(@Valid LoginRequest request,
throw new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest);
}

boolean b = organizationRepository.existsById(request.getOrganizationId());
if (!b) {
throw new RestException(ErrorType.ORGANIZATION_NOT_FOUND, ErrorCodes.NotFound);
List<Student> studentList = studentRepository.findAllByUserId(user.getId());

if (user.getRole().equals(Role.STUDENT)&& !studentList.isEmpty()) {
if (studentList.size() > 1) {
List<String> orgId = studentList.stream().map(Student::getOrganizationId).toList();
List<IdNameDto> idNameDtos = organizationRepository.findAllById(orgId).stream().map(
organization -> IdNameDto.builder()
.id(organization.getId())
.name(organization.getName())
.build()).toList();

return LoginResponse.builder()
.token(null)
.expiry(null)
.requiresOrganizationSelection(true)
.organizations(idNameDtos)
.build();
}

Student student = studentList.get(0);
if (!student.getOrganizationId().equals(user.getOrganizationId())) {
throw new RestException(ErrorType.WRONG_ORGANIZATION, ErrorCodes.Forbidden);
}

}
Map<String, Object> claims = jwtUtils.prepareClaims(user, request.getOrganizationId());
TokenDto accessToken = jwtUtils.generateToken(user.getPhone(), claims, "access");
TokenDto refreshToken = jwtUtils.generateToken(user.getPhone(), claims, "refresh");
setRefreshCookie(response, refreshToken.getToken());

return LoginResponse.builder()
.token(accessToken.getToken())
.expiry(accessToken.getExpiry())
.build();
return getLoginResponse(user.getOrganizationId(), response, user);


}


Expand Down Expand Up @@ -142,4 +163,38 @@ public UserDto getMe() {
User user = userValidator.authenticateAndGetUser();
return userMapper.toDto(user);
}

public LoginResponse selectOrganization(String organizationId, LoginRequest request, HttpServletResponse response) {
User user = userRepository.findByPhoneAndDeletedFalse(request.getPhone())
.orElseThrow(() -> new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest));

if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest);
}

boolean b = organizationRepository.existsById(organizationId);
if (!b) {
throw new RestException(ErrorType.ORGANIZATION_NOT_FOUND, ErrorCodes.NotFound);
}
studentRepository.findStudentByOrganizationIdAndUserId(organizationId, user.getId())
.orElseThrow(() -> new RestException(ErrorType.STUDENT_NOT_FOUND, ErrorCodes.NotFound));


return getLoginResponse(organizationId, response, user);
}

private LoginResponse getLoginResponse(String organizationId, HttpServletResponse response, User user) {
Map<String, Object> claims = jwtUtils.prepareClaims(user, organizationId);
TokenDto accessToken = jwtUtils.generateToken(user.getPhone(), claims, "access");
TokenDto refreshToken = jwtUtils.generateToken(user.getPhone(), claims, "refresh");
setRefreshCookie(response, refreshToken.getToken());


return LoginResponse.builder()
.token(accessToken.getToken())
.expiry(accessToken.getExpiry())
.requiresOrganizationSelection(false)
.organizations(null)
.build();
}
}