diff --git a/cadastral/pom.xml b/cadastral/pom.xml
index c5468ce4..b31f3daf 100644
--- a/cadastral/pom.xml
+++ b/cadastral/pom.xml
@@ -107,6 +107,25 @@
spring-cloud-starter-config
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.1
+
+
+
+ org.mapstruct
+ mapstruct
+ 1.5.5.Final
+
+
@@ -133,6 +152,11 @@
lombok
${lombok.version}
+
+ org.mapstruct
+ mapstruct-processor
+ 1.5.5.Final
+
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java
index b5772e33..1523c904 100644
--- a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java
+++ b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java
@@ -6,6 +6,7 @@
import org.bson.Document;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.List;
@@ -53,16 +54,32 @@ public void run(String... args) throws Exception {
if (userCount == 0) {
System.out.println("Usuários não encontrados, criando inicialização...");
+ Document roleAdmin = roleCollection.find(new Document("roleName", "ROLE_ADMINISTRATOR")).first();
+
+ if (roleAdmin == null) {
+ throw new IllegalStateException("Role 'ROLE_ADMINISTRATOR' não encontrada no banco!");
+ }
+
userCollection.insertMany(List.of(
new Document("_id", "admin-1")
.append("userName", dotenv.get("ADMIN1_USERNAME"))
- .append("password", passwordEncoder.encode(dotenv.get("ADMIN1_PASSWORD"))) // Senha criptografada
- .append("roles", UserType.ROLE_ADMINISTRATOR),
+ .append("password", passwordEncoder.encode(dotenv.get("ADMIN1_PASSWORD")))
+ .append("roles", List.of(roleAdmin)),
new Document("_id", "admin-2")
.append("userName", dotenv.get("ADMIN2_USERNAME"))
- .append("password", passwordEncoder.encode(dotenv.get("ADMIN2_PASSWORD"))) // Senha criptografada
- .append("roles", UserType.ROLE_ADMINISTRATOR)
+ .append("password", passwordEncoder.encode(dotenv.get("ADMIN2_PASSWORD")))
+ .append("roles", List.of(roleAdmin)),
+
+ new Document("_id", "admin-3")
+ .append("userName", dotenv.get("ADMIN3_USERNAME"))
+ .append("password", passwordEncoder.encode(dotenv.get("ADMIN3_PASSWORD")))
+ .append("roles", List.of(roleAdmin)),
+
+ new Document("_id", "admin-4")
+ .append("userName", dotenv.get("ADMIN4_USERNAME"))
+ .append("password", passwordEncoder.encode(dotenv.get("ADMIN4_PASSWORD")))
+ .append("roles", List.of(roleAdmin))
));
System.out.println("Usuários criados com sucesso!");
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java b/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java
new file mode 100644
index 00000000..d9364a3c
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java
@@ -0,0 +1,24 @@
+package com.unipampa.crud.config;
+
+import org.springframework.amqp.rabbit.connection.ConnectionFactory;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RabbitConfig {
+
+ @Bean
+ public Jackson2JsonMessageConverter jackson2JsonMessageConverter() {
+ return new Jackson2JsonMessageConverter();
+ }
+
+ @Bean
+ public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,
+ Jackson2JsonMessageConverter converter) {
+ RabbitTemplate template = new RabbitTemplate(connectionFactory);
+ template.setMessageConverter(converter);
+ return template;
+ }
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java
new file mode 100644
index 00000000..f49cda92
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java
@@ -0,0 +1,52 @@
+package com.unipampa.crud.config.security;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ @Autowired
+ private JwtProvider jwtProvider;
+
+ @Autowired
+ private UserDetailsServiceImpl userDetailsService;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ String token = extractToken(request);
+ if (token != null && jwtProvider.validateToken(token)) {
+ String userId = jwtProvider.getSubjectFromToken(token);
+ logger.info("username extraído do token: " + userId);
+
+ UserDetails userDetails = userDetailsService.loadUserByUserId(userId);
+ logger.info("user details encontrado: " + userDetails);
+
+ UsernamePasswordAuthenticationToken authentication =
+ new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+
+ filterChain.doFilter(request, response);
+
+ }
+
+ private String extractToken(HttpServletRequest request) {
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader != null && authHeader.startsWith("Bearer ")) {
+ return authHeader.substring(7);
+ }
+ return null;
+ }
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java
new file mode 100644
index 00000000..6d2c80e2
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java
@@ -0,0 +1,57 @@
+package com.unipampa.crud.config.security;
+
+import io.jsonwebtoken.*;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+@Log4j2
+@Component
+public class JwtProvider {
+
+ @Value( "${ead.auth.jwtSecret}")
+ private String jwtSecret;
+
+ @Value( "${ead.auth.jwtExpirationMs}")
+ private int jwtExpirationMs;
+
+ public String generateJwtToken(Authentication authentication) {
+ UserDatailsImpl userPrincipal = (UserDatailsImpl) authentication.getPrincipal();
+ return Jwts.builder()
+ .setSubject(userPrincipal.getUserId())
+ .setIssuedAt(new Date())
+ .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
+ .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, jwtSecret)
+ .compact();
+ }
+
+ public String getSubjectFromToken(String token) {
+ return Jwts.parser()
+ .setSigningKey(jwtSecret)
+ .parseClaimsJws(token)
+ .getBody()
+ .getSubject();
+ }
+
+ public boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
+ return true;
+
+ } catch (SignatureException e) {
+ log.error("Invalid JWT signature: {}", e.getMessage());
+ } catch (MalformedJwtException e) {
+ log.error("Invalid JWT token: {}", e.getMessage());
+ } catch (ExpiredJwtException e) {
+ log.error("JWT token is expired: {}", e.getMessage());
+ } catch (UnsupportedJwtException e) {
+ log.error("JWT token is unsupported: {}", e.getMessage());
+ } catch (IllegalArgumentException e) {
+ log.error("JWT claims string is empty: {}", e.getMessage());
+ }
+ return false;
+ }
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java
index 459c9cc1..13fd1628 100644
--- a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java
+++ b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java
@@ -1,9 +1,9 @@
package com.unipampa.crud.config.security;
+import jakarta.ws.rs.HttpMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -13,42 +13,30 @@
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
- private static final String HOST = "HOST";
- private static final String ADMIN = "ADMINISTRATOR";
- private static final String GUEST = "GUEST";
@Autowired
private UserDetailsServiceImpl userDetailsService;
- private static final String[] PUBLIC_MATCHERS = {
- "/users/**"
- };
+ @Autowired
+ private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http.authorizeHttpRequests(auth -> auth
- .requestMatchers(HttpMethod.POST, PUBLIC_MATCHERS).permitAll()
- .requestMatchers("/accommodations/images/**").permitAll()
- .requestMatchers(HttpMethod.POST, "/accommodations/**").hasAnyRole(HOST, ADMIN)
- .requestMatchers(HttpMethod.GET, "/accommodations/**").hasAnyRole(HOST, GUEST, ADMIN)
- .requestMatchers(HttpMethod.GET, "/accommodations/{id}").hasAnyRole(HOST, GUEST, ADMIN)
- .requestMatchers(HttpMethod.PUT, "/accommodations/{id}").hasAnyRole(HOST, ADMIN)
- .requestMatchers(HttpMethod.DELETE, "/accommodations/{id}").hasAnyRole(HOST, ADMIN)
-
- .requestMatchers(HttpMethod.GET, "/users").hasRole(ADMIN)
- .requestMatchers(HttpMethod.GET, "/users/email/**").hasAnyRole(ADMIN, HOST, GUEST)
- .requestMatchers(HttpMethod.GET, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST)
- .requestMatchers(HttpMethod.PUT, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST)
- .requestMatchers(HttpMethod.DELETE, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST)
-
+ http
+ .csrf(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(HttpMethod.POST, "/users/**", "/auth/**").permitAll()
+ .requestMatchers(HttpMethod.GET, "/accommodations", "/accommodations/**", "/images/**").permitAll()
.anyRequest().authenticated()
)
- .httpBasic(Customizer.withDefaults())
- .csrf(AbstractHttpConfigurer::disable);
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
+ .httpBasic(Customizer.withDefaults());
+
return http.build();
}
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java
index 72fdfb03..d071fae9 100644
--- a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java
+++ b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java
@@ -29,7 +29,7 @@ public class UserDatailsImpl implements UserDetails {
public static UserDatailsImpl build(User user) {
List authorities = user.getRoles().stream()
- .map(role -> new SimpleGrantedAuthority(role.getAuthority()))
+ .map(role -> new SimpleGrantedAuthority(role.getRoleName().name()))
.collect(Collectors.toList());
return new UserDatailsImpl (
diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java
index 3182d833..f9f5985c 100644
--- a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java
+++ b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java
@@ -2,6 +2,7 @@
import com.unipampa.crud.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@@ -10,7 +11,7 @@
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
- public static final String USER_NOT_FOUND = "Usuário não encontrado para o nome de usuário: ";
+ public static final String USER_NOT_FOUND = "Usuário não encontrado para o id de usuário: ";
@Autowired
private UserRepository userRepository;
@@ -18,11 +19,18 @@ public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
- var user = userRepository.findByUserName(username)
- .orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND + username));
- return UserDatailsImpl.build(user);
+ var user = userRepository.findByUserName(username);
+ var opt = user.orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND + username));
+ return UserDatailsImpl.build(opt);
} catch (Exception e) {
throw new UsernameNotFoundException("Erro de conectividade ou ao carregar os dados do usuário: " + username, e);
}
}
+
+ public UserDetails loadUserByUserId(String userId) throws AuthenticationCredentialsNotFoundException {
+ var user = userRepository.findById(userId);
+ var userModel = user.orElseThrow(() -> new AuthenticationCredentialsNotFoundException(USER_NOT_FOUND + userId));
+ return UserDatailsImpl.build(userModel);
+
+ }
}
diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java
new file mode 100644
index 00000000..fb20fc99
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java
@@ -0,0 +1,11 @@
+package com.unipampa.crud.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class AccommodationMessageDTO {
+ private BigDecimal price;
+ private int maxOccupancy;
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java
new file mode 100644
index 00000000..aaac8b48
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java
@@ -0,0 +1,15 @@
+package com.unipampa.crud.dto;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@Data
+@RequiredArgsConstructor
+public class JwtDTO {
+
+ @NotNull
+ private final String token;
+
+ private final String type = "Bearer";
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java
new file mode 100644
index 00000000..413497c4
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java
@@ -0,0 +1,13 @@
+package com.unipampa.crud.dto;
+
+import jakarta.validation.constraints.NotBlank;
+
+public record LoginDTO(
+
+ @NotBlank
+ String username,
+
+ @NotBlank
+ String password
+
+) {}
diff --git a/cadastral/src/main/java/com/unipampa/crud/entities/User.java b/cadastral/src/main/java/com/unipampa/crud/entities/User.java
index c78de22e..bc351988 100644
--- a/cadastral/src/main/java/com/unipampa/crud/entities/User.java
+++ b/cadastral/src/main/java/com/unipampa/crud/entities/User.java
@@ -27,6 +27,7 @@ public class User implements Serializable {
@Id
private String id;
+ @Field("userName")
private String userName;
private String password;
private String cpf;
@@ -46,4 +47,5 @@ public class User implements Serializable {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private LocalDateTime lastUpdateDate;
+
}
diff --git a/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java b/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java
new file mode 100644
index 00000000..7a7db77e
--- /dev/null
+++ b/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java
@@ -0,0 +1,14 @@
+package com.unipampa.crud.mappers;
+
+import com.unipampa.crud.dto.UserDTO;
+import com.unipampa.crud.entities.User;
+import org.mapstruct.*;
+
+@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface UserMapper {
+
+
+ @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
+ void updateUserFromDto(UserDTO dto, @MappingTarget User entity);
+
+}
diff --git a/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java b/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java
index 62ac135b..3afdc61a 100644
--- a/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java
+++ b/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java
@@ -18,4 +18,7 @@ public interface UserRepository extends MongoRepository {
@EntityGraph(attributePaths = {"roles"}, type = EntityGraph.EntityGraphType.FETCH)
Optional findByUserName(String username);
+
+ @EntityGraph(attributePaths = {"roles"}, type = EntityGraph.EntityGraphType.FETCH)
+ Optional findById(String userId);
}
\ No newline at end of file
diff --git a/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java b/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java
index 71f3f9e7..17f94c8a 100644
--- a/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java
+++ b/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java
@@ -24,6 +24,7 @@
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -58,6 +59,8 @@ public class AccommodationResource {
@Autowired
private List validations;
+
+ @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST')" )
@PostMapping
@Operation(summary = "Criar uma nova acomodação",
description = "Valida e salva uma nova acomodação no sistema.")
@@ -88,43 +91,7 @@ public ResponseEntity save(
return ResponseEntity.created(location).body(accomodationResponseDTO);
}
- @GetMapping
- @Operation(summary = "Listar todas as acomodações",
- description = "Retorna uma lista contendo todas as acomodações cadastradas.")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "Lista retornada com sucesso",
- content = @Content(schema = @Schema(implementation = AccommodationDTO.class)))
- })
- public ResponseEntity> findAll() {
- List accommodationDtos = accommodationService.findAll()
- .stream()
- .map(accommodation -> accommodationMapper.toDTO(accommodation))
- .collect(Collectors.toList());
-
- return ResponseEntity.ok(accommodationDtos);
- }
-
- @GetMapping("{id}")
- @Operation(summary = "Buscar acomodação por ID",
- description = "Retorna os dados de uma acomodação específica a partir do seu ID.")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200", description = "Acomodação encontrada",
- content = @Content(schema = @Schema(implementation = AccommodationDTO.class))),
- @ApiResponse(responseCode = "404", description = "Acomodação não encontrada",
- content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
- })
- public ResponseEntity