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>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-rest-jackson</artifactId>
|
<artifactId>quarkus-rest-jackson</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-smallrye-openapi</artifactId>
|
<artifactId>quarkus-smallrye-openapi</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ public class User {
|
||||||
private Long id;
|
private Long id;
|
||||||
private String name;
|
private String name;
|
||||||
private String email;
|
private String email;
|
||||||
|
private String password;
|
||||||
|
private String photo;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -28,4 +30,20 @@ public class User {
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
this.email = 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 User user;
|
||||||
private Organization organization;
|
private Organization organization;
|
||||||
private UserOrganizationRole role;
|
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 Long id;
|
||||||
private String name;
|
private String name;
|
||||||
private String email;
|
private String email;
|
||||||
|
private String password;
|
||||||
|
private String photo;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -38,4 +40,20 @@ public class UserEntity {
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
this.email = 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.Operation;
|
||||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
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.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Path("/auth")
|
@Path("/auth")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Tag(name = "Authentication", description = "Authentication operations (Mocked)")
|
@Tag(name = "Authentication", description = "Authentication operations")
|
||||||
public class AuthController {
|
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")
|
@Path("/login")
|
||||||
@POST
|
@POST
|
||||||
@Operation(summary = "Login", description = "Mocked login that returns user data")
|
@Operation(summary = "Login", description = "Login that returns real user data and JWT")
|
||||||
public LoginResponse login() {
|
public LoginResponse login(LoginRequest request) {
|
||||||
return new LoginResponse(
|
LoginInput input = AuthControllerMapper.toLoginInput(request);
|
||||||
"Gabriel Menezes",
|
LoginOutput output = loginUseCase.execute(input);
|
||||||
"gabriel@beetech.net",
|
return AuthControllerMapper.toLoginResponse(output);
|
||||||
"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"))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/refresh")
|
@Path("/refresh")
|
||||||
@POST
|
@POST
|
||||||
@Operation(summary = "Refresh token", description = "Mocked token refresh")
|
@Operation(summary = "Refresh token", description = "Token refresh using refresh token")
|
||||||
public LoginResponse refresh() {
|
public LoginResponse refresh(RefreshTokenRequest request) {
|
||||||
return new LoginResponse(
|
RefreshTokenInput input = AuthControllerMapper.toRefreshTokenInput(request);
|
||||||
"Gabriel Menezes",
|
LoginOutput output = refreshTokenUseCase.execute(input);
|
||||||
"gabriel@beetech.net",
|
return AuthControllerMapper.toLoginResponse(output);
|
||||||
"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"))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
String refreshToken,
|
||||||
Long expiresIn,
|
Long expiresIn,
|
||||||
String issuedAt,
|
String issuedAt,
|
||||||
List<OrganizationDTO> organizations
|
List<Organization> organizations
|
||||||
) {
|
) {
|
||||||
public record OrganizationDTO(
|
public record Organization(
|
||||||
String name,
|
String name,
|
||||||
List<String> roles
|
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;
|
package net.beetechgroup.beetask.interfaceadapters.controllers.task;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.beetechgroup.beetask.entities.task.TaskStatus;
|
import net.beetechgroup.beetask.entities.task.TaskStatus;
|
||||||
|
|
@ -12,3 +13,8 @@ public record CreateTaskResponse(
|
||||||
String project,
|
String project,
|
||||||
List<TaskHistoryItemResponse> history) {
|
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