create account, login e refresh token
This commit is contained in:
parent
953730fffd
commit
4a2f4371e0
29 changed files with 580 additions and 42 deletions
|
|
@ -39,6 +39,14 @@
|
|||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-jwt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-jwt-build</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-openapi</artifactId>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ public class User {
|
|||
private Long id;
|
||||
private String name;
|
||||
private String email;
|
||||
private String password;
|
||||
private String photo;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
@ -28,4 +30,20 @@ public class User {
|
|||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPhoto() {
|
||||
return photo;
|
||||
}
|
||||
|
||||
public void setPhoto(String photo) {
|
||||
this.photo = photo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,28 @@ public class UserOrganization {
|
|||
private User user;
|
||||
private Organization organization;
|
||||
private UserOrganizationRole role;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Organization getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
||||
public void setOrganization(Organization organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
public UserOrganizationRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(UserOrganizationRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package net.beetechgroup.beetask.frameworks.config;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.enterprise.inject.Produces;
|
||||
import jakarta.inject.Inject;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginUseCase;
|
||||
import net.beetechgroup.beetask.usecase.auth.refresh.RefreshTokenUseCase;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserUseCase;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||
import io.smallrye.jwt.auth.principal.JWTParser;
|
||||
|
||||
@ApplicationScoped
|
||||
public class UserUseCaseConfig {
|
||||
|
||||
@Inject
|
||||
JWTParser jwtParser;
|
||||
|
||||
@Produces
|
||||
public CreateUserUseCase createUserUseCase(UserRepository userRepository) {
|
||||
return new CreateUserUseCase(userRepository);
|
||||
}
|
||||
|
||||
@Produces
|
||||
public LoginUseCase loginUseCase(UserRepository userRepository) {
|
||||
return new LoginUseCase(userRepository);
|
||||
}
|
||||
|
||||
@Produces
|
||||
public RefreshTokenUseCase refreshTokenUseCase(UserRepository userRepository) {
|
||||
return new RefreshTokenUseCase(userRepository, jwtParser);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ public class UserEntity {
|
|||
private Long id;
|
||||
private String name;
|
||||
private String email;
|
||||
private String password;
|
||||
private String photo;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
@ -38,4 +40,20 @@ public class UserEntity {
|
|||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPhoto() {
|
||||
return photo;
|
||||
}
|
||||
|
||||
public void setPhoto(String photo) {
|
||||
this.photo = photo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package net.beetechgroup.beetask.frameworks.persistence.mapper;
|
||||
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserEntity;
|
||||
|
||||
public class UserEntityMapper {
|
||||
|
||||
public static UserEntity toEntity(User user) {
|
||||
if (user == null) return null;
|
||||
UserEntity entity = new UserEntity();
|
||||
entity.setId(user.getId());
|
||||
entity.setName(user.getName());
|
||||
entity.setEmail(user.getEmail());
|
||||
entity.setPassword(user.getPassword());
|
||||
entity.setPhoto(user.getPhoto());
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static User toDomain(UserEntity entity) {
|
||||
if (entity == null) return null;
|
||||
User user = new User();
|
||||
user.setId(entity.getId());
|
||||
user.setName(entity.getName());
|
||||
user.setEmail(entity.getEmail());
|
||||
user.setPassword(entity.getPassword());
|
||||
user.setPhoto(entity.getPhoto());
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package net.beetechgroup.beetask.frameworks.persistence.mapper;
|
||||
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity;
|
||||
|
||||
public class UserOrganizationEntityMapper {
|
||||
|
||||
public static UserOrganization toDomain(UserOrganizationEntity entity) {
|
||||
if (entity == null) return null;
|
||||
UserOrganization domain = new UserOrganization();
|
||||
domain.setUser(UserEntityMapper.toDomain(entity.getUser()));
|
||||
domain.setOrganization(OrganizationEntityMapper.toDomain(entity.getOrganization()));
|
||||
domain.setRole(entity.getRole());
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package net.beetechgroup.beetask.frameworks.persistence.repository;
|
||||
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApplicationScoped
|
||||
public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository<UserOrganizationEntity> {
|
||||
|
||||
@Override
|
||||
public List<UserOrganization> findByUserId(Long userId) {
|
||||
return find("user.id", userId).stream()
|
||||
.map(UserOrganizationEntityMapper::toDomain)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package net.beetechgroup.beetask.frameworks.persistence.repository;
|
||||
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserEntity;
|
||||
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserEntityMapper;
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ApplicationScoped
|
||||
public class UserRepositoryImpl implements UserRepository, PanacheRepository<UserEntity> {
|
||||
|
||||
@Inject
|
||||
UserOrganizationRepository userOrganizationRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public User save(User user) {
|
||||
UserEntity entity = UserEntityMapper.toEntity(user);
|
||||
if (entity.getId() == null) {
|
||||
persist(entity);
|
||||
} else {
|
||||
entity = getEntityManager().merge(entity);
|
||||
}
|
||||
return UserEntityMapper.toDomain(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findByEmail(String email) {
|
||||
return find("email", email).firstResultOptional()
|
||||
.map(UserEntityMapper::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findUserById(Long id) {
|
||||
return findByIdOptional(id)
|
||||
.map(UserEntityMapper::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserOrganization> findUserOrganizations(Long userId) {
|
||||
return userOrganizationRepository.findByUserId(userId);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,50 +9,58 @@ import jakarta.ws.rs.core.MediaType;
|
|||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginInput;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginOutput;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginUseCase;
|
||||
import net.beetechgroup.beetask.usecase.auth.refresh.RefreshTokenInput;
|
||||
import net.beetechgroup.beetask.usecase.auth.refresh.RefreshTokenUseCase;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserInput;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserOutput;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserUseCase;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Path("/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Authentication", description = "Authentication operations (Mocked)")
|
||||
@Tag(name = "Authentication", description = "Authentication operations")
|
||||
public class AuthController {
|
||||
|
||||
@Inject
|
||||
CreateUserUseCase createUserUseCase;
|
||||
|
||||
@Inject
|
||||
LoginUseCase loginUseCase;
|
||||
|
||||
@Inject
|
||||
RefreshTokenUseCase refreshTokenUseCase;
|
||||
|
||||
@Path("/register")
|
||||
@POST
|
||||
@Operation(summary = "Register", description = "Creates a new user account")
|
||||
public CreateAccountResponse register(CreateAccountRequest request) {
|
||||
CreateUserInput input = AuthControllerMapper.toCreateUserInput(request);
|
||||
CreateUserOutput output = createUserUseCase.execute(input);
|
||||
return AuthControllerMapper.toCreateAccountResponse(output);
|
||||
}
|
||||
|
||||
@Path("/login")
|
||||
@POST
|
||||
@Operation(summary = "Login", description = "Mocked login that returns user data")
|
||||
public LoginResponse login() {
|
||||
return new LoginResponse(
|
||||
"Gabriel Menezes",
|
||||
"gabriel@beetech.net",
|
||||
"https://ui-avatars.com/api/?name=Gabriel+Menezes&background=random",
|
||||
"mocked-jwt-token-abcd-1234",
|
||||
"mocked-refresh-token-wxyz-5678",
|
||||
3600L, // 1 hour
|
||||
LocalDateTime.now().toString(),
|
||||
List.of(
|
||||
new LoginResponse.OrganizationDTO("BeeTech Group", List.of("ADMIN", "OWNER")),
|
||||
new LoginResponse.OrganizationDTO("SkyMob Dev", List.of("DEVELOPER"))
|
||||
)
|
||||
);
|
||||
@Operation(summary = "Login", description = "Login that returns real user data and JWT")
|
||||
public LoginResponse login(LoginRequest request) {
|
||||
LoginInput input = AuthControllerMapper.toLoginInput(request);
|
||||
LoginOutput output = loginUseCase.execute(input);
|
||||
return AuthControllerMapper.toLoginResponse(output);
|
||||
}
|
||||
|
||||
@Path("/refresh")
|
||||
@POST
|
||||
@Operation(summary = "Refresh token", description = "Mocked token refresh")
|
||||
public LoginResponse refresh() {
|
||||
return new LoginResponse(
|
||||
"Gabriel Menezes",
|
||||
"gabriel@beetech.net",
|
||||
"https://ui-avatars.com/api/?name=Gabriel+Menezes&background=random",
|
||||
"refreshed-jwt-token-" + System.currentTimeMillis(),
|
||||
"refreshed-refresh-token-" + System.currentTimeMillis(),
|
||||
3600L,
|
||||
LocalDateTime.now().toString(),
|
||||
List.of(
|
||||
new LoginResponse.OrganizationDTO("BeeTech Group", List.of("ADMIN", "OWNER")),
|
||||
new LoginResponse.OrganizationDTO("SkyMob Dev", List.of("DEVELOPER"))
|
||||
)
|
||||
);
|
||||
@Operation(summary = "Refresh token", description = "Token refresh using refresh token")
|
||||
public LoginResponse refresh(RefreshTokenRequest request) {
|
||||
RefreshTokenInput input = AuthControllerMapper.toRefreshTokenInput(request);
|
||||
LoginOutput output = refreshTokenUseCase.execute(input);
|
||||
return AuthControllerMapper.toLoginResponse(output);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.auth;
|
||||
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginInput;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginOutput;
|
||||
import net.beetechgroup.beetask.usecase.auth.refresh.RefreshTokenInput;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserInput;
|
||||
import net.beetechgroup.beetask.usecase.user.create.CreateUserOutput;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AuthControllerMapper {
|
||||
|
||||
public static CreateUserInput toCreateUserInput(CreateAccountRequest request) {
|
||||
return new CreateUserInput(request.name(), request.email(), request.password());
|
||||
}
|
||||
|
||||
public static CreateAccountResponse toCreateAccountResponse(CreateUserOutput output) {
|
||||
return new CreateAccountResponse(output.id(), output.name(), output.email());
|
||||
}
|
||||
|
||||
public static LoginInput toLoginInput(LoginRequest request) {
|
||||
return new LoginInput(request.email(), request.password());
|
||||
}
|
||||
|
||||
public static RefreshTokenInput toRefreshTokenInput(RefreshTokenRequest request) {
|
||||
return new RefreshTokenInput(request.refreshToken());
|
||||
}
|
||||
|
||||
public static LoginResponse toLoginResponse(LoginOutput output) {
|
||||
return new LoginResponse(
|
||||
output.name(),
|
||||
output.email(),
|
||||
output.photo(),
|
||||
output.jwt(),
|
||||
output.refreshToken(),
|
||||
output.expiresIn(),
|
||||
output.issuedAt(),
|
||||
output.organizations().stream()
|
||||
.map(org -> new LoginResponse.Organization(org.name(), org.roles()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.auth;
|
||||
|
||||
public record CreateAccountRequest(String name, String email, String password) {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.auth;
|
||||
|
||||
public record CreateAccountResponse(Long id, String name, String email) {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.auth;
|
||||
|
||||
public record LoginRequest(String email, String password) {
|
||||
}
|
||||
|
|
@ -10,9 +10,9 @@ public record LoginResponse(
|
|||
String refreshToken,
|
||||
Long expiresIn,
|
||||
String issuedAt,
|
||||
List<OrganizationDTO> organizations
|
||||
List<Organization> organizations
|
||||
) {
|
||||
public record OrganizationDTO(
|
||||
public record Organization(
|
||||
String name,
|
||||
List<String> roles
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.auth;
|
||||
|
||||
public record RefreshTokenRequest(String refreshToken) {
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.task;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import net.beetechgroup.beetask.entities.task.TaskStatus;
|
||||
|
|
@ -12,3 +13,8 @@ public record CreateTaskResponse(
|
|||
String project,
|
||||
List<TaskHistoryItemResponse> history) {
|
||||
}
|
||||
|
||||
record TaskHistoryItemResponse(
|
||||
LocalDateTime startAt,
|
||||
LocalDateTime endAt) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package net.beetechgroup.beetask.interfaceadapters.controllers.task;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record TaskHistoryItemResponse(
|
||||
LocalDateTime startAt,
|
||||
LocalDateTime endAt) {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.usecase.auth.login;
|
||||
|
||||
public record LoginInput(String email, String password) {
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package net.beetechgroup.beetask.usecase.auth.login;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record LoginOutput(
|
||||
String name,
|
||||
String email,
|
||||
String photo,
|
||||
String jwt,
|
||||
String refreshToken,
|
||||
Long expiresIn,
|
||||
String issuedAt,
|
||||
List<OrganizationOutput> organizations
|
||||
) {
|
||||
public record OrganizationOutput(
|
||||
String name,
|
||||
List<String> roles
|
||||
) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package net.beetechgroup.beetask.usecase.auth.login;
|
||||
|
||||
import io.smallrye.jwt.build.Jwt;
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LoginUseCase {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public LoginUseCase(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public LoginOutput execute(LoginInput input) {
|
||||
Optional<User> userOptional = userRepository.findByEmail(input.email());
|
||||
|
||||
if (userOptional.isEmpty()) {
|
||||
throw new RuntimeException("Invalid credentials");
|
||||
}
|
||||
|
||||
User user = userOptional.get();
|
||||
|
||||
// Simple password check for now (should use Bcrypt)
|
||||
if (!user.getPassword().equals(input.password())) {
|
||||
throw new RuntimeException("Invalid credentials");
|
||||
}
|
||||
|
||||
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
|
||||
|
||||
Set<String> groups = new HashSet<>();
|
||||
userOrganizations.forEach(uo -> groups.add(uo.getRole().name()));
|
||||
|
||||
String token = Jwt.issuer("https://beetech.net")
|
||||
.upn(user.getEmail())
|
||||
.groups(groups)
|
||||
.claim("name", user.getName())
|
||||
.expiresIn(3600)
|
||||
.sign();
|
||||
|
||||
List<LoginOutput.OrganizationOutput> orgs = userOrganizations.stream()
|
||||
.collect(Collectors.groupingBy(uo -> uo.getOrganization().getName()))
|
||||
.entrySet().stream()
|
||||
.map(entry -> new LoginOutput.OrganizationOutput(
|
||||
entry.getKey(),
|
||||
entry.getValue().stream().map(uo -> uo.getRole().name()).collect(Collectors.toList())
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
String refreshToken = Jwt.issuer("https://beetech.net")
|
||||
.upn(user.getEmail())
|
||||
.expiresIn(86400) // 24 hours
|
||||
.sign();
|
||||
|
||||
return new LoginOutput(
|
||||
user.getName(),
|
||||
user.getEmail(),
|
||||
user.getPhoto() != null ? user.getPhoto() : "https://ui-avatars.com/api/?name=" + user.getName().replace(" ", "+") + "&background=random",
|
||||
token,
|
||||
refreshToken,
|
||||
3600L,
|
||||
LocalDateTime.now().toString(),
|
||||
orgs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.usecase.auth.refresh;
|
||||
|
||||
public record RefreshTokenInput(String refreshToken) {
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package net.beetechgroup.beetask.usecase.auth.refresh;
|
||||
|
||||
import io.smallrye.jwt.auth.principal.JWTParser;
|
||||
import io.smallrye.jwt.auth.principal.ParseException;
|
||||
import io.smallrye.jwt.build.Jwt;
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import net.beetechgroup.beetask.usecase.auth.login.LoginOutput;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RefreshTokenUseCase {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final JWTParser jwtParser;
|
||||
|
||||
public RefreshTokenUseCase(UserRepository userRepository, JWTParser jwtParser) {
|
||||
this.userRepository = userRepository;
|
||||
this.jwtParser = jwtParser;
|
||||
}
|
||||
|
||||
public LoginOutput execute(RefreshTokenInput input) {
|
||||
try {
|
||||
JsonWebToken token = jwtParser.parse(input.refreshToken());
|
||||
String email = token.getSubject(); // Or token.getName() / token.getClaim(Claims.upn.name())
|
||||
|
||||
if (email == null) {
|
||||
throw new RuntimeException("Invalid refresh token: email not found");
|
||||
}
|
||||
|
||||
Optional<User> userOptional = userRepository.findByEmail(email);
|
||||
if (userOptional.isEmpty()) {
|
||||
throw new RuntimeException("User not found for the provided refresh token");
|
||||
}
|
||||
|
||||
User user = userOptional.get();
|
||||
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
|
||||
|
||||
Set<String> groups = new HashSet<>();
|
||||
userOrganizations.forEach(uo -> groups.add(uo.getRole().name()));
|
||||
|
||||
String newToken = Jwt.issuer("https://beetech.net")
|
||||
.upn(user.getEmail())
|
||||
.groups(groups)
|
||||
.claim("name", user.getName())
|
||||
.expiresIn(3600)
|
||||
.sign();
|
||||
|
||||
// Generate a new refresh token (also a JWT but with longer expiration)
|
||||
String newRefreshToken = Jwt.issuer("https://beetech.net")
|
||||
.upn(user.getEmail())
|
||||
.expiresIn(86400) // 24 hours
|
||||
.sign();
|
||||
|
||||
List<LoginOutput.OrganizationOutput> orgs = userOrganizations.stream()
|
||||
.collect(Collectors.groupingBy(uo -> uo.getOrganization().getName()))
|
||||
.entrySet().stream()
|
||||
.map(entry -> new LoginOutput.OrganizationOutput(
|
||||
entry.getKey(),
|
||||
entry.getValue().stream().map(uo -> uo.getRole().name()).collect(Collectors.toList())
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new LoginOutput(
|
||||
user.getName(),
|
||||
user.getEmail(),
|
||||
user.getPhoto() != null ? user.getPhoto() : "https://ui-avatars.com/api/?name=" + user.getName().replace(" ", "+") + "&background=random",
|
||||
newToken,
|
||||
newRefreshToken,
|
||||
3600L,
|
||||
LocalDateTime.now().toString(),
|
||||
orgs
|
||||
);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException("Invalid refresh token: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package net.beetechgroup.beetask.usecase.repository;
|
||||
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserOrganizationRepository {
|
||||
List<UserOrganization> findByUserId(Long userId);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package net.beetechgroup.beetask.usecase.repository;
|
||||
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||
|
||||
public interface UserRepository {
|
||||
User save(User user);
|
||||
Optional<User> findByEmail(String email);
|
||||
Optional<User> findUserById(Long id);
|
||||
List<UserOrganization> findUserOrganizations(Long userId);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.usecase.user.create;
|
||||
|
||||
public record CreateUserInput(String name, String email, String password) {
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package net.beetechgroup.beetask.usecase.user.create;
|
||||
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
|
||||
public class CreateUserMapper {
|
||||
|
||||
public static User toUser(CreateUserInput input) {
|
||||
User user = new User();
|
||||
user.setName(input.name());
|
||||
user.setEmail(input.email());
|
||||
user.setPassword(input.password());
|
||||
return user;
|
||||
}
|
||||
|
||||
public static CreateUserOutput toCreateUserOutput(User user) {
|
||||
return new CreateUserOutput(user.getId(), user.getName(), user.getEmail());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package net.beetechgroup.beetask.usecase.user.create;
|
||||
|
||||
public record CreateUserOutput(Long id, String name, String email) {
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package net.beetechgroup.beetask.usecase.user.create;
|
||||
|
||||
import net.beetechgroup.beetask.entities.User;
|
||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||
|
||||
public class CreateUserUseCase {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public CreateUserUseCase(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public CreateUserOutput execute(CreateUserInput input) {
|
||||
if (userRepository.findByEmail(input.email()).isPresent()) {
|
||||
throw new RuntimeException("User already exists with this email");
|
||||
}
|
||||
|
||||
User user = CreateUserMapper.toUser(input);
|
||||
User savedUser = userRepository.save(user);
|
||||
|
||||
return CreateUserMapper.toCreateUserOutput(savedUser);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue