diff --git a/eeg_backend/pom.xml b/eeg_backend/pom.xml index 5f9bdde..9f11765 100644 --- a/eeg_backend/pom.xml +++ b/eeg_backend/pom.xml @@ -42,6 +42,16 @@ springdoc-openapi-starter-webmvc-api 3.0.3 + + org.mapstruct + mapstruct + 1.6.3 + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + com.h2database @@ -99,6 +109,31 @@ org.apache.maven.plugins maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + org.projectlombok + lombok + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + 1.6.3 + + + + -Amapstruct.defaultComponentModel=spring + -Amapstruct.defaultInjectionStrategy=constructor + -Amapstruct.unmappedTargetPolicy=ERROR + + default-compile diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/EdaTopologyCheckEvent.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/EdaTopologyCheckEvent.java new file mode 100644 index 0000000..a61e6ee --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/EdaTopologyCheckEvent.java @@ -0,0 +1,8 @@ +package at.mueller.eeg.backend.common.config.event; + +import java.util.UUID; + +public record EdaTopologyCheckEvent( + UUID userId, + String atNumber +) {} \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/UserRegisteredEvent.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/UserRegisteredEvent.java new file mode 100644 index 0000000..7e6ec6f --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/config/event/UserRegisteredEvent.java @@ -0,0 +1,8 @@ +package at.mueller.eeg.backend.common.config.event; + +import java.util.UUID; + +public record UserRegisteredEvent( + UUID userId, + String atNumber +) {} \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/event/listener/MeteringPointEventListener.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/event/listener/MeteringPointEventListener.java new file mode 100644 index 0000000..54f5966 --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/event/listener/MeteringPointEventListener.java @@ -0,0 +1,33 @@ +package at.mueller.eeg.backend.community.event.listener; + +import at.mueller.eeg.backend.common.config.event.EdaTopologyCheckEvent; +import at.mueller.eeg.backend.common.config.event.UserRegisteredEvent; +import at.mueller.eeg.backend.community.domain.MeteringPoint; +import at.mueller.eeg.backend.community.domain.PointType; +import at.mueller.eeg.backend.community.repository.MeteringPointRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +public class MeteringPointEventListener { + + private final MeteringPointRepository meteringPointRepository; + private final ApplicationEventPublisher eventPublisher; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + @Transactional + public void handleUserRegistered(UserRegisteredEvent event) { + MeteringPoint point = new MeteringPoint(); + point.setUserId(event.userId()); + point.setAtNumber(event.atNumber()); + point.setType(PointType.CONSUMER); + + meteringPointRepository.save(point); + eventPublisher.publishEvent(new EdaTopologyCheckEvent(point.getId(), event.atNumber())); + } +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java new file mode 100644 index 0000000..4398bcd --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MeteringPointRepository.java @@ -0,0 +1,7 @@ +package at.mueller.eeg.backend.community.repository; + +import at.mueller.eeg.backend.community.domain.MeteringPoint; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MeteringPointRepository extends JpaRepository { +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/api/AdminIamController.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/api/AdminIamController.java index 0703caf..74f441c 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/api/AdminIamController.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/api/AdminIamController.java @@ -1,8 +1,10 @@ package at.mueller.eeg.backend.iam.api; import at.mueller.eeg.backend.iam.api.dto.IamDtos.UserProfileResponse; +import at.mueller.eeg.backend.iam.repository.UserRepository; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -14,16 +16,19 @@ import java.util.UUID; @RequestMapping("/api/admin/users") @PreAuthorize("hasRole('ADMIN')") @Tag(name = "Admin IAM", description = "User-Verwaltung und Freigabe-Workflow") +@RequiredArgsConstructor public class AdminIamController { - // private final UserManagementService userManagementService; + private final UserRepository userRepository; @GetMapping("/pending") @Operation(summary = "Lädt alle wartenden User", description = "Zeigt User im Status PENDING inkl. der Ergebnisse des EDA-Netz-Checks.") public ResponseEntity> getPendingUsers() { - // Logik: Finde alle User mit status = PENDING - return ResponseEntity.ok(List.of(/* ... */)); + + + //return ResponseEntity.ok(userRepository.findAllByStatus(RegistrationStatus.PENDING)); + return null; } @PostMapping("/{userId}/approve") diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/PasswordHashMapper.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/PasswordHashMapper.java new file mode 100644 index 0000000..b86d5fe --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/PasswordHashMapper.java @@ -0,0 +1,21 @@ +package at.mueller.eeg.backend.iam.mapper; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import static at.mueller.eeg.backend.iam.mapper.UserMapperAnnotations.PasswordHash; + +@Component +@RequiredArgsConstructor +public class PasswordHashMapper { + + private final PasswordEncoder passwordEncoder; + + @PasswordHash + public String hashPassword(String password) { + return passwordEncoder.encode(password); + } + + +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapper.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapper.java new file mode 100644 index 0000000..05a0a19 --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapper.java @@ -0,0 +1,28 @@ +package at.mueller.eeg.backend.iam.mapper; + +import at.mueller.eeg.backend.iam.api.dto.IamDtos; +import at.mueller.eeg.backend.iam.domain.User; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +@Mapper( + uses = PasswordHashMapper.class +) +public interface UserMapper { + + @Mapping(target = "primaryAtNumber", ignore = true) + IamDtos.UserProfileResponse mapToDto(User user); + + @Mapping(target = "status", constant = "PENDING") + @Mapping(target = "role", constant = "MEMBER") + @Mapping(target = "passwordHash", source = "password", qualifiedBy = UserMapperAnnotations.PasswordHash.class) + @Mapping(target = "id", ignore = true) + @Mapping(target = "iban", ignore = true) + @Mapping(target = "enabled", constant = "false") + @Mapping(target = "authorities", ignore = true) + User mapToEntity(IamDtos.RegistrationRequest request, @MappingTarget User user); + + + +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapperAnnotations.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapperAnnotations.java new file mode 100644 index 0000000..582c196 --- /dev/null +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/mapper/UserMapperAnnotations.java @@ -0,0 +1,19 @@ +package at.mueller.eeg.backend.iam.mapper; + +import lombok.experimental.UtilityClass; +import org.mapstruct.Qualifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@UtilityClass +public class UserMapperAnnotations { + + @Qualifier + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + public @interface PasswordHash { + } +} diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java index b6659f3..6025aeb 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java @@ -1,11 +1,15 @@ package at.mueller.eeg.backend.iam.repository; +import at.mueller.eeg.backend.iam.domain.RegistrationStatus; import at.mueller.eeg.backend.iam.domain.User; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + + List findAllByStatus(RegistrationStatus status); } diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AuthService.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AuthService.java index bb0f7d2..e83f485 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AuthService.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AuthService.java @@ -1,14 +1,15 @@ package at.mueller.eeg.backend.iam.service; -import at.mueller.eeg.backend.iam.domain.RegistrationStatus; +import at.mueller.eeg.backend.common.config.event.UserRegisteredEvent; import at.mueller.eeg.backend.iam.domain.User; +import at.mueller.eeg.backend.iam.mapper.UserMapper; import at.mueller.eeg.backend.iam.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.jose.jws.MacAlgorithm; import org.springframework.security.oauth2.jwt.JwsHeader; import org.springframework.security.oauth2.jwt.JwtClaimsSet; @@ -27,8 +28,9 @@ public class AuthService { private final AuthenticationManager authenticationManager; private final JwtEncoder jwtEncoder; - private final PasswordEncoder passwordEncoder; private final UserRepository userRepository; + private final UserMapper userMapper; + private final ApplicationEventPublisher eventPublisher; /** * Verarbeitet den Login und gibt den JWT-Token als String zurück. @@ -57,17 +59,10 @@ public class AuthService { throw new RuntimeException("E-Mail already exists!"); }); - String hashedPassword = passwordEncoder.encode(request.password()); - - User newUser = new User(); - newUser.setEmail(request.email()); - newUser.setPasswordHash(hashedPassword); - newUser.setFirstName(request.firstName()); - newUser.setLastName(request.lastName()); - newUser.setStatus(RegistrationStatus.PENDING); - newUser.setEnabled(false); - + User newUser = userMapper.mapToEntity(request, new User()); userRepository.save(newUser); + + eventPublisher.publishEvent(new UserRegisteredEvent(newUser.getId(), request.atNumber())); } /**