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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.soupulsar.modulith.auth.api.controllers;

import com.soupulsar.modulith.auth.application.dto.AuthUserRequest;
import com.soupulsar.modulith.auth.application.dto.CreateUserRequest;
import com.soupulsar.modulith.auth.application.dto.CreateUserResponse;
import com.soupulsar.modulith.auth.application.usecase.AuthenticateUserUseCase;
import com.soupulsar.modulith.auth.application.usecase.RegisterUserUseCase;
import lombok.RequiredArgsConstructor;
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;

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

private final AuthenticateUserUseCase authenticateUserUseCase;
private final RegisterUserUseCase registerUserUseCase;


@PostMapping(value = {"/login", "/signin"})
public ResponseEntity<String> login(@RequestBody AuthUserRequest request) {
String token = authenticateUserUseCase.execute(request);
return ResponseEntity.ok(token);
}

@PostMapping(value = {"/register", "/signup"})
public ResponseEntity<CreateUserResponse> register(@RequestBody CreateUserRequest request) {
var response = registerUserUseCase.execute(request);
return ResponseEntity.ok(response);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.soupulsar.modulith.auth.application.config;

import com.soupulsar.modulith.auth.application.security.PasswordHasher;
import com.soupulsar.modulith.auth.application.usecase.AuthenticateUserUseCase;
import com.soupulsar.modulith.auth.application.usecase.RegisterUserUseCase;
import com.soupulsar.modulith.auth.domain.repository.UserRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AuthUseCaseConfig {

@Bean
public AuthenticateUserUseCase authenticateUserUseCase(UserRepository userRepository, PasswordHasher passwordHasher) {
return new AuthenticateUserUseCase(userRepository, passwordHasher);
}

@Bean
public RegisterUserUseCase registerUserUseCase(UserRepository userRepository, PasswordHasher passwordHasher) {
return new RegisterUserUseCase(userRepository, passwordHasher);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.soupulsar.modulith.auth.application.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record AuthUserRequest(

@Email String email,
@NotBlank String password

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.soupulsar.modulith.auth.application.dto;

import com.soupulsar.modulith.auth.domain.model.enums.UserRole;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import org.hibernate.validator.constraints.br.CPF;

public record CreateUserRequest(

@NotBlank
String name,
@Email
String email,
@CPF
String cpf,
@NotBlank
String telephone,
@NotBlank
String password,
@NotBlank
UserRole role

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.soupulsar.modulith.auth.application.dto;

import java.util.UUID;

public record CreateUserResponse(
UUID userId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.soupulsar.modulith.auth.application.security;

public interface PasswordHasher {

String hash(String password);
boolean matches(String rawPassword, String hashedPassword);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.soupulsar.modulith.auth.application.usecase;

import com.soupulsar.modulith.auth.application.dto.AuthUserRequest;
import com.soupulsar.modulith.auth.application.security.PasswordHasher;
import com.soupulsar.modulith.auth.domain.model.User;
import com.soupulsar.modulith.auth.domain.model.enums.UserStatus;
import com.soupulsar.modulith.auth.domain.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;

@RequiredArgsConstructor
public class AuthenticateUserUseCase {

private final UserRepository userRepository;
private final PasswordHasher passwordHasher;

public String execute(AuthUserRequest request) {

User user = userRepository.findByEmail(request.email())
.orElseThrow(() -> new IllegalArgumentException("Invalid email or password"));

if (user.getStatus() != UserStatus.ACTIVE) {
throw new IllegalArgumentException("User is not active");
}

if (!passwordHasher.matches(request.password(), user.getPasswordHash())) {
throw new IllegalArgumentException("Invalid email or password");
}

return "JWT-TOKEN"; // Placeholder for actual JWT generation logic

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.soupulsar.modulith.auth.application.usecase;

import com.soupulsar.modulith.auth.application.dto.CreateUserRequest;
import com.soupulsar.modulith.auth.application.dto.CreateUserResponse;
import com.soupulsar.modulith.auth.application.security.PasswordHasher;
import com.soupulsar.modulith.auth.domain.model.User;
import com.soupulsar.modulith.auth.domain.repository.UserRepository;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class RegisterUserUseCase {

private final UserRepository userRepository;
private final PasswordHasher passwordEncoder;

public CreateUserResponse execute (CreateUserRequest userRequest) {
if (userRepository.existsByEmail(userRequest.email())) {
throw new IllegalArgumentException("Email already in use");
}

User user = User.create(
userRequest.name(),
userRequest.cpf(),
userRequest.telephone(),
userRequest.email(),
passwordEncoder.hash(userRequest.password()),
userRequest.role()
);

userRepository.save(user);

return new CreateUserResponse(user.getUserId());

}
}
56 changes: 56 additions & 0 deletions src/main/java/com/soupulsar/modulith/auth/domain/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.soupulsar.modulith.auth.domain.model;

import com.soupulsar.modulith.auth.domain.model.enums.UserRole;
import com.soupulsar.modulith.auth.domain.model.enums.UserStatus;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.UUID;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
public class User {

private final UUID userId;
private final String name;
private final String cpf;
private String telephone;
private String email;
private String passwordHash;
private final UserRole role;
private UserStatus status;


public static User create(String name, String cpf, String telephone, String email, String passwordHash, UserRole role) {
if (name == null || name.isBlank()) throw new IllegalArgumentException("Name cannot be null or blank");
if (cpf == null || cpf.isBlank()) throw new IllegalArgumentException("CPF cannot be null or blank");
if (telephone == null || telephone.isBlank()) throw new IllegalArgumentException("Telephone cannot be null or blank");
if (email == null || email.isBlank()) throw new IllegalArgumentException("Email cannot be null or blank");
if (passwordHash == null || passwordHash.isBlank()) throw new IllegalArgumentException("Password cannot be null or blank");
return new User(UUID.randomUUID(), name, cpf, telephone, email, passwordHash, role, UserStatus.ACTIVE);
}

public static User restore(UUID userId, String name, String cpf, String telephone, String email, String passwordHash, UserRole role, UserStatus status) {
return new User(userId, name, cpf, telephone, email, passwordHash, role, status);
}


public void activate() {
if (this.status == UserStatus.ACTIVE) {
throw new IllegalStateException("User is already active.");
}
this.status = UserStatus.ACTIVE;
}

public void deactivate() {
if (this.status == UserStatus.INACTIVE) {
throw new IllegalStateException("User is already inactive.");
}
this.status = UserStatus.INACTIVE;
}




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.soupulsar.modulith.auth.domain.model.enums;

public enum UserRole {

CLIENT,
SPECIALIST,
ADMIN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.soupulsar.modulith.auth.domain.model.enums;

public enum UserStatus {
ACTIVE,
INACTIVE,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.soupulsar.modulith.auth.domain.repository;

import com.soupulsar.modulith.auth.domain.model.User;

import java.util.Optional;
import java.util.UUID;

public interface UserRepository {

User save(User user);
Optional<User> findByEmail(String email);
Optional<User> findById(UUID userId);
boolean existsByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.soupulsar.modulith.auth.infrastructure.persistence.entity;

import com.soupulsar.modulith.auth.domain.model.enums.UserRole;
import com.soupulsar.modulith.auth.domain.model.enums.UserStatus;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.UUID;

@Entity
@Table(name = "users")
@Getter
@Setter
public class UserEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private UUID userId;

@Column(nullable = false)
private String name;

@Column(nullable = false)
private String cpf;

@Column(nullable = false)
private String email;

@Column(nullable = false)
private String password;

@Column(nullable = false)
private String telephone;

@Column(nullable = false)
private UserRole role;

@Column(nullable = false)
private UserStatus status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.soupulsar.modulith.auth.infrastructure.persistence.mapper;

import com.soupulsar.modulith.auth.domain.model.User;
import com.soupulsar.modulith.auth.infrastructure.persistence.entity.UserEntity;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserMapper {

public static UserEntity toEntity(User user){
UserEntity entity = new UserEntity();
entity.setUserId(user.getUserId());
entity.setEmail(user.getEmail());
entity.setPassword(user.getPasswordHash());
entity.setRole(user.getRole());
return entity;
}

public static User toModel(UserEntity entity){
return User.restore(entity.getUserId(),
entity.getName(),
entity.getEmail(),
entity.getPassword(),
entity.getTelephone(),
entity.getCpf(),
entity.getRole(),
entity.getStatus());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.soupulsar.modulith.auth.infrastructure.persistence.repository;

import com.soupulsar.modulith.auth.infrastructure.persistence.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface UserJpaRepository extends JpaRepository<UserEntity, UUID> {

Optional<UserEntity> findByEmail(String email);
boolean existsByEmail(String email);

}
Loading