diff --git a/pom.xml b/pom.xml index 76a63d9f5b3f0f1976c6034c236900fb2350bf9f..f2ab9d3049519f6a30c5911e54b1053bc205bdad 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ </parent> <groupId>de.ozgcloud</groupId> <artifactId>administration</artifactId> - <version>0.3.0-SNAPSHOT</version> + <version>0.4.0-SNAPSHOT</version> <name>Administration</name> <description>Administration Backend Project</description> @@ -19,11 +19,14 @@ <imageName>docker.ozg-sh.de/administration</imageName> <imageTag>build-latest</imageTag> <publishImage>false</publishImage> - <build.number>SET_BY_JENKINS</build.number> + <build.number>SET_BY_JENKINS</build.number> <spring-cloud-config-server.version>4.1.0</spring-cloud-config-server.version> <testcontainers-keycloak.version>3.2.0</testcontainers-keycloak.version> <keycloak-admin-client.version>23.0.6</keycloak-admin-client.version> <mongock.version>5.4.0</mongock.version> + <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version> + <mapstruct-processor.version>1.5.5.Final</mapstruct-processor.version> + </properties> <dependencies> @@ -70,6 +73,17 @@ <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> + <!-- tools --> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>${mapstruct-processor.version}</version> + </dependency> + <!-- mongock --> <dependency> <groupId>io.mongock</groupId> @@ -222,6 +236,29 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct-processor</artifactId> + <version>${mapstruct-processor.version}</version> + </path> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </path> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok-mapstruct-binding</artifactId> + <version>${lombok-mapstruct-binding.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> </plugins> </build> diff --git a/src/main/helm/templates/_helpers.tpl b/src/main/helm/templates/_helpers.tpl index d59b652ef42c838efa9acc93c794dc93d864c8c9..95cbd1ab2dc40644d5831806da452e5992c99085 100644 --- a/src/main/helm/templates/_helpers.tpl +++ b/src/main/helm/templates/_helpers.tpl @@ -42,4 +42,31 @@ app.kubernetes.io/namespace: {{ include "app.namespace" . }} {{- define "app.ozgcloudEnvironment" -}} {{- required "Environment muss angegeben sein" (.Values.ozgcloud).environment -}} +{{- end -}} + +{{- define "app.ssoRealm" -}} +{{ printf "%s-%s-%s" (include "app.ozgcloudBundesland" .) ( include "app.ozgcloudBezeichner" . ) ( include "app.ozgcloudEnvironment" . ) | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{- define "app.ozgcloudBundesland" -}} +{{- required "Bundesland muss angegeben sein" (.Values.ozgcloud).bundesland }} +{{- end -}} + +{{- define "app.ozgcloudBezeichner" -}} +{{- required "ozgcloud.bezeichner muss angegeben sein" (.Values.ozgcloud).bezeichner -}} +{{- if lt 27 (len (.Values.ozgcloud).bezeichner) -}} +{{ fail (printf "ozgcloud.bezeichner %s ist zu lang (max. 27 Zeichen)" (.Values.ozgcloud).bezeichner) }} +{{- end -}} +{{- end -}} + +{{- define "app.ssoClientName" -}} +{{- if (.Values.sso.keycloak_client).client_name -}} +{{ printf "%s" (.Values.sso.keycloak_client).client_name }} +{{- else -}} +{{ printf "admin" }} +{{- end -}} +{{- end -}} + +{{- define "app.ssoServerUrl" -}} +{{- required "sso.serverUrl muss angegeben sein" (.Values.sso).serverUrl -}} {{- end -}} \ No newline at end of file diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index c5c0cf7411b990d848ab845e1f6a7bff0122472c..dfbadbc7c4591bb2d064d5b580be401e598c524e 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -61,6 +61,12 @@ spec: value: "/bindings" - name: spring_profiles_active value: {{ include "app.envSpringProfiles" . }} + - name: ozgcloud_oauth2_realm + value: {{ include "app.ssoRealm" . }} + - name: ozgcloud_oauth2_resource + value: {{ include "app.ssoClientName" . }} + - name: ozgcloud_oauth2_auth-server-url + value: {{ include "app.ssoServerUrl" . }} {{- if not (.Values.database).useExternal }} - name: spring_data_mongodb_uri valueFrom: @@ -71,7 +77,7 @@ spec: - name: spring_data_mongodb_database value: {{ .Values.database.databaseName | default "administration-database" }} {{- end }} - + envFrom: {{- if (.Values.database).useExternal }} - secretRef: diff --git a/src/main/helm/templates/keycloak_client_crd.yaml b/src/main/helm/templates/keycloak_client_crd.yaml index 12c71b9e5c138ca1fc0d83f38074b52d49310a8a..2aac793289643ccddb7d26d885731fd0a901a93a 100644 --- a/src/main/helm/templates/keycloak_client_crd.yaml +++ b/src/main/helm/templates/keycloak_client_crd.yaml @@ -1,5 +1,5 @@ {{- if not (.Values.sso).disableOzgOperator -}} -{{ range $client := (.Values.sso).keycloak_clients }} +{{ $client := (.Values.sso).keycloak_client }} --- apiVersion: operator.ozgcloud.de/v1 kind: OzgCloudKeycloakClient @@ -9,7 +9,7 @@ metadata: spec: keep_after_delete: {{ $.Values.sso.keep_after_delete | default false }} client_name: {{ $client.client_name }} - client_base_url: https://{{ include "app.baseDomain" $ }} + client_base_url: https://{{ include "app.baseDomain" $ }} client_web_origins: - https://{{ include "app.baseDomain" $ }} @@ -32,5 +32,4 @@ spec: {{ toYaml . | indent 4 }} {{- end }} {{- end }} -{{ end }} {{- end -}} \ No newline at end of file diff --git a/src/main/helm/templates/network_policy.yaml b/src/main/helm/templates/network_policy.yaml index 0b04539f5f49f11190eeeddf9de4c0fb867e7fd9..efe20f5232454748e707d50e2fb5539dc5736cfb 100644 --- a/src/main/helm/templates/network_policy.yaml +++ b/src/main/helm/templates/network_policy.yaml @@ -27,7 +27,7 @@ spec: protocol: TCP # public keycloak ip - to: - - ipBlock: + - ipBlock: cidr: {{ required "networkPolicy.ssoPublicIp must be set" (.Values.networkPolicy).ssoPublicIp }} - to: - namespaceSelector: diff --git a/src/main/java/de/ozgcloud/admin/AdministrationApplication.java b/src/main/java/de/ozgcloud/admin/AdministrationApplication.java index 563eea5b36d486c99187d35a08c61e6fb2e34bf1..b33cca756ff78962f1b54145707fa20ae95a0f6c 100644 --- a/src/main/java/de/ozgcloud/admin/AdministrationApplication.java +++ b/src/main/java/de/ozgcloud/admin/AdministrationApplication.java @@ -21,18 +21,18 @@ */ package de.ozgcloud.admin; -import io.mongock.runner.springboot.EnableMongock; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import io.mongock.runner.springboot.EnableMongock; + @SpringBootApplication @EnableConfigServer @EnableMongock @EnableMongoRepositories public class AdministrationApplication { - public static void main(String[] args) { SpringApplication.run(AdministrationApplication.class, args); } diff --git a/src/main/java/de/ozgcloud/admin/Root.java b/src/main/java/de/ozgcloud/admin/Root.java index 8fc3d5966abd999afa09d17b2c555cb4cc0b409d..13af4d82bbccf0c18deac076b9358babd4e9a904 100644 --- a/src/main/java/de/ozgcloud/admin/Root.java +++ b/src/main/java/de/ozgcloud/admin/Root.java @@ -30,7 +30,7 @@ import lombok.Getter; @Getter public class Root { private String javaVersion; - private String buildVersion; + private String version; private Instant buildTime; private String buildNumber; } diff --git a/src/main/java/de/ozgcloud/admin/RootController.java b/src/main/java/de/ozgcloud/admin/RootController.java index 887862e364924be5ce3de9b80bb4c4d2afd5a0a7..54cc0572d94c47f3f141071066fd9bf02e776322 100644 --- a/src/main/java/de/ozgcloud/admin/RootController.java +++ b/src/main/java/de/ozgcloud/admin/RootController.java @@ -48,7 +48,7 @@ public class RootController { return Root.builder() .javaVersion(System.getProperty("java.version")) .buildTime(buildProperties.getTime()) - .buildVersion(buildProperties.getVersion()) + .version(buildProperties.getVersion()) .buildNumber(buildProperties.get("number")) .build(); } diff --git a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java index 2cf01448f6fa1ab4f52d3a72fc06b15d57c5cda7..319cb4d3525e1eede720494444555aa7c2f81a8d 100644 --- a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java +++ b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java @@ -21,6 +21,8 @@ */ package de.ozgcloud.admin; +import de.ozgcloud.admin.common.user.CurrentUserService; +import de.ozgcloud.admin.common.user.UserRole; import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; @@ -30,6 +32,9 @@ import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; +import java.util.ArrayList; +import java.util.List; + @Component @RequiredArgsConstructor public class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> { @@ -37,13 +42,31 @@ public class RootModelAssembler implements RepresentationModelAssembler<Root, En private final RepositoryRestProperties restProperties; + private final CurrentUserService currentUserService; + @Override public EntityModel<Root> toModel(Root root) { - var rootLink = WebMvcLinkBuilder.linkTo(RootController.class); - var configLink = rootLink.toUriComponentsBuilder().replacePath(restProperties.getBasePath()); + List<Link> links = buildRootModelLinks(); return EntityModel.of( root, - Link.of(configLink.toUriString(), REL_CONFIGURATION), - rootLink.withSelfRel()); + links); + } + + List<Link> buildRootModelLinks() { + List<Link> links = new ArrayList<>(); + var rootLinkBuilder = WebMvcLinkBuilder.linkTo(RootController.class); + links.add(rootLinkBuilder.withSelfRel()); + if (currentUserService.hasRole(UserRole.ADMIN_ADMIN)) { + links.add(buildConfigLink()); + } + return links; + } + + private Link buildConfigLink() { + var rootLinkBuilder = WebMvcLinkBuilder.linkTo(RootController.class); + return Link.of( + rootLinkBuilder.toUriComponentsBuilder().replacePath(restProperties.getBasePath()).toUriString(), + REL_CONFIGURATION + ); } } diff --git a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..16f4bf18d38516bbdf07cadfd58b9ce5fe793426 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserHelper.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.admin.common.user; + +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +class CurrentUserHelper { + static final String ROLE_PREFIX = "ROLE_"; + private static final Predicate<String> IS_ROLE_PREFIX_MISSING = role -> !role.startsWith(ROLE_PREFIX); + private static final AuthenticationTrustResolver TRUST_RESOLVER = new AuthenticationTrustResolverImpl(); + private static final Predicate<Authentication> IS_TRUSTED = auth -> !TRUST_RESOLVER.isAnonymous(auth); + + public static boolean hasRole(String role) { + var auth = getAuthentication(); + + if ((Objects.isNull(auth)) || (Objects.isNull(auth.getPrincipal()))) { + return false; + } + + return containsRole(auth.getAuthorities(), role); + } + + static boolean containsRole(Collection<? extends GrantedAuthority> authorities, String role) { + if (Objects.isNull(authorities)) { + return false; + } + return authorities.stream().anyMatch(a -> StringUtils.equalsIgnoreCase(addRolePrefixIfMissing(role), a.getAuthority())); + } + + static String addRolePrefixIfMissing(String roleToCheck) { + return Optional.ofNullable(roleToCheck) + .filter(IS_ROLE_PREFIX_MISSING) + .map(role -> ROLE_PREFIX + role) + .orElse(roleToCheck); + } + + static Authentication getAuthentication() { + return findAuthentication().orElseThrow(() -> new IllegalStateException("No authenticated User found")); + } + + private static Optional<Authentication> findAuthentication() { + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).filter(IS_TRUSTED); + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java new file mode 100644 index 0000000000000000000000000000000000000000..94912e9e4749fe871f4120477fe840fe617bb52b --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/user/CurrentUserService.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.admin.common.user; + +import org.springframework.stereotype.Service; + +@Service +public class CurrentUserService { + public boolean hasRole(String role) { + return CurrentUserHelper.hasRole(role); + } +} diff --git a/src/main/java/de/ozgcloud/admin/common/user/UserRole.java b/src/main/java/de/ozgcloud/admin/common/user/UserRole.java new file mode 100644 index 0000000000000000000000000000000000000000..1b0db41b19d75eb43875afd31da0ea2b0beb69b7 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/common/user/UserRole.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.admin.common.user; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UserRole { + public static final String ADMIN_ADMIN = "ADMIN_ADMIN"; +} diff --git a/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java b/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java index 568d79a76fc6268d93b7ecc2e172ae9625d40ed1..b1c6280e10acdf642638dea39854bd5eec7b4446 100644 --- a/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java +++ b/src/main/java/de/ozgcloud/admin/security/SecurityConfiguration.java @@ -19,7 +19,13 @@ */ package de.ozgcloud.admin.security; +import static java.util.stream.Collectors.*; + +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,10 +35,15 @@ import org.springframework.security.config.annotation.method.configuration.Enabl import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.web.SecurityFilterChain; +import de.ozgcloud.admin.common.user.UserRole; +import de.ozgcloud.admin.environment.OAuth2Properties; import lombok.RequiredArgsConstructor; @Configuration @@ -43,6 +54,14 @@ public class SecurityConfiguration { private final AdminAuthenticationEntryPoint authenticationEntryPoint; + private final OAuth2Properties oAuth2Properties; + + static final String RESOURCE_ACCESS_KEY = "resource_access"; + + static final String SIMPLE_GRANT_AUTHORITY_PREFIX = "ROLE_"; + + static final String ROLES_KEY = "roles"; + @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @@ -54,6 +73,8 @@ public class SecurityConfiguration { http.authorizeHttpRequests(requests -> requests .requestMatchers(HttpMethod.GET, "/api/environment").permitAll() + .requestMatchers("/api/configuration").hasRole(UserRole.ADMIN_ADMIN) + .requestMatchers("/api/configuration/**").hasRole(UserRole.ADMIN_ADMIN) .requestMatchers("/api").authenticated() .requestMatchers("/api/**").authenticated() .requestMatchers("/actuator").permitAll() @@ -67,9 +88,42 @@ public class SecurityConfiguration { @Bean JwtAuthenticationConverter jwtAuthenticationConverter() { var jwtConverter = new JwtAuthenticationConverter(); - jwtConverter.setJwtGrantedAuthoritiesConverter(jwt -> List.of(() -> "ROLE_USER")); + jwtConverter.setJwtGrantedAuthoritiesConverter( + this::convertJwtToGrantedAuthorities); jwtConverter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME); return jwtConverter; } -} \ No newline at end of file + Set<GrantedAuthority> convertJwtToGrantedAuthorities(Jwt jwt) { + return getRolesFromJwt(jwt) + .stream() + .map(this::mapRoleStringToGrantedAuthority) + .collect(toSet()); + } + + private GrantedAuthority mapRoleStringToGrantedAuthority(String role) { + return new SimpleGrantedAuthority(SIMPLE_GRANT_AUTHORITY_PREFIX + role); + } + + List<String> getRolesFromJwt(Jwt jwt) { + return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS_KEY)) + .flatMap(resourceAccessMap -> getMap(resourceAccessMap, oAuth2Properties.getResource())) + .flatMap(adminClientMap -> getList(adminClientMap, ROLES_KEY)) + .orElse(Collections.emptyList()); + } + + @SuppressWarnings("unchecked") + private Optional<Map<String, Object>> getMap(Map<String, Object> map, String mapKey) { + return Optional.ofNullable(map.get(mapKey)) + .filter(Map.class::isInstance) + .map(obj -> (Map<String, Object>) obj); + } + + @SuppressWarnings("unchecked") + private Optional<List<String>> getList(Map<String, Object> map, String mapKey) { + return Optional.ofNullable(map.get(mapKey)) + .filter(List.class::isInstance) + .map(obj -> (List<String>) obj); + } + +} diff --git a/src/main/java/de/ozgcloud/admin/setting/AlfaSettingDTO.java b/src/main/java/de/ozgcloud/admin/setting/AlfaSettingDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..0e6e6fed08fee3d9a87e10a57461901bfea7f531 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/AlfaSettingDTO.java @@ -0,0 +1,14 @@ +package de.ozgcloud.admin.setting; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +class AlfaSettingDTO implements ApplicationSettingDTO { + @JsonProperty("ozgcloud.postfach.signatur") + @Builder.Default + private String signatur = ""; +} diff --git a/src/main/java/de/ozgcloud/admin/setting/ApplicationSettingDTO.java b/src/main/java/de/ozgcloud/admin/setting/ApplicationSettingDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..789f27b030e9b4e54ce96bdffb5107aa8345a684 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/ApplicationSettingDTO.java @@ -0,0 +1,4 @@ +package de.ozgcloud.admin.setting; + +interface ApplicationSettingDTO { +} diff --git a/src/main/java/de/ozgcloud/admin/settings/DataRestConfiguration.java b/src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java similarity index 61% rename from src/main/java/de/ozgcloud/admin/settings/DataRestConfiguration.java rename to src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java index 7c5d282338be512a5a0397f0950d30935e513dcf..c82d68e26f46afa10bd35a193fc9152dc62a0035 100644 --- a/src/main/java/de/ozgcloud/admin/settings/DataRestConfiguration.java +++ b/src/main/java/de/ozgcloud/admin/setting/DataRestConfiguration.java @@ -1,18 +1,20 @@ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; +import lombok.RequiredArgsConstructor; + @Configuration +@RequiredArgsConstructor public class DataRestConfiguration implements RepositoryRestConfigurer { - @Autowired - private SettingsValidator settingsValidator; + + private final SettingValidator settingValidator; @Override public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) { - v.addValidator("beforeSave", settingsValidator); - v.addValidator("beforeCreate", settingsValidator); + v.addValidator("beforeSave", settingValidator); + v.addValidator("beforeCreate", settingValidator); } } \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/settings/Settings.java b/src/main/java/de/ozgcloud/admin/setting/Setting.java similarity index 91% rename from src/main/java/de/ozgcloud/admin/settings/Settings.java rename to src/main/java/de/ozgcloud/admin/setting/Setting.java index 85c519591406cba2bd753b283e377aea4d8ab9ca..6bf42c624ded3481ee4d976352d8cece48fd6fee 100644 --- a/src/main/java/de/ozgcloud/admin/settings/Settings.java +++ b/src/main/java/de/ozgcloud/admin/setting/Setting.java @@ -19,7 +19,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; import jakarta.validation.Valid; @@ -36,8 +36,8 @@ import lombok.extern.jackson.Jacksonized; @Builder @Getter @Jacksonized -@Document(Settings.COLLECTION_NAME) -public class Settings { +@Document(Setting.COLLECTION_NAME) +public class Setting { static final String COLLECTION_NAME = "settings"; @Id @@ -46,5 +46,5 @@ public class Settings { private String name; @Valid @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.EXTERNAL_PROPERTY, property = "name") - private SettingsBody settingsBody; + private SettingBody settingBody; } diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingBody.java b/src/main/java/de/ozgcloud/admin/setting/SettingBody.java new file mode 100644 index 0000000000000000000000000000000000000000..d9aaa8b2b2a99ab518214bc3ade69ea78b9b2039 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/SettingBody.java @@ -0,0 +1,13 @@ +package de.ozgcloud.admin.setting; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; + +@JsonSubTypes({ + @Type(value = PostfachSettingBody.class, name = "Postfach") +}) +public interface SettingBody { + +} diff --git a/src/main/java/de/ozgcloud/admin/settings/SettingsConstants.java b/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java similarity index 94% rename from src/main/java/de/ozgcloud/admin/settings/SettingsConstants.java rename to src/main/java/de/ozgcloud/admin/setting/SettingConstants.java index d12906c039760748fd9904a2bc9a3ccdf9f66259..521f39ad36f7a9672133d265d17e07b7a8cec837 100644 --- a/src/main/java/de/ozgcloud/admin/settings/SettingsConstants.java +++ b/src/main/java/de/ozgcloud/admin/setting/SettingConstants.java @@ -19,13 +19,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; import lombok.AccessLevel; import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class SettingsConstants { +public class SettingConstants { static final String REL = "settings"; diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java b/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..0843cb38ab3701c0d46106324654d97af1afe889 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/SettingEnvironmentRepository.java @@ -0,0 +1,55 @@ +package de.ozgcloud.admin.setting; + +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.environment.PropertySource; +import org.springframework.cloud.config.server.environment.EnvironmentRepository; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Component +public class SettingEnvironmentRepository implements EnvironmentRepository { + + private final SettingService settingService; + + private final ObjectMapper objectMapper; + + private static final String ALFA = "Alfa"; + private static final String VORGANG_MANAGER = "OzgCloud_VorgangManager"; + + @Override + public Environment findOne(String application, String profile, String label) { + return buildEnvironment(application, findAnwendungSettingDTO(application)); + } + + Optional<ApplicationSettingDTO> findAnwendungSettingDTO(String application) { + switch (application) { + case ALFA: + return Optional.of(settingService.getAlfaSettingDTO()); + case VORGANG_MANAGER: + return Optional.of(settingService.getVorgangManagerSettingDTO()); + default: + return Optional.empty(); + } + } + + Environment buildEnvironment(String application, Optional<ApplicationSettingDTO> settingDTO) { + var environment = new Environment(application); + settingDTO.ifPresent(setting -> environment.add(transformToPropertySource(setting))); + return environment; + } + + PropertySource transformToPropertySource(ApplicationSettingDTO settingDTO) { + return new PropertySource(StringUtils.EMPTY, objectMapper.convertValue(settingDTO, new TypeReference<Map<String, Object>>() { + })); + } + +} diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingMapper.java b/src/main/java/de/ozgcloud/admin/setting/SettingMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..f0f1ba6273a836a1a3fc94af1bd73a4fa3e2b1c0 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/SettingMapper.java @@ -0,0 +1,21 @@ +package de.ozgcloud.admin.setting; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.ReportingPolicy; + +import de.ozgcloud.admin.setting.postfach.Absender; +import de.ozgcloud.admin.setting.postfach.AbsenderVorgangManager; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN) +interface SettingMapper { + + AlfaSettingDTO.AlfaSettingDTOBuilder mapAlfaPostfach(@MappingTarget AlfaSettingDTO.AlfaSettingDTOBuilder builder, + PostfachSettingBody postfachData); + + VorgangManagerSettingDTO.VorgangManagerSettingDTOBuilder mapVorgangManagerPostfach( + @MappingTarget VorgangManagerSettingDTO.VorgangManagerSettingDTOBuilder builder, PostfachSettingBody postfachData); + + AbsenderVorgangManager mapAbsenderToAbsenderVorgangManager(Absender absender); +} diff --git a/src/main/java/de/ozgcloud/admin/settings/SettingsRepository.java b/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java similarity index 78% rename from src/main/java/de/ozgcloud/admin/settings/SettingsRepository.java rename to src/main/java/de/ozgcloud/admin/setting/SettingRepository.java index 722039cce0407f83ea7e4edb87943f705dbf4f6f..0094e0ecf2375606e486eea2ab7fc28e06ffb8f3 100644 --- a/src/main/java/de/ozgcloud/admin/settings/SettingsRepository.java +++ b/src/main/java/de/ozgcloud/admin/setting/SettingRepository.java @@ -19,12 +19,15 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; + +import java.util.Optional; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; -@RepositoryRestResource(collectionResourceRel = SettingsConstants.REL, path = SettingsConstants.PATH) -interface SettingsRepository extends MongoRepository<Settings, String> { +@RepositoryRestResource(collectionResourceRel = SettingConstants.REL, path = SettingConstants.PATH) +interface SettingRepository extends MongoRepository<Setting, String> { + Optional<Setting> findOneByName(String name); } diff --git a/src/main/java/de/ozgcloud/admin/setting/SettingService.java b/src/main/java/de/ozgcloud/admin/setting/SettingService.java new file mode 100644 index 0000000000000000000000000000000000000000..ca6a15e72d2e7032faa1def7d2dac03785a0d758 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/SettingService.java @@ -0,0 +1,31 @@ +package de.ozgcloud.admin.setting; + +import org.springframework.stereotype.Service; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +class SettingService { + private static final String POSTFACH_SETTING_ITEM_NAME = "Postfach"; + + private final SettingRepository repository; + + private final SettingMapper mapper; + + public AlfaSettingDTO getAlfaSettingDTO() { + var postfachData = getSettingWithPostfachFromDb(); + return mapper.mapAlfaPostfach(AlfaSettingDTO.builder(), postfachData).build(); + } + + public VorgangManagerSettingDTO getVorgangManagerSettingDTO() { + var postfachData = getSettingWithPostfachFromDb(); + return mapper.mapVorgangManagerPostfach(VorgangManagerSettingDTO.builder(), postfachData).build(); + } + + PostfachSettingBody getSettingWithPostfachFromDb() { + var postfach = repository.findOneByName(POSTFACH_SETTING_ITEM_NAME); + return postfach.isPresent() ? (PostfachSettingBody) postfach.get().getSettingBody() : PostfachSettingBody.builder().build(); + } +} diff --git a/src/main/java/de/ozgcloud/admin/settings/SettingsValidator.java b/src/main/java/de/ozgcloud/admin/setting/SettingValidator.java similarity index 83% rename from src/main/java/de/ozgcloud/admin/settings/SettingsValidator.java rename to src/main/java/de/ozgcloud/admin/setting/SettingValidator.java index e4b91f801e5d04f32b8d74e4d552d8440d05d957..6a8c9951f5dae99a5383ca0c77ad6922a2232961 100644 --- a/src/main/java/de/ozgcloud/admin/settings/SettingsValidator.java +++ b/src/main/java/de/ozgcloud/admin/setting/SettingValidator.java @@ -1,4 +1,4 @@ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Validation; @@ -8,11 +8,11 @@ import org.springframework.validation.Errors; import org.springframework.validation.Validator; @Component -class SettingsValidator implements Validator { +class SettingValidator implements Validator { @Override public boolean supports(Class<?> clazz) { - return Settings.class.equals(clazz); + return Setting.class.equals(clazz); } @Override diff --git a/src/main/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTO.java b/src/main/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..aaaabb35fe0929efbd8b6c684f4d281530b28879 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTO.java @@ -0,0 +1,15 @@ +package de.ozgcloud.admin.setting; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import de.ozgcloud.admin.setting.postfach.AbsenderVorgangManager; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +class VorgangManagerSettingDTO implements ApplicationSettingDTO { + @JsonUnwrapped(prefix = "ozgcloud.postfach.absender.") + @Builder.Default + private AbsenderVorgangManager absender = AbsenderVorgangManager.builder().build(); +} diff --git a/src/main/java/de/ozgcloud/admin/settings/postfach/Absender.java b/src/main/java/de/ozgcloud/admin/setting/postfach/Absender.java similarity index 89% rename from src/main/java/de/ozgcloud/admin/settings/postfach/Absender.java rename to src/main/java/de/ozgcloud/admin/setting/postfach/Absender.java index c5be084a5b87420612dcbef48ae3a57359b015e4..d29b58267320ab47f1d8220b5234841e84948fa5 100644 --- a/src/main/java/de/ozgcloud/admin/settings/postfach/Absender.java +++ b/src/main/java/de/ozgcloud/admin/setting/postfach/Absender.java @@ -1,4 +1,4 @@ -package de.ozgcloud.admin.settings.postfach; +package de.ozgcloud.admin.setting.postfach; import static de.ozgcloud.admin.common.errorhandling.ValidationMessageCodes.*; @@ -11,7 +11,7 @@ import lombok.extern.jackson.Jacksonized; @Getter @Builder @Jacksonized -class Absender { +public class Absender { @NotEmpty(message = FIELD_IS_EMPTY) private String name; @NotEmpty(message = FIELD_IS_EMPTY) diff --git a/src/main/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManager.java b/src/main/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManager.java new file mode 100644 index 0000000000000000000000000000000000000000..1efc59b8a55df0f26cb99e08814bc90fafea6b5c --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManager.java @@ -0,0 +1,22 @@ +package de.ozgcloud.admin.setting.postfach; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class AbsenderVorgangManager { + @Builder.Default + private String name = ""; + @Builder.Default + private String anschrift = ""; + @Builder.Default + private String dienst = ""; + @Builder.Default + private String mandant = ""; + @Builder.Default + @JsonProperty("gemeinde-schluessel") + private String gemeindeschluessel = ""; +} diff --git a/src/main/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBody.java b/src/main/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBody.java new file mode 100644 index 0000000000000000000000000000000000000000..a7ea5da7a5ff695e9dd6a921479cba380335d1f2 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBody.java @@ -0,0 +1,20 @@ +package de.ozgcloud.admin.setting.postfach; + +import jakarta.validation.Valid; + +import org.springframework.data.annotation.TypeAlias; + +import de.ozgcloud.admin.setting.SettingBody; +import lombok.Builder; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; + +@Getter +@Jacksonized +@Builder +@TypeAlias("Postfach") +public class PostfachSettingBody implements SettingBody { + @Valid + private Absender absender; + private String signatur; +} diff --git a/src/main/java/de/ozgcloud/admin/settings/AdminEnvironmentRepository.java b/src/main/java/de/ozgcloud/admin/settings/AdminEnvironmentRepository.java deleted file mode 100644 index 78d539beba3d9ad97e9197f0fc2efd86b5d99934..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/admin/settings/AdminEnvironmentRepository.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.ozgcloud.admin.settings; - -import java.util.List; - -import org.springframework.cloud.config.environment.Environment; -import org.springframework.cloud.config.environment.PropertySource; -import org.springframework.cloud.config.server.environment.EnvironmentRepository; -import org.springframework.stereotype.Component; - -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Component -public class AdminEnvironmentRepository implements EnvironmentRepository { - - private final PropertyRepository propertyRepository; - - @Override - public Environment findOne(String application, String profile, String label) { - List<ConfigurationProperties> properties = propertyRepository.findAllByApplication(application); - return buildEnvironment(application, properties); - } - - Environment buildEnvironment(String application, List<ConfigurationProperties> properties) { - var environment = new Environment(application); - environment.addAll(properties.stream().map(this::createPropertySource).toList()); - return environment; - } - - PropertySource createPropertySource(ConfigurationProperties configurationProperties) { - return new PropertySource(null, configurationProperties.getSource()); - } - -} diff --git a/src/main/java/de/ozgcloud/admin/settings/ConfigurationProperties.java b/src/main/java/de/ozgcloud/admin/settings/ConfigurationProperties.java deleted file mode 100644 index aec9487ddc5e61d46dc64ca82e6df0488981ca15..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/admin/settings/ConfigurationProperties.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.ozgcloud.admin.settings; - -import java.util.Map; - -import org.springframework.data.mongodb.core.mapping.Document; - -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Builder -@Document(ConfigurationProperties.COLLECTION_NAME) -public class ConfigurationProperties { - public static final String COLLECTION_NAME = "configuration_properties"; - private String application; - private String profile; - private Map<String, Object> source; -} diff --git a/src/main/java/de/ozgcloud/admin/settings/PropertyRepository.java b/src/main/java/de/ozgcloud/admin/settings/PropertyRepository.java deleted file mode 100644 index 69b124411a0b340c7f2920705d3698390c46391b..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/admin/settings/PropertyRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.ozgcloud.admin.settings; - -import java.util.List; - -import org.springframework.data.mongodb.repository.MongoRepository; - -interface PropertyRepository extends MongoRepository<ConfigurationProperties, String> { - - List<ConfigurationProperties> findAllByApplication(String application); - -} \ No newline at end of file diff --git a/src/main/java/de/ozgcloud/admin/settings/SettingsBody.java b/src/main/java/de/ozgcloud/admin/settings/SettingsBody.java deleted file mode 100644 index f007d95301abb9f33a262299ff864188d1008967..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/admin/settings/SettingsBody.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.ozgcloud.admin.settings; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; - -import de.ozgcloud.admin.settings.postfach.Postfach; - -@JsonSubTypes({ - @Type(value = Postfach.class, name = "Postfach") -}) -public interface SettingsBody { - -} diff --git a/src/main/java/de/ozgcloud/admin/settings/postfach/Postfach.java b/src/main/java/de/ozgcloud/admin/settings/postfach/Postfach.java deleted file mode 100644 index f764f208307e71d8865e5ebf9bb6dd35c7408ff0..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/admin/settings/postfach/Postfach.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.ozgcloud.admin.settings.postfach; - -import jakarta.validation.Valid; - -import de.ozgcloud.admin.settings.SettingsBody; -import lombok.Builder; -import lombok.Getter; -import lombok.extern.jackson.Jacksonized; - -@Getter -@Jacksonized -@Builder -public class Postfach implements SettingsBody { - @Valid - private Absender absender; - private String signatur; -} diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 174b55dcb8dee4e35f6b11275f4d563dc4371669..c3514d2f7c0a361129dde64e8f53fb137c55a67f 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -1,6 +1,2 @@ ozgcloud: - production: false - oauth2: - auth-server-url: https://sso.dev.by.ozg-cloud.de - realm: by-kiel-dev - resource: admin \ No newline at end of file + production: false \ No newline at end of file diff --git a/src/test/helm/deployment_database_test.yaml b/src/test/helm/deployment_database_test.yaml index bde6a7f20fcca8b5db8234965c1c38cad365f268..8c747b650b4925c6026325d50f46de3ee42e6c73 100644 --- a/src/test/helm/deployment_database_test.yaml +++ b/src/test/helm/deployment_database_test.yaml @@ -28,6 +28,12 @@ release: namespace: sh-helm-test templates: - templates/deployment.yaml +set: + ozgcloud: + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should have a volume for the mongodb certificate asserts: @@ -73,4 +79,4 @@ tests: path: spec.template.spec.containers[0].envFrom content: secretRef: - name: administration-database-spring \ No newline at end of file + name: administration-database-spring diff --git a/src/test/helm/deployment_defaults_sso_test.yaml b/src/test/helm/deployment_defaults_sso_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3af4ab3385e69bae001433ba9cf8b0df3795ce9b --- /dev/null +++ b/src/test/helm/deployment_defaults_sso_test.yaml @@ -0,0 +1,87 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test deployment +release: + name: administration + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + baseUrl: test.company.local + ozgcloud: + environment: test + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local +tests: + - it: check default values + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_oauth2_realm + value: sh-helm-test + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_oauth2_resource + value: admin + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_oauth2_auth-server-url + value: https://sso.company.local + + - it: check realm with long namespace + set: + ozgcloud: + bezeichner: eins-zwei-drei + environment: dev + bundesland: sh + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_oauth2_realm + value: sh-eins-zwei-drei-dev + + - it: check different client name + set: + sso: + keycloak_client: + client_name: different-client + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_oauth2_resource + value: different-client diff --git a/src/test/helm/deployment_image_pull_secret_test.yaml b/src/test/helm/deployment_image_pull_secret_test.yaml index 7aea9cb83872375b74ff8f737eb6d2165d940093..44f285aa845dddef22c11220bef7714efdc1f9ae 100644 --- a/src/test/helm/deployment_image_pull_secret_test.yaml +++ b/src/test/helm/deployment_image_pull_secret_test.yaml @@ -27,6 +27,12 @@ templates: release: name: administration namespace: helm-test +set: + ozgcloud: + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should use default image pull secret asserts: @@ -41,4 +47,4 @@ tests: - contains: path: spec.template.spec.imagePullSecrets content: - name: custom-image-pull-secret \ No newline at end of file + name: custom-image-pull-secret diff --git a/src/test/helm/deployment_resources_test.yaml b/src/test/helm/deployment_resources_test.yaml index 29322c2183f84bf7515fff981bb1f4bc849c63f3..d37443f22191266721ac95118b716692cebfeeb0 100644 --- a/src/test/helm/deployment_resources_test.yaml +++ b/src/test/helm/deployment_resources_test.yaml @@ -27,6 +27,12 @@ templates: release: name: administration namespace: helm-test +set: + ozgcloud: + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should have customizable resources set: @@ -34,4 +40,4 @@ tests: asserts: - equal: path: spec.template.spec.containers[0].resources - value: { "test": 1 } \ No newline at end of file + value: { "test": 1 } diff --git a/src/test/helm/deployment_spring_profile_test.yaml b/src/test/helm/deployment_spring_profile_test.yaml index 35c8ad1d614d343022a87aff7bdabaf623082b10..f99d9b9893a31a013693ccf7b6402603f8bc390f 100644 --- a/src/test/helm/deployment_spring_profile_test.yaml +++ b/src/test/helm/deployment_spring_profile_test.yaml @@ -28,6 +28,12 @@ release: namespace: sh-helm-test templates: - templates/deployment.yaml +set: + ozgcloud: + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should override the spring profiles set: @@ -48,4 +54,4 @@ tests: path: spec.template.spec.containers[0].env content: name: spring_profiles_active - value: oc, dev \ No newline at end of file + value: oc, dev diff --git a/src/test/helm/deployment_test.yaml b/src/test/helm/deployment_test.yaml index 7168d78dd2471583010650f08e3900c8d7aea49a..6bd31db1bcadaa990cef70e13f04845e42e6f9e7 100644 --- a/src/test/helm/deployment_test.yaml +++ b/src/test/helm/deployment_test.yaml @@ -28,7 +28,12 @@ release: templates: - templates/deployment.yaml set: - ozgcloud.environment: "default" + ozgcloud: + environment: "default" + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should have metadata values asserts: diff --git a/src/test/helm/deployment_volume_test.yaml b/src/test/helm/deployment_volume_test.yaml index 0625a237b77e1d234ed73d6669f686ad0990b97e..6546763a8b9a03d0ce9fc964a503fe6fbb847092 100644 --- a/src/test/helm/deployment_volume_test.yaml +++ b/src/test/helm/deployment_volume_test.yaml @@ -28,8 +28,13 @@ release: templates: - templates/deployment.yaml set: - Release.Name: "administration" - ozgcloud.environment: "default" + Release.Name: "administration" + ozgcloud: + environment: "default" + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local tests: - it: should have volumeMounts on it's container asserts: @@ -73,4 +78,4 @@ tests: value: "ozg-mongodb-tls-cert" - equal: path: spec.template.spec.volumes[1].secret.optional - value: true \ No newline at end of file + value: true diff --git a/src/test/helm/keycloak_client_crd_test.yaml b/src/test/helm/keycloak_client_crd_test.yaml index 47e90d4d0dd42abd6bd85d67ad1b223d2eed5abe..bb199c30c8003e7b5bf3b1b8329633a687a85277 100644 --- a/src/test/helm/keycloak_client_crd_test.yaml +++ b/src/test/helm/keycloak_client_crd_test.yaml @@ -37,8 +37,8 @@ tests: - it: should contain header data set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - isAPIVersion: of: operator.ozgcloud.de/v1 @@ -47,8 +47,8 @@ tests: - it: should have metadata set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: metadata.name @@ -59,8 +59,8 @@ tests: - it: should set keep after delete default to false set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.keep_after_delete @@ -69,8 +69,8 @@ tests: set: sso: keep_after_delete: true - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.keep_after_delete @@ -78,8 +78,8 @@ tests: - it: has client name value set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.client_name @@ -87,8 +87,8 @@ tests: - it: should set client base url set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.client_base_url @@ -96,8 +96,8 @@ tests: - it: should set client redirect uris set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.client_web_origins @@ -106,8 +106,8 @@ tests: - it: should set client web origins set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin asserts: - equal: path: spec.client_redirect_uris @@ -117,11 +117,11 @@ tests: - it: should use additional client web origins set: sso: - keycloak_clients: - - client_name: admin - additional_client_web_origins: - - https://some.origin.de - - https://some.other.origin.de + keycloak_client: + client_name: admin + additional_client_web_origins: + - https://some.origin.de + - https://some.other.origin.de asserts: - equal: path: spec.client_web_origins @@ -132,10 +132,10 @@ tests: - it: should use additional redirect uris for client web origins set: sso: - keycloak_clients: - - client_name: admin - additional_redirect_uris: - - https://additional.url.de + keycloak_client: + client_name: admin + additional_redirect_uris: + - https://additional.url.de asserts: - equal: path: spec.client_redirect_uris @@ -146,8 +146,8 @@ tests: - it: should not create client cr if ozg operator is disabled set: sso: - keycloak_clients: - - client_name: admin + keycloak_client: + client_name: admin disableOzgOperator: true asserts: - hasDocuments: diff --git a/src/test/java/de/ozgcloud/admin/ApiRootITCase.java b/src/test/java/de/ozgcloud/admin/ConfigurationITCase.java similarity index 94% rename from src/test/java/de/ozgcloud/admin/ApiRootITCase.java rename to src/test/java/de/ozgcloud/admin/ConfigurationITCase.java index 327eea8dfbe2120d59233c3891dc807f8882fbae..f3c0c415f3e73aa152eec948f20cf5a47bf9d249 100644 --- a/src/test/java/de/ozgcloud/admin/ApiRootITCase.java +++ b/src/test/java/de/ozgcloud/admin/ConfigurationITCase.java @@ -34,13 +34,14 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import de.ozgcloud.admin.common.user.UserRole; import de.ozgcloud.common.test.ITCase; import lombok.SneakyThrows; @ITCase @AutoConfigureMockMvc -@WithMockUser -class ApiRootITCase { +@WithMockUser(roles = UserRole.ADMIN_ADMIN) +class ConfigurationITCase { @Autowired private RepositoryRestProperties restProperties; @@ -49,7 +50,7 @@ class ApiRootITCase { private MockMvc mockMvc; @Nested - class TestRootEndpoint { + class TestConfigurationRestEndpoint { @Test void shouldBetSetToApi() { diff --git a/src/test/java/de/ozgcloud/admin/RootControllerTest.java b/src/test/java/de/ozgcloud/admin/RootControllerTest.java index f703d1a3c3e3f4ab96c340516a66f519626cf187..9401253fa33c9dd7a7b61a0efc32816dee8ee5c9 100644 --- a/src/test/java/de/ozgcloud/admin/RootControllerTest.java +++ b/src/test/java/de/ozgcloud/admin/RootControllerTest.java @@ -30,11 +30,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.info.BuildProperties; import org.springframework.hateoas.EntityModel; import org.springframework.test.web.servlet.MockMvc; @@ -43,7 +41,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import lombok.SneakyThrows; -@ExtendWith(MockitoExtension.class) class RootControllerTest { @Spy @@ -93,7 +90,7 @@ class RootControllerTest { ResultActions result = doRequest(); - result.andExpect(jsonPath("$.buildVersion").value(RootTestFactory.BUILD_VERSION)); + result.andExpect(jsonPath("$.version").value(RootTestFactory.BUILD_VERSION)); } @Test diff --git a/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java index 6510e8f1187d204a062143130590313f075771d0..9e797f60d83dadc15a280d94e692dc9d191b92d3 100644 --- a/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java +++ b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java @@ -22,26 +22,24 @@ package de.ozgcloud.admin; import static de.ozgcloud.admin.RootModelAssembler.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.*; -import java.util.Optional; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; -import org.springframework.hateoas.EntityModel; -import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; -@ExtendWith(MockitoExtension.class) +import de.ozgcloud.admin.common.user.CurrentUserService; +import de.ozgcloud.admin.common.user.UserRole; + class RootModelAssemblerTest { private static final String BASE_PATH = "/api/base"; @@ -51,34 +49,65 @@ class RootModelAssemblerTest { @Mock private RepositoryRestProperties restProperties; - - @BeforeEach - void mockBasePath() { - when(restProperties.getBasePath()).thenReturn(BASE_PATH); - } + @Mock + private CurrentUserService currentUserService; @DisplayName("Entity Model") @Nested class TestEntityModel { + @BeforeEach + void beforeEach() { + Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(true); + Mockito.when(restProperties.getBasePath()).thenReturn(BASE_PATH); + } @Test - void shouldHaveHrefToBasePath() { - var configurationLink = toModel().getLink(REL_CONFIGURATION); + void shouldHaveRoot() { + var givenRoot = RootTestFactory.create(); + List<Link> links = List.of(); + Mockito.when(modelAssembler.buildRootModelLinks()).thenReturn(links); - assertEquals(Optional.of(Link.of(BASE_PATH, REL_CONFIGURATION)), configurationLink); + var resultRoot = modelAssembler.toModel(givenRoot).getContent(); + + assertThat(resultRoot).isEqualTo(givenRoot); } @Test - void shouldHaveHrefToSelf() { - var selfLink = toModel().getLink(IanaLinkRelations.SELF); + void shouldHaveLinks() { + List<Link> links = List.of(Link.of(RootController.PATH)); + Mockito.when(modelAssembler.buildRootModelLinks()).thenReturn(links); + + var modelLinks = modelAssembler.toModel(RootTestFactory.create()).getLinks(); - assertEquals(Optional.of(Link.of(RootController.PATH)), selfLink); + assertThat(modelLinks).containsAll(links); } + } - private EntityModel<Root> toModel() { - return modelAssembler.toModel(RootTestFactory.create()); + @DisplayName("Root Model Links") + @Nested + class TestBuildRootModelLinks { + + @Test + void shouldHaveHrefToBasePathIfAuthorized() { + Mockito.when(restProperties.getBasePath()).thenReturn(BASE_PATH); + Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(true); + + List<Link> links = modelAssembler.buildRootModelLinks(); + + assertThat(links).containsExactly( + Link.of(RootController.PATH), + Link.of(BASE_PATH, REL_CONFIGURATION)); } + @Test + void shouldNotHaveHrefToBasePathIfUnauthorized() { + Mockito.when(currentUserService.hasRole(UserRole.ADMIN_ADMIN)).thenReturn(false); + + List<Link> links = modelAssembler.buildRootModelLinks(); + + assertThat(links).containsExactly( + Link.of(RootController.PATH)); + } } } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/RootTestFactory.java b/src/test/java/de/ozgcloud/admin/RootTestFactory.java index e4a77201b527061485a7dcf7d567dfa161f73f7b..4c0312d9a9014b860c7ef9739245d5c012b1c61e 100644 --- a/src/test/java/de/ozgcloud/admin/RootTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/RootTestFactory.java @@ -40,7 +40,7 @@ public class RootTestFactory { return Root.builder() .buildTime(BUILD_TIME) .javaVersion(JAVA_VERSION) - .buildVersion(BUILD_VERSION) + .version(BUILD_VERSION) .buildNumber(BUILD_NUMBER); } } diff --git a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..428668577ed0add18a3c5a9fc041e350ea7d97f8 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserHelperTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.admin.common.user; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Collection; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; + +class CurrentUserHelperTest { + @DisplayName("Has role") + @Nested + class TestHasRole { + @Mock + private final Authentication mockAuthentication = Mockito.mock(Authentication.class); + @Mock + private final User mockPrincipal = Mockito.mock(User.class); + + @Test + void shouldReturnFalseOnMissingAuthentication() { + try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic( + CurrentUserHelper.class, + Mockito.CALLS_REAL_METHODS)) { + mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(null); + + boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN); + + assertThat(hasRole).isFalse(); + } + } + + @Test + void shouldReturnFalseOnMissingPrincipal() { + Mockito.when(mockAuthentication.getPrincipal()).thenReturn(null); + try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic( + CurrentUserHelper.class, + Mockito.CALLS_REAL_METHODS)) { + mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(mockAuthentication); + + boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN); + + assertThat(hasRole).isFalse(); + } + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void shouldReturnValue(boolean containsRoleValue) { + Mockito.when(mockAuthentication.getPrincipal()).thenReturn(mockPrincipal); + List<GrantedAuthority> authorities = List.of(); + Mockito.<Collection<? extends GrantedAuthority>>when(mockAuthentication.getAuthorities()).thenReturn(authorities); + + try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic( + CurrentUserHelper.class, + Mockito.CALLS_REAL_METHODS)) { + mockUserHelper.when(CurrentUserHelper::getAuthentication).thenReturn(mockAuthentication); + mockUserHelper.when(() -> CurrentUserHelper.containsRole(Mockito.anyList(), Mockito.anyString())) + .thenReturn(containsRoleValue); + + boolean hasRole = CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN); + + mockUserHelper.verify(() -> CurrentUserHelper.containsRole(mockAuthentication.getAuthorities(), UserRole.ADMIN_ADMIN)); + assertThat(hasRole).isEqualTo(containsRoleValue); + } + } + } + + @DisplayName("Contains role") + @Nested + class TestContainsRole { + @Test + void shouldNotContainRoleIfAuthoritiesIsNull() { + boolean containsRole = CurrentUserHelper.containsRole(null, UserRole.ADMIN_ADMIN); + + assertThat(containsRole).isFalse(); + } + + @Test + void shouldNotContainRole() { + List<GrantedAuthority> authorities = List.of( + new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + "OTHER")); + + boolean containsRole = CurrentUserHelper.containsRole(authorities, UserRole.ADMIN_ADMIN); + + assertThat(containsRole).isFalse(); + } + + @Test + void shouldContainRole() { + Collection<? extends GrantedAuthority> authorities = List.of( + new SimpleGrantedAuthority(CurrentUserHelper.ROLE_PREFIX + UserRole.ADMIN_ADMIN)); + + boolean containsRole = CurrentUserHelper.containsRole(authorities, UserRole.ADMIN_ADMIN); + + assertThat(containsRole).isTrue(); + } + } + + @DisplayName("Add Role Prefix If Missing") + @Nested + class TestAddRolePrefixIfMissing { + + @Test + void shouldAddPrefixIfMissing() { + var roleWithoutPrefix = UserRole.ADMIN_ADMIN; + + var role = CurrentUserHelper.addRolePrefixIfMissing(roleWithoutPrefix); + + assertThat(role).isEqualTo(CurrentUserHelper.ROLE_PREFIX + UserRole.ADMIN_ADMIN); + } + + @Test + void shouldReturnRoleIfPrefixAlreadyExists() { + var roleWithPrefix = CurrentUserHelper.ROLE_PREFIX + UserRole.ADMIN_ADMIN; + + var role = CurrentUserHelper.addRolePrefixIfMissing(roleWithPrefix); + + assertThat(role).isEqualTo(roleWithPrefix); + } + + @Test + void shouldReturnNullIfPassingNull() { + var role = CurrentUserHelper.addRolePrefixIfMissing(null); + + assertThat(role).isNull(); + } + } + + @DisplayName("Get authentication") + @Nested + class TestGetAuthentication { + @Mock + private final SecurityContext mockSecurityContext = Mockito.mock(SecurityContext.class); + + @Test + void shouldThrowIfNoAuthenticatedUser() { + Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(null); + + try (MockedStatic<SecurityContextHolder> contextHolder = Mockito.mockStatic(SecurityContextHolder.class)) { + contextHolder.when(SecurityContextHolder::getContext).thenReturn(mockSecurityContext); + + assertThatIllegalStateException() + .isThrownBy(CurrentUserHelper::getAuthentication) + .withMessage("No authenticated User found"); + } + } + + @Test + void shouldPassAuthentication() { + Authentication mockAuthentication = Mockito.mock(Authentication.class); + Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); + + try (MockedStatic<SecurityContextHolder> contextHolder = Mockito.mockStatic(SecurityContextHolder.class)) { + contextHolder.when(SecurityContextHolder::getContext).thenReturn(mockSecurityContext); + + Authentication authentication = CurrentUserHelper.getAuthentication(); + + assertThat(authentication).isSameAs(mockAuthentication); + } + } + } +} diff --git a/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..878292bc14ffc812739213d84ce34b0fd97a3b4a --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/common/user/CurrentUserServiceTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.admin.common.user; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; + +class CurrentUserServiceTest { + private final CurrentUserService currentUserService = new CurrentUserService(); + + @DisplayName("Has role") + @Nested + class TestHasRole { + @ParameterizedTest + @ValueSource(booleans = {false, true}) + void shouldReturnValue(boolean hasRoleValue) { + try (MockedStatic<CurrentUserHelper> mockUserHelper = Mockito.mockStatic( + CurrentUserHelper.class) + ){ + mockUserHelper.when(() -> CurrentUserHelper.hasRole(Mockito.anyString())) + .thenReturn(hasRoleValue); + + boolean hasRole = currentUserService.hasRole(UserRole.ADMIN_ADMIN); + + mockUserHelper.verify(() -> CurrentUserHelper.hasRole(UserRole.ADMIN_ADMIN)); + assertThat(hasRole).isEqualTo(hasRoleValue); + } + } + } +} diff --git a/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java b/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java index bdeaf83b00de2410112833f1447e940fc088e9b5..1fa9815f84c8421d85cc65035f63ff377d2d0523 100644 --- a/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java +++ b/src/test/java/de/ozgcloud/admin/environment/EnvironmentControllerTest.java @@ -7,13 +7,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -21,7 +19,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.ozgcloud.admin.RootController; import lombok.SneakyThrows; -@ExtendWith(MockitoExtension.class) class EnvironmentControllerTest { @Spy diff --git a/src/test/java/de/ozgcloud/admin/security/AdminAuthenticationEntryPointTest.java b/src/test/java/de/ozgcloud/admin/security/AdminAuthenticationEntryPointTest.java index cd032501d67fa6a4bd3ce55d2082bc7eca1d8277..3b582efa9b75efeba55009589eb2754747abb044 100644 --- a/src/test/java/de/ozgcloud/admin/security/AdminAuthenticationEntryPointTest.java +++ b/src/test/java/de/ozgcloud/admin/security/AdminAuthenticationEntryPointTest.java @@ -35,13 +35,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -53,8 +51,7 @@ import org.springframework.web.servlet.function.ServerResponse.Context; import lombok.SneakyThrows; -@ExtendWith(MockitoExtension.class) -public class AdminAuthenticationEntryPointTest { +class AdminAuthenticationEntryPointTest { private static final String REQUEST_URI = "/request-uri"; diff --git a/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java b/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java index 3727bfc0c968540bafa8abe9dffdb6df9add2972..daf3d634ab66c34ea6f6e393d92681b69ed69b59 100644 --- a/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java @@ -31,6 +31,7 @@ public class AuthenticationExceptionTestFactory { @Builder public static class DummyAuthenticationException extends AuthenticationException { + @SuppressWarnings("unused") private String msg; DummyAuthenticationException(String msg) { diff --git a/src/test/java/de/ozgcloud/admin/security/JwtTestFactory.java b/src/test/java/de/ozgcloud/admin/security/JwtTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..719c94afe86b01a9f87d4b0bbf1f51a40fcdaa75 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/security/JwtTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.security; + +import static de.ozgcloud.admin.security.SecurityConfiguration.*; + +import java.util.List; +import java.util.Map; + +import org.springframework.security.oauth2.jwt.Jwt; + +public class JwtTestFactory { + + public static final String ROLE_1 = "ADMIN_ADMIN"; + public static final String ROLE_2 = "Lower_case"; + public static final String ROLE_3 = "UPPER"; + + public static final String AUTH_RESOURCE = "admin"; + + public static Jwt create() { + return createBuilder().build(); + } + + public static Jwt.Builder createWithRoles(List<String> roles) { + return createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of(AUTH_RESOURCE, Map.of(ROLES_KEY, roles))); + } + + public static Jwt.Builder createBuilder() { + return Jwt.withTokenValue("token-value").header("header-key", "header-value").claim("claim-key", "claim-value"); + } + +} diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java index d8bf3dcae4e6b88a078fa58c860d8365bb77ae60..711a2d8806c5c79b96082073c7bfb09bc7294d76 100644 --- a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java +++ b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationITCase.java @@ -46,9 +46,9 @@ class SecurityConfigurationITCase { @Autowired private MockMvc mockMvc; - @DisplayName("without authorization") + @DisplayName("without authentication") @Nested - class TestWithoutAuthorization { + class TestWithoutAuthentication { @DisplayName("allow for not found") @SneakyThrows @@ -132,33 +132,76 @@ class SecurityConfigurationITCase { } } - @DisplayName("with authorization") + @DisplayName("with authentication") @Nested - class TestWithAuthorization { - + class TestWithAuthentication { static final String CLAIMS = """ { "preferredUsername": "testUser", "scope": "openid testscope" }"""; + @Test @SneakyThrows - @ParameterizedTest - @ValueSource(strings = { - "/api/environment", - "/configserver/name/profile", - "/api", "/api/configuration", "/api/configuration/settings", - }) @WithJwt(CLAIMS) - void shouldAllow(String path) { - var result = doPerformAuthenticated(path); + void shouldAllowApiEndpoint() { + var result = doPerformAuthenticated("/api"); result.andExpect(status().isOk()); } + @Test + @SneakyThrows + @WithJwt(CLAIMS) + void shouldForbidSettingsEndpoint() { + var result = doPerformAuthenticated("/api/configuration/settings"); + + result.andExpect(status().isForbidden()); + } + + @Test + @SneakyThrows + @WithJwt(CLAIMS) + void shouldForbidConfigurationsEndpoint() { + var result = doPerformAuthenticated("/api/configuration"); + + result.andExpect(status().isForbidden()); + } + @SneakyThrows private ResultActions doPerformAuthenticated(String path) { return mockMvc.perform(get(path)); } } + + @DisplayName("with admin role") + @Nested + class TestWithAdminRole { + + static final String CLAIMS = """ + { + "preferredUsername": "testUser", + "scope": "openid testscope", + "resource_access": { "admin": { "roles": ["ADMIN_ADMIN"] } } + }"""; + + + @Test + @SneakyThrows + @WithJwt(CLAIMS) + void shouldAllowSettings() { + var result = mockMvc.perform(get("/api/configuration/settings")); + + result.andExpect(status().isOk()); + } + + @Test + @SneakyThrows + @WithJwt(CLAIMS) + void shouldAllowConfiguration() { + var result = mockMvc.perform(get("/api/configuration")); + + result.andExpect(status().isOk()); + } + } } diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9acdae93189e5d6e25d65361c78929584da935f7 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.security; + +import static de.ozgcloud.admin.security.JwtTestFactory.*; +import static de.ozgcloud.admin.security.SecurityConfiguration.*; +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import org.springframework.security.oauth2.jwt.Jwt; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.admin.environment.OAuth2Properties; + +class SecurityConfigurationTest { + + @Spy + @InjectMocks + private SecurityConfiguration securityConfiguration; + + @Mock + private OAuth2Properties oAuth2Properties; + + @DisplayName("jwt authentication converter") + @Nested + class TestJwtAuthenticationConverter { + + private final String roleString = "ROLE_Test"; + + @BeforeEach + void mock() { + doReturn(Set.of(new SimpleGrantedAuthority(roleString))).when(securityConfiguration).convertJwtToGrantedAuthorities(any()); + } + + @DisplayName("should use preferred_username") + @Test + void shouldUsePreferredUsername() { + var preferredName = LoremIpsum.getInstance().getName(); + var jwtWithPreferredName = JwtTestFactory.createBuilder() + .claim(StandardClaimNames.PREFERRED_USERNAME, preferredName) + .build(); + + var jwtAuthenticationConverter = securityConfiguration.jwtAuthenticationConverter(); + + var abstractAuthenticationToken = jwtAuthenticationConverter.convert(jwtWithPreferredName); + assertThat(abstractAuthenticationToken.getName()).isEqualTo(preferredName); + } + + @DisplayName("should use granted authorities converter") + @Test + void shouldUseGrantedAuthoritiesConverter() { + var jwtWithRoles = JwtTestFactory.create(); + + var jwtAuthenticationConverter = securityConfiguration.jwtAuthenticationConverter(); + + var abstractAuthenticationToken = jwtAuthenticationConverter.convert(jwtWithRoles); + var securityRoleStrings = abstractAuthenticationToken.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList(); + + assertThat(securityRoleStrings).isEqualTo(List.of(roleString)); + } + } + + @DisplayName("convert jwt to granted authorities") + @Nested + class TestConvertJwtToGrantedAuthorities { + + private List<String> expectedSecurityRoleStrings; + + @BeforeEach + void mock() { + var keycloakRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3); + expectedSecurityRoleStrings = keycloakRoles.stream().map(role -> SIMPLE_GRANT_AUTHORITY_PREFIX + role).toList(); + doReturn(keycloakRoles).when(securityConfiguration).getRolesFromJwt(any()); + } + + @DisplayName("should call get keycloak roles from jwt") + @Test + void shouldCallGetKeycloakRolesFromJwt() { + var jwt = JwtTestFactory.create(); + + securityConfiguration.convertJwtToGrantedAuthorities(jwt); + + verify(securityConfiguration).getRolesFromJwt(jwt); + } + + @DisplayName("should return granted authorities with ROLE_ prefix") + @Test + void shouldReturnGrantedAuthoritiesWithRolePrefix() { + var jwt = JwtTestFactory.create(); + + var grantedAuthorities = securityConfiguration.convertJwtToGrantedAuthorities(jwt); + + var securityRoles = grantedAuthorities + .stream() + .map(GrantedAuthority::getAuthority).toList(); + assertThat(securityRoles).containsAll(expectedSecurityRoleStrings); + } + } + + @DisplayName("get roles from jwt") + @Nested + class TestGetRolesFromJwt { + + @BeforeEach + void mock() { + lenient().when(oAuth2Properties.getResource()).thenReturn(JwtTestFactory.AUTH_RESOURCE); + } + + @DisplayName("should return empty list if resource_access.admin.roles path is missing") + @ParameterizedTest + @MethodSource("getIncompleteJwt") + void shouldReturnEmptyListIfResourceAccessAdminRolesPathIsMissing(Jwt incompleteJwt) { + var roleStrings = securityConfiguration.getRolesFromJwt(incompleteJwt); + + assertThat(roleStrings).isEmpty(); + } + + private static Stream<Arguments> getIncompleteJwt() { + return Stream.of(JwtTestFactory.create(), + JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of()).build(), + JwtTestFactory.createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of("admin", Map.of())).build(), + JwtTestFactory.createWithRoles(emptyList()).build()) + .map(Arguments::of); + } + + @DisplayName("should return resource_access.admin.roles list") + @Test + void shouldReturnResourceAccessAdminRolesList() { + var expectedRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3); + var jwtWithRoles = JwtTestFactory.createWithRoles(expectedRoles).build(); + + var roleStrings = securityConfiguration.getRolesFromJwt(jwtWithRoles); + + assertThat(roleStrings).isEqualTo(expectedRoles); + } + + } +} diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java deleted file mode 100644 index e336b21c6bde4f0904228557051a411c687276a9..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright - * (C) 2024 Das Land Schleswig-Holstein vertreten durch das - * Minis - * erium für Energiewende, Klimaschutz, Umwelt und Natur Zentrales - * IT-Management - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald diese von der - * Europäischen Kommission genehmigt wurden - Folgeversionen der EUPL - * - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften gefordert oder in - * schriftlicher Form vereinbart, wird die unter der Lizenz verbreitete Software - * "so wie sie ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. Die sprachspezifischen - * Genehmigungen und Beschränkungen unter der Lizenz sind dem Lizenztext zu - * entnehmen. - */ -package de.ozgcloud.admin.security; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.net.URI; -import java.util.Collections; -import java.util.Map; - -import org.apache.http.client.utils.URIBuilder; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.http.MediaType; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClient; - -import dasniko.testcontainers.keycloak.KeycloakContainer; -import de.ozgcloud.admin.RootController; -import de.ozgcloud.common.test.DataITCase; -import lombok.SneakyThrows; - -@DataITCase -@AutoConfigureMockMvc -class SecurityConfigurationWithKeycloakITCase { - @Autowired - private MockMvc mockMvc; - - static KeycloakContainer keycloak; - - @BeforeAll - static void setupKeycloakContainer() { - keycloak = new KeycloakContainer().withRealmImportFile("keycloak/realm-export.json"); - keycloak.start(); - } - - @AfterAll - static void closeKeycloakContainer() { - keycloak.close(); - } - - @DynamicPropertySource - static void registerResourceServerIssuerProperty(DynamicPropertyRegistry registry) { - registry.add("spring.security.oauth2.resourceserver.jwt.issuer-uri", () -> keycloak.getAuthServerUrl() + "/realms/by-kiel-dev"); - } - - @Nested - class TestSecuredEndpointWithKeycloakToken { - @SneakyThrows - @ParameterizedTest - @ValueSource(strings = { - "/api/environment", - "/configserver/name/profile", - "/api", "/api/configuration", "/api/configuration/param", - }) - void shouldGetAccessWithToken() { - String token = getToken(); - - var result = mockMvc.perform(get(RootController.PATH).header("Authorization", token)); - - result.andExpect(status().isOk()); - } - - @SneakyThrows - String getToken() { - MultiValueMap<String, String> formData = setPostBodyForToken(); - - Map<String, String> resultBody = performPostRequestToKeycloak(formData); - - return "Bearer " + resultBody.get("access_token").toString(); - } - - MultiValueMap<String, String> setPostBodyForToken() { - MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); - formData.put("grant_type", Collections.singletonList("password")); - formData.put("client_id", Collections.singletonList("admin")); - formData.put("username", Collections.singletonList("admin-test")); - formData.put("password", Collections.singletonList("Password")); - return formData; - } - - @SuppressWarnings("unchecked") - @SneakyThrows - Map<String, String> performPostRequestToKeycloak(MultiValueMap<String, String> formData) { - RestClient restClient = RestClient.create(); - URI authorizationURI = new URIBuilder(keycloak.getAuthServerUrl() + "/realms/by-kiel-dev/protocol/openid-connect/token").build(); - var response = restClient.post().uri(authorizationURI) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body(formData); - return response.retrieve().body(Map.class); - - } - - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/setting/AlfaSettingDTOTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/AlfaSettingDTOTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b00c52b64f139c94e90a101aba3bdb8c66df6313 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/AlfaSettingDTOTestFactory.java @@ -0,0 +1,13 @@ +package de.ozgcloud.admin.setting; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; + +public class AlfaSettingDTOTestFactory { + public static AlfaSettingDTO create() { + return createBuilder().build(); + } + + public static AlfaSettingDTO.AlfaSettingDTOBuilder createBuilder() { + return AlfaSettingDTO.builder().signatur(PostfachSettingBodyTestFactory.SIGNATUR); + } +} diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..0f8ca42b3cd6517c895bb9da385bd49c200b3c5e --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentITCase.java @@ -0,0 +1,69 @@ + +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; +import de.ozgcloud.common.test.DataITCase; +import lombok.SneakyThrows; + +@DataITCase +@AutoConfigureMockMvc +@WithMockUser +class SettingEnvironmentITCase { + @Autowired + private MockMvc mockMvc; + + @Autowired + private SettingRepository settingRepository; + + private Setting settingWithPostfach = SettingTestFactory.createBuilder() + .name("Postfach") + .settingBody(PostfachSettingBodyTestFactory.create()) + .build(); + + @Test + @SneakyThrows + void shouldHaveHttpEndpoint() { + var result = mockMvc.perform(get("/configserver/example/path")); + + result.andExpect(status().isOk()); + } + + @Nested + class TestFindOne { + @BeforeEach + void fillDb() { + settingRepository.deleteAll(); + settingRepository.save(settingWithPostfach); + + } + + @Test + @SneakyThrows + void shouldReturnValuesForVorgangManager() { + var result = mockMvc.perform(get("/configserver/OzgCloud_VorgangManager-profile.yaml")); + + assertThat(result.andReturn().getResponse().getContentAsString()).isEqualTo(YamlTestFactory.createVorgangManagerYaml()); + } + + @Test + @SneakyThrows + void shouldReturnValuesForAlfa() { + var result = mockMvc.perform(get("/configserver/Alfa-any.yaml")); + + assertThat(result.andReturn().getResponse().getContentAsString().replaceAll("\\s+", " ")) + .isEqualTo(YamlTestFactory.createAlfaYaml().replaceAll("\\s+", " ")); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentRepositoryTest.java b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentRepositoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4bb93d6f696c096c9b537ac4943c41539031aaff --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingEnvironmentRepositoryTest.java @@ -0,0 +1,201 @@ +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.environment.PropertySource; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.Getter; + +class SettingEnvironmentRepositoryTest { + @InjectMocks + @Spy + private SettingEnvironmentRepository repository; + @Spy + private ObjectMapper objectMapper; + @Mock + private SettingService service; + @Mock + private ApplicationSettingDTO mockedSettingDTO; + @Mock + private Environment mockedEnvironment; + @Mock + private PropertySource mockedPropertySource; + + private static final String ALFA = "Alfa"; + private static final String VORGANG_MANAGER = "OzgCloud_VorgangManager"; + + @Nested + class TestFindOne { + private static final String ANY_APPLICATION = "any application"; + + @Test + void shouldCallGetAnwendungsSettingDTO() { + repository.findOne(ANY_APPLICATION, "any profile", "any label"); + + verify(repository).findAnwendungSettingDTO(ANY_APPLICATION); + + } + + @Test + void shouldCallBuildEnvironment() { + doReturn(Optional.of(mockedSettingDTO)).when(repository).findAnwendungSettingDTO(any()); + doReturn(mockedEnvironment).when(repository).buildEnvironment(any(), any()); + + repository.findOne(ANY_APPLICATION, "any profile", "any label"); + + verify(repository).buildEnvironment(ANY_APPLICATION, Optional.of(mockedSettingDTO)); + } + + @Test + void shouldReturnBuiltEnvironment() { + doReturn(Optional.of(mockedSettingDTO)).when(repository).findAnwendungSettingDTO(any()); + doReturn(mockedEnvironment).when(repository).buildEnvironment(any(), any()); + + var returnedEnvironment = repository.findOne(ANY_APPLICATION, "any profile", "any label"); + + assertThat(returnedEnvironment).isEqualTo(mockedEnvironment); + } + + } + + @Nested + class TestFindAnwendungsSettingDTO { + + @Test + void shouldReturnEmptyOptionalForUnsupportedApplication() { + var returnedSettingDTO = repository.findAnwendungSettingDTO("UnsupportedApp"); + + assertThat(returnedSettingDTO).isEmpty(); + } + + @Nested + class TestWithAlfaApplication { + @Mock + private AlfaSettingDTO mockedAlfaSettingDTO; + + @BeforeEach + void mockService() { + when(service.getAlfaSettingDTO()).thenReturn(mockedAlfaSettingDTO); + } + + @Test + void shouldOnlyCallGetAlfaSettingDTO() { + repository.findAnwendungSettingDTO(ALFA); + + verify(service).getAlfaSettingDTO(); + verify(service, never()).getVorgangManagerSettingDTO(); + } + + @Test + void shouldReturnAlfaSettingDTO() { + var returnedSettingDTO = repository.findAnwendungSettingDTO(ALFA); + + assertThat(returnedSettingDTO).hasValue(mockedAlfaSettingDTO); + } + } + + @Nested + class TestWithVorgangManagerApplication { + @Mock + private VorgangManagerSettingDTO mockedVorgangManagerSettingDTO; + + @BeforeEach + void mockService() { + when(service.getVorgangManagerSettingDTO()).thenReturn(mockedVorgangManagerSettingDTO); + } + + @Test + void shouldOnlyCallGetVorgangManagerSettingDTO() { + repository.findAnwendungSettingDTO(VORGANG_MANAGER); + + verify(service, never()).getAlfaSettingDTO(); + verify(service).getVorgangManagerSettingDTO(); + } + + @Test + void shouldReturnAlfaSettingDTO() { + var returnedSettingDTO = repository.findAnwendungSettingDTO(VORGANG_MANAGER); + + assertThat(returnedSettingDTO).hasValue(mockedVorgangManagerSettingDTO); + } + } + } + + @Nested + class TestBuildEnvironment { + private static final String APPLICATION_NAME = "app"; + + @BeforeEach + void mockTransformToPropertySource() { + doReturn(mockedPropertySource).when(repository).transformToPropertySource(mockedSettingDTO); + } + + @Test + void shouldCallTransformToPropertySource() { + repository.buildEnvironment(APPLICATION_NAME, Optional.of(mockedSettingDTO)); + + verify(repository).transformToPropertySource(mockedSettingDTO); + } + + @Test + void shouldReturnBuiltEnvironment() { + var expectedEnvironemnt = new Environment(APPLICATION_NAME); + expectedEnvironemnt.add(mockedPropertySource); + + var resultEnvironment = repository.buildEnvironment(APPLICATION_NAME, Optional.of(mockedSettingDTO)); + + assertThat(resultEnvironment).usingRecursiveComparison().isEqualTo(expectedEnvironemnt); + } + } + + @Nested + class TestTransformToPropertySource { + private ApplicationSettingDTO flatSettingDTO = new ApplicationSettingDTO() { + @Getter + private String attribute = "outerValue"; + }; + + private ApplicationSettingDTO nestedSettingDTO = new ApplicationSettingDTO() { + @JsonUnwrapped(prefix = "attribute.") + @Getter + private ApplicationSettingDTO attribute = new ApplicationSettingDTO() { + @Getter + private String nestedAttribute = "innerValue"; + }; + }; + + @Test + void shouldReturnPropertySourceOfFlatObject() { + var expectedPropertySource = new PropertySource(StringUtils.EMPTY, Map.of("attribute", "outerValue")); + + var resultPropertySource = repository.transformToPropertySource(flatSettingDTO); + + assertThat(resultPropertySource).usingRecursiveComparison().isEqualTo(expectedPropertySource); + } + + @Test + void shouldReturnPropertySourceOfNestedObject() { + var expectedPropertySource = new PropertySource(StringUtils.EMPTY, Map.of("attribute.nestedAttribute", "innerValue")); + + var resultPropertySource = repository.transformToPropertySource(nestedSettingDTO); + + assertThat(resultPropertySource).usingRecursiveComparison().isEqualTo(expectedPropertySource); + } + } +} diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingITCase.java b/src/test/java/de/ozgcloud/admin/setting/SettingITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..04aaf44fb4105118d90343fc2271e58479959173 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingITCase.java @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import de.ozgcloud.admin.common.user.UserRole; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.admin.setting.postfach.Absender; +import de.ozgcloud.admin.setting.postfach.AbsenderTestFactory; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; +import de.ozgcloud.common.test.DataITCase; +import lombok.SneakyThrows; + +@DataITCase +@AutoConfigureMockMvc +@WithMockUser(roles = UserRole.ADMIN_ADMIN) +class SettingITCase { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private MongoOperations mongoOperations; + + @Autowired + private RepositoryRestProperties restProperties; + + @Nested + class TestRestRepo { + + private String id; + + @BeforeEach + void init() { + mongoOperations.dropCollection(Setting.class); + id = mongoOperations.save(SettingTestFactory.create()).getId(); + } + + @Nested + class TestGet { + + @Test + @SneakyThrows + void shouldHaveStatusOkForExisting() { + var result = performGet(id); + + result.andExpect(status().isOk()); + } + + @Test + @SneakyThrows + void shouldHaveStatusNotFoundForNonExisting() { + var result = performGet("unknown"); + + result.andExpect(status().is(HttpStatus.NOT_FOUND.value())); + } + + @SneakyThrows + private ResultActions performGet(String id) { + return mockMvc.perform(get(String.join("/", restProperties.getBasePath(), SettingConstants.PATH, id))); + } + } + } + + @Nested + class TestForSettingWithPostfach { + private static final String POSTFACH_NAME = "Postfach"; + + private final Setting settingWithPostfach = SettingTestFactory.createBuilder() + .name(POSTFACH_NAME) + .settingBody(PostfachSettingBodyTestFactory.create()) + .build(); + + @BeforeEach + void clear() { + mongoOperations.dropCollection(Setting.class); + } + + @Nested + class TestSave { + + @Test + @SneakyThrows + void shouldHaveResponseStatusCreated() { + var result = performPost(settingWithPostfach); + + result.andExpect(status().isCreated()); + } + + @Test + @SneakyThrows + void shouldCreateSettingItem() { + performPost(settingWithPostfach); + + assertThat(getSettingWithPostfachFromDb()) + .usingRecursiveComparison().ignoringFields("id").isEqualTo(settingWithPostfach); + } + + @Nested + class TestPostfachSetting { + + @Test + @SneakyThrows + void shouldBeInstanceOfPostfach() { + performPost(settingWithPostfach); + + assertThat(getSettingWithPostfachFromDb() + .getSettingBody()).isInstanceOf(PostfachSettingBody.class); + } + + @Test + @SneakyThrows + void shouldCreateEmptySignatur() { + var postfachWithEmptySignatur = PostfachSettingBodyTestFactory.createBuilder() + .signatur(StringUtils.EMPTY).build(); + var settingWithPostfachEmptySignatur = createSettingWithPostfach(postfachWithEmptySignatur); + + performPost(settingWithPostfachEmptySignatur); + + assertThat(getPostfachFromDb().getSignatur()).isEmpty(); + } + + private PostfachSettingBody getPostfachFromDb() { + return (PostfachSettingBody) getSettingWithPostfachFromDb().getSettingBody(); + } + + @Nested + class TestAbsenderValidation { + @Test + @SneakyThrows + void shouldReturnUnprocessableEntityOnEmptyName() { + var absenderWithEmptyName = AbsenderTestFactory.createBuilder().name(StringUtils.EMPTY).build(); + var settingWithPostfachEmptyAbsenderName = createSettingWithPostfachAbsender(absenderWithEmptyName); + + var result = performPost(settingWithPostfachEmptyAbsenderName); + + result.andExpect(status().isUnprocessableEntity()); + } + + @Test + @SneakyThrows + void shouldReturnUnprocessableEntityOnEmptyAnschrift() { + var absenderWithEmptyAnschrift = AbsenderTestFactory.createBuilder().anschrift(StringUtils.EMPTY).build(); + var settingWithPostfachEmptyAnschrift = createSettingWithPostfachAbsender(absenderWithEmptyAnschrift); + + var result = performPost(settingWithPostfachEmptyAnschrift); + + result.andExpect(status().isUnprocessableEntity()); + } + + @Test + @SneakyThrows + void shouldReturnUnprocessableEntityOnEmptyDienst() { + var absenderWithEmptyDienst = AbsenderTestFactory.createBuilder().dienst(StringUtils.EMPTY).build(); + var settingWithPostfachEmptyDienst = createSettingWithPostfachAbsender(absenderWithEmptyDienst); + + var result = performPost(settingWithPostfachEmptyDienst); + + result.andExpect(status().isUnprocessableEntity()); + } + + @Test + @SneakyThrows + void shouldReturnUnprocessableEntityOnEmptyMandant() { + var absenderWithEmptyMandant = AbsenderTestFactory.createBuilder().mandant(StringUtils.EMPTY).build(); + var settingWithPostfachEmptyMandant = createSettingWithPostfachAbsender(absenderWithEmptyMandant); + + var result = performPost(settingWithPostfachEmptyMandant); + + result.andExpect(status().isUnprocessableEntity()); + } + + @Test + @SneakyThrows + void shouldReturnUnprocessableEntityOnEmptyGemeindeSchluessel() { + var absenderWithEmptyGemeindeschluessel = AbsenderTestFactory.createBuilder().gemeindeschluessel(StringUtils.EMPTY).build(); + var settingWithPostfachEmptyGemeindeschluessel = createSettingWithPostfachAbsender(absenderWithEmptyGemeindeschluessel); + + var result = performPost(settingWithPostfachEmptyGemeindeschluessel); + + result.andExpect(status().isUnprocessableEntity()); + } + + private Setting createSettingWithPostfachAbsender(Absender absender) { + var postfach = PostfachSettingBodyTestFactory.createBuilder().absender(absender).build(); + return createSettingWithPostfach(postfach); + } + + } + + private Setting createSettingWithPostfach(PostfachSettingBody postfach) { + return SettingTestFactory.createBuilder().name(POSTFACH_NAME).settingBody(postfach).build(); + } + } + + @SneakyThrows + private ResultActions performPost(Setting setting) { + var postBody = convertSettingToString(setting); + return mockMvc.perform(post(String.join("/", restProperties.getBasePath(), + SettingConstants.PATH)) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(postBody)); + } + } + + @Nested + class TestGet { + private String id; + @Autowired + private ObjectMapper mapper; + + @BeforeEach + void init() { + id = mongoOperations.save(settingWithPostfach).getId(); + } + + @Test + @SneakyThrows + void shouldHaveStatusOkForExisting() { + var result = performGet(); + + result.andExpect(status().isOk()); + } + + @Test + @SneakyThrows + void shouldHaveInstanceOfPostfach() { + + var result = performGet(); + + assertThat(mapper.readValue(result.andReturn().getResponse().getContentAsString(), Setting.class).getSettingBody()) + .isInstanceOf(PostfachSettingBody.class); + } + + @SneakyThrows + private ResultActions performGet() { + return mockMvc.perform(get(String.join("/", restProperties.getBasePath(), SettingConstants.PATH, id))); + } + } + + @Nested + class TestPut { + private String id; + private final PostfachSettingBody updatedPostfach = PostfachSettingBodyTestFactory.createBuilder() + .absender(AbsenderTestFactory.createBuilder() + .name("Neuer Name") + .anschrift("Neue Anschrift") + .build()) + .build(); + private final Setting updatedSetting = SettingTestFactory.createBuilder() + .name(POSTFACH_NAME) + .settingBody(updatedPostfach) + .build(); + + @BeforeEach + void init() { + id = mongoOperations.save(settingWithPostfach).getId(); + } + + @Test + @SneakyThrows + void shouldHaveStatusNoContent() { + var result = performPut(); + + result.andExpect(status().isNoContent()); + } + + @Test + @SneakyThrows + void shouldHaveUpdatedSetting() { + performPut(); + + assertThat(getSettingWithPostfachFromDb()) + .usingRecursiveComparison().ignoringFields("id").isEqualTo(updatedSetting); + } + + @Test + @SneakyThrows + void shouldHaveExactlyOnePostfachSetting() { + performPut(); + + List<Setting> postfachSettings = mongoOperations.find(createQueryForPostfach(), Setting.class); + + assertThat(postfachSettings).hasSize(1); + } + + @SneakyThrows + private ResultActions performPut() { + var body = convertSettingToString(updatedSetting); + return mockMvc.perform(put(String.join("/", restProperties.getBasePath(), SettingConstants.PATH, id)) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(body)); + } + + } + + private Setting getSettingWithPostfachFromDb() { + return mongoOperations.findOne(createQueryForPostfach(), Setting.class); + } + + private Query createQueryForPostfach() { + return new Query().addCriteria(Criteria.where("name").in(POSTFACH_NAME)); + } + + @SneakyThrows + private String convertSettingToString(Setting setting) { + return SettingTestFactory.buildSettingJson(setting, + PostfachSettingBodyTestFactory.buildPostfachJson((PostfachSettingBody) setting.getSettingBody())); + } + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingMapperTest.java b/src/test/java/de/ozgcloud/admin/setting/SettingMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d19e81a2dc480db8bfc2e8d6e1daefc7e001392c --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingMapperTest.java @@ -0,0 +1,41 @@ +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.Spy; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; + +class SettingMapperTest { + private final PostfachSettingBody postfach = PostfachSettingBodyTestFactory.create(); + private final AlfaSettingDTO alfaSettingDTO = AlfaSettingDTOTestFactory.create(); + private final VorgangManagerSettingDTO vorgangManagerSettingDTO = VorgangManagerSettingDTOTestFactory.create(); + + @Spy + private final SettingMapper mapper = Mappers.getMapper(SettingMapper.class); + + @Nested + class TestMapPostfachToAlfaSetting { + @Test + void shouldReturnFlattenedAlfaMap() { + var resultAlfaSetting = mapper.mapAlfaPostfach(AlfaSettingDTO.builder(), postfach).build(); + + assertThat(resultAlfaSetting).usingRecursiveComparison().isEqualTo(alfaSettingDTO); + } + } + + @Nested + class TestMapPostfachToVorgangManagerSetting { + @Test + void shouldReturnFlattenedVorgangManagerMap() { + var resultVorgangmanagerSetting = mapper.mapVorgangManagerPostfach(VorgangManagerSettingDTO.builder(), postfach).build(); + + assertThat(resultVorgangmanagerSetting).usingRecursiveComparison().isEqualTo(vorgangManagerSettingDTO); + } + } + +} diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java b/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..f255693e6e13a86d8add0dc4ab326eadfbc250d6 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingRepositoryITCase.java @@ -0,0 +1,36 @@ +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoOperations; + +import de.ozgcloud.common.test.DataITCase; + +@DataITCase +class SettingRepositoryITCase { + @Autowired + private MongoOperations mongoOperations; + @Autowired + private SettingRepository repository; + + private final static String SETTING_NAME = "Name"; + private Setting setting = SettingTestFactory.createBuilder() + .name(SETTING_NAME) + .build(); + + @Nested + class TestFindOneByName { + @Test + void shouldGetSavedData() { + mongoOperations.dropCollection(Setting.COLLECTION_NAME); + mongoOperations.save(setting); + + var settingFromDb = repository.findOneByName(SETTING_NAME).get(); + + assertThat(settingFromDb).usingRecursiveComparison().isEqualTo(setting); + } + } +} diff --git a/src/test/java/de/ozgcloud/admin/setting/SettingServiceTest.java b/src/test/java/de/ozgcloud/admin/setting/SettingServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00af130bc3b191d9c1ff8b02af5812c628922662 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/SettingServiceTest.java @@ -0,0 +1,150 @@ +package de.ozgcloud.admin.setting; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; + +class SettingServiceTest { + + @InjectMocks + @Spy + private SettingService service; + + @Mock + private SettingRepository repository; + + @Mock + private SettingMapper mapper; + + private final PostfachSettingBody postfach = PostfachSettingBodyTestFactory.create(); + + private static final String POSTFACH = "Postfach"; + private final Setting settingWithPostfach = SettingTestFactory.createBuilder().name(POSTFACH).settingBody(postfach).build(); + + @Nested + class TestGetAlfaSettingDTO { + @Captor + private ArgumentCaptor<AlfaSettingDTO.AlfaSettingDTOBuilder> alfaBuilderCaptor; + + private final AlfaSettingDTO alfaSetting = AlfaSettingDTOTestFactory.create(); + + @BeforeEach + void mockGetSettingWithPostfachFromDb() { + doReturn(postfach).when(service).getSettingWithPostfachFromDb(); + } + + @Test + void shouldCallGetSettingWithPostfachFromDb() { + when(mapper.mapAlfaPostfach(any(), any())).thenReturn(AlfaSettingDTOTestFactory.createBuilder()); + + service.getAlfaSettingDTO(); + + verify(service).getSettingWithPostfachFromDb(); + } + + @Test + void shouldCallMapperWithEmptyBuilderAndPostfach() { + when(mapper.mapAlfaPostfach(any(), any())).thenReturn(AlfaSettingDTOTestFactory.createBuilder()); + + service.getAlfaSettingDTO(); + + verify(mapper).mapAlfaPostfach(alfaBuilderCaptor.capture(), eq(postfach)); + assertThat(alfaBuilderCaptor.getValue()).usingRecursiveComparison().isEqualTo(AlfaSettingDTO.builder()); + } + + @Test + void shouldReturnBuiltAlfaSettingDTO() { + when(mapper.mapAlfaPostfach(any(), any())).thenReturn(AlfaSettingDTOTestFactory.createBuilder()); + + var resultSettingDTO = service.getAlfaSettingDTO(); + + assertThat(resultSettingDTO).usingRecursiveComparison().isEqualTo(alfaSetting); + } + } + + @Nested + class TestGetVorgangManagerSettingDTO { + @Captor + private ArgumentCaptor<VorgangManagerSettingDTO.VorgangManagerSettingDTOBuilder> vorgangManagerBuilderCaptor; + + private final VorgangManagerSettingDTO vorgangManagerSetting = VorgangManagerSettingDTOTestFactory.create(); + + @BeforeEach + void mockGetSettingWithPostfachFromDb() { + doReturn(postfach).when(service).getSettingWithPostfachFromDb(); + } + + @BeforeEach + void mockMapper() { + when(mapper.mapVorgangManagerPostfach(any(), any())).thenReturn(VorgangManagerSettingDTOTestFactory.createBuilder()); + } + + @Test + void shouldCallGetSettingWithPostfachFromDb() { + service.getVorgangManagerSettingDTO(); + + verify(service).getSettingWithPostfachFromDb(); + } + + @Test + void shouldCallMapperWithEmptyBuilderAndPostfach() { + service.getVorgangManagerSettingDTO(); + + verify(mapper).mapVorgangManagerPostfach(vorgangManagerBuilderCaptor.capture(), eq(postfach)); + assertThat(vorgangManagerBuilderCaptor.getValue()).usingRecursiveComparison().isEqualTo(VorgangManagerSettingDTO.builder()); + } + + @Test + void shouldReturnBuiltVorgangManagerSettingDTO() { + var resultSettingDTO = service.getVorgangManagerSettingDTO(); + + assertThat(resultSettingDTO).usingRecursiveComparison().isEqualTo(vorgangManagerSetting); + } + + } + + @Nested + class TestGetSettingWithPostfachFromDb { + @Test + void shouldCallRepository() { + when(repository.findOneByName(POSTFACH)).thenReturn(Optional.of(settingWithPostfach)); + + service.getSettingWithPostfachFromDb(); + + verify(repository).findOneByName(POSTFACH); + } + + @Test + void shouldReturnPostfachSettingBody() { + when(repository.findOneByName(POSTFACH)).thenReturn(Optional.of(settingWithPostfach)); + + var returnedBody = service.getSettingWithPostfachFromDb(); + + assertThat(returnedBody).isEqualTo(postfach); + } + + @Test + void shouldReturnEmptyPostfachSettingBodyForEmptySetting() { + when(repository.findOneByName(POSTFACH)).thenReturn(Optional.empty()); + + var returnedBody = service.getSettingWithPostfachFromDb(); + + assertThat(returnedBody).usingRecursiveComparison().isEqualTo(PostfachSettingBody.builder().build()); + } + } + +} diff --git a/src/test/java/de/ozgcloud/admin/settings/SettingsTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java similarity index 75% rename from src/test/java/de/ozgcloud/admin/settings/SettingsTestFactory.java rename to src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java index ddf73db41640f52d74bd08155f4415581278cffa..023102f84527913302dd14d68acab990f0849bb0 100644 --- a/src/test/java/de/ozgcloud/admin/settings/SettingsTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/setting/SettingTestFactory.java @@ -19,21 +19,21 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.admin.settings; +package de.ozgcloud.admin.setting; import de.ozgcloud.common.test.TestUtils; -public class SettingsTestFactory { +public class SettingTestFactory { - public static Settings create() { + public static Setting create() { return createBuilder().build(); } - public static Settings.SettingsBuilder createBuilder() { - return Settings.builder(); + public static Setting.SettingBuilder createBuilder() { + return Setting.builder(); } - public static String buildSettingsJson(Settings settings, String settingBodyString) { - return TestUtils.loadTextFile("jsonTemplates/settings/createSettings.json.tmpl", settings.getName(), settingBodyString); + public static String buildSettingJson(Setting setting, String settingBodyString) { + return TestUtils.loadTextFile("jsonTemplates/settings/createSettings.json.tmpl", setting.getName(), settingBodyString); } } diff --git a/src/test/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTOTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTOTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..88b58eeedf6006f967346bb5356137c8963e99bd --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/VorgangManagerSettingDTOTestFactory.java @@ -0,0 +1,16 @@ +package de.ozgcloud.admin.setting; + +import de.ozgcloud.admin.setting.postfach.AbsenderVorgangManager; +import de.ozgcloud.admin.setting.postfach.AbsenderVorgangManagerTestFactory; + +public class VorgangManagerSettingDTOTestFactory { + public static AbsenderVorgangManager ABSENDER_VM = AbsenderVorgangManagerTestFactory.create(); + + public static VorgangManagerSettingDTO create() { + return createBuilder().build(); + } + + public static VorgangManagerSettingDTO.VorgangManagerSettingDTOBuilder createBuilder() { + return VorgangManagerSettingDTO.builder().absender(ABSENDER_VM); + } +} diff --git a/src/test/java/de/ozgcloud/admin/setting/YamlTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/YamlTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e46098b9caef41f936cce70994c983172f51b5cf --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/YamlTestFactory.java @@ -0,0 +1,22 @@ +package de.ozgcloud.admin.setting; + +import de.ozgcloud.admin.setting.postfach.PostfachSettingBody; +import de.ozgcloud.admin.setting.postfach.PostfachSettingBodyTestFactory; +import de.ozgcloud.common.test.TestUtils; + +public class YamlTestFactory { + public static PostfachSettingBody postfach = PostfachSettingBodyTestFactory.create(); + + public static String createVorgangManagerYaml() { + return TestUtils.loadTextFile("yamlTemplates/settings/vorgangManager.yaml.tmpl", + postfach.getAbsender().getName(), + postfach.getAbsender().getAnschrift(), + postfach.getAbsender().getDienst(), + postfach.getAbsender().getMandant(), + postfach.getAbsender().getGemeindeschluessel()); + } + + public static String createAlfaYaml() { + return TestUtils.loadTextFile("yamlTemplates/settings/alfa.yaml.tmpl", postfach.getSignatur()); + } +} diff --git a/src/test/java/de/ozgcloud/admin/settings/postfach/AbsenderTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderTestFactory.java similarity index 94% rename from src/test/java/de/ozgcloud/admin/settings/postfach/AbsenderTestFactory.java rename to src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderTestFactory.java index d048fb4c1b12ab988c88540ede24d3cd1b21e06a..abff05656418c3dbe9ef04e76ae155f56e930ab4 100644 --- a/src/test/java/de/ozgcloud/admin/settings/postfach/AbsenderTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderTestFactory.java @@ -1,4 +1,4 @@ -package de.ozgcloud.admin.settings.postfach; +package de.ozgcloud.admin.setting.postfach; import java.util.Random; diff --git a/src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManagerTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManagerTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..268ec8acafa399a8caae5ecfeb01c5a88b52f7e9 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/postfach/AbsenderVorgangManagerTestFactory.java @@ -0,0 +1,16 @@ +package de.ozgcloud.admin.setting.postfach; + +public class AbsenderVorgangManagerTestFactory { + public static AbsenderVorgangManager create() { + return createBuilder().build(); + } + + public static AbsenderVorgangManager.AbsenderVorgangManagerBuilder createBuilder() { + return AbsenderVorgangManager.builder() + .name(AbsenderTestFactory.NAME) + .anschrift(AbsenderTestFactory.ANSCHRIFT) + .dienst(AbsenderTestFactory.DIENST) + .mandant(AbsenderTestFactory.MANDANT) + .gemeindeschluessel(AbsenderTestFactory.GEMEINDESCHLUESSEL); + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBodyTestFactory.java b/src/test/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBodyTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..97b877744f8f0c4d2d2966cebe628331ca351104 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/setting/postfach/PostfachSettingBodyTestFactory.java @@ -0,0 +1,40 @@ +package de.ozgcloud.admin.setting.postfach; + +import java.util.Map; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.common.test.TestUtils; + +public class PostfachSettingBodyTestFactory { + public static final Absender ABSENDER = AbsenderTestFactory.create(); + public static final String SIGNATUR = LoremIpsum.getInstance().getHtmlParagraphs(1, 2); + + public static PostfachSettingBody create() { + return createBuilder().build(); + } + + public static PostfachSettingBody.PostfachSettingBodyBuilder createBuilder() { + return PostfachSettingBody.builder() + .absender(ABSENDER) + .signatur(SIGNATUR); + } + + public static String buildPostfachJson(PostfachSettingBody postfach) { + return TestUtils.loadTextFile("jsonTemplates/settings/createPostfach.json.tmpl", postfach.getAbsender().getName(), + postfach.getAbsender().getAnschrift(), postfach.getAbsender().getDienst(), postfach.getAbsender().getMandant(), + postfach.getAbsender().getGemeindeschluessel(), postfach.getSignatur()); + } + + public static Map<String, Object> createPostfachAsFlattenedMapVorgangManager() { + return Map.of("ozgcloud.postfach.absender.name", ABSENDER.getName(), + "ozgcloud.postfach.absender.anschrift", ABSENDER.getAnschrift(), + "ozgcloud.postfach.absender.dienst", ABSENDER.getDienst(), + "ozgcloud.postfach.absender.mandant", ABSENDER.getMandant(), + "ozgcloud.postfach.absender.gemeinde-schluessel", ABSENDER.getGemeindeschluessel()); + } + + public static Map<String, Object> createPostfachAsFlattenedMapAlfa() { + return Map.of("ozgcloud.postfach.signatur", SIGNATUR); + } +} diff --git a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentITCase.java b/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentITCase.java deleted file mode 100644 index bb562e80385ddcef643285c9ff78f1cacabc3f79..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentITCase.java +++ /dev/null @@ -1,30 +0,0 @@ - -package de.ozgcloud.admin.settings; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; - -import de.ozgcloud.common.test.DataITCase; -import lombok.SneakyThrows; - -@DataITCase -@AutoConfigureMockMvc -@WithMockUser -class AdminEnvironmentITCase { - @Autowired - private MockMvc mockMvc; - - @Test - @SneakyThrows - void shouldHaveHttpEndpoint() { - var result = mockMvc.perform(get("/configserver/example/path")); - - result.andExpect(status().isOk()); - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryITCase.java b/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryITCase.java deleted file mode 100644 index a894dec5af5e10e2a510daae05ebba6f508977cc..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryITCase.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.ozgcloud.admin.settings; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.config.environment.Environment; -import org.springframework.cloud.config.server.environment.EnvironmentRepository; -import org.springframework.security.test.context.support.WithMockUser; - -import de.ozgcloud.common.test.DataITCase; - -@DataITCase -@WithMockUser -class AdminEnvironmentRepositoryITCase { - - @Autowired - private EnvironmentRepository repository; - @Autowired - private PropertyRepository propertyRepository; - - private ConfigurationProperties configurationProperties = ConfigurationPropertiesTestFactory.create(); - - @AfterEach - void clearData() { - propertyRepository.deleteAll(); - } - - @Nested - class TestFindOne { - @BeforeEach - void initDb() { - saveMongoSource(configurationProperties); - } - - @Test - void shouldFindEnvironmentWithOneCorrectPropertySource() { - - var environment = findOneEnvironment(); - - assertThat(environment.getPropertySources()).hasSize(1); - } - - @Test - void shouldFindEnvironmentWithTwoCorrectPropertySources() { - var configurationProperties2 = ConfigurationPropertiesTestFactory.createBuilder().profile("other").build(); - saveMongoSource(configurationProperties2); - - var environment = findOneEnvironment(); - - assertThat(environment.getPropertySources()).hasSize(2); - } - - private void saveMongoSource(ConfigurationProperties configurationProperties) { - propertyRepository.save(configurationProperties); - } - - @Test - void shouldFindEnvironmentWithoutPropertySources() { - - var environment = repository.findOne("FalseAppName", ConfigurationPropertiesTestFactory.PROFILE, null); - - assertThat(environment.getPropertySources()).isEmpty(); - - } - - @Test - void shouldFindCorrectEnvironment() { - - var environment = findOneEnvironment(); - - assertThat(environment.getPropertySources().getLast().getSource()).isEqualTo(ConfigurationPropertiesTestFactory.PROPERTIES); - } - - private Environment findOneEnvironment() { - return repository.findOne(ConfigurationPropertiesTestFactory.APPNAME, ConfigurationPropertiesTestFactory.PROFILE, - null); - } - - } - -} diff --git a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryTest.java b/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryTest.java deleted file mode 100644 index bc10bdbd739bbd215f54f352c9413b77b3186c3b..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/AdminEnvironmentRepositoryTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.ozgcloud.admin.settings; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class AdminEnvironmentRepositoryTest { - - @InjectMocks - private AdminEnvironmentRepository repository; - - private ConfigurationProperties configurationProperties = ConfigurationPropertiesTestFactory.create(); - - @Nested - class TestBuildEnvironment { - @Test - void shouldHaveCorrectAppName() { - - var environment = repository.buildEnvironment(ConfigurationPropertiesTestFactory.APPNAME, new ArrayList<ConfigurationProperties>()); - - assertThat(environment.getName()).isEqualTo(ConfigurationPropertiesTestFactory.APPNAME); - } - - @Test - void shouldHaveCorrectNumberOfPropertySources() { - - var environment = repository.buildEnvironment(ConfigurationPropertiesTestFactory.APPNAME, - List.of(configurationProperties, configurationProperties)); - - assertThat(environment.getPropertySources()).hasSize(2); - } - - @Test - void shouldHandleEmptyConfigurationList() { - - var environment = repository.buildEnvironment(ConfigurationPropertiesTestFactory.APPNAME, new ArrayList<ConfigurationProperties>()); - - assertThat(environment.getPropertySources()).isEmpty(); - - } - } - - @Nested - class TestCreatePropertySource { - @Test - void shouldHaveCorrectSource() { - - var propertySource = repository.createPropertySource(configurationProperties); - - assertThat(propertySource.getSource()).isEqualTo(configurationProperties.getSource()); - } - - @Test - void shouldHaveCorrectName() { - - var propertySource = repository.createPropertySource(configurationProperties); - - assertThat(propertySource.getName()).isNull(); - } - } - -} diff --git a/src/test/java/de/ozgcloud/admin/settings/ConfigurationPropertiesTestFactory.java b/src/test/java/de/ozgcloud/admin/settings/ConfigurationPropertiesTestFactory.java deleted file mode 100644 index c1db07dd6f33924272dae792d6cac31eed53dc6e..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/ConfigurationPropertiesTestFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.ozgcloud.admin.settings; - -import java.util.Map; - -public class ConfigurationPropertiesTestFactory { - public static final String APPNAME = "testapp"; - public static final String PROFILE = "testprofile"; - public static final String PROPERTY1 = "property1"; - public static final String VALUE1 = "value1"; - public static Map<String, Object> PROPERTIES = Map.of(PROPERTY1, VALUE1); - - public static ConfigurationProperties create() { - return createBuilder().build(); - } - - public static ConfigurationProperties.ConfigurationPropertiesBuilder createBuilder() { - return ConfigurationProperties.builder() - .application(APPNAME) - .profile(PROFILE) - .source(PROPERTIES); - } -} diff --git a/src/test/java/de/ozgcloud/admin/settings/SettingsITCase.java b/src/test/java/de/ozgcloud/admin/settings/SettingsITCase.java deleted file mode 100644 index 8b21102c73aaec826c2d6a62d0fd5ab59620556f..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/SettingsITCase.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.admin.settings; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.http.HttpStatus; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; - -import de.ozgcloud.common.test.DataITCase; -import lombok.SneakyThrows; - -@DataITCase -@AutoConfigureMockMvc -@WithMockUser -class SettingsITCase { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private MongoOperations mongoOperations; - - @Autowired - private RepositoryRestProperties restProperties; - - @Nested - class TestRestRepo { - - @BeforeEach - void init() { - mongoOperations.dropCollection(Settings.class); - mongoOperations.save(SettingsTestFactory.create()); - } - - @Test - @SneakyThrows - void shouldHaveStatusOkForExisting() { - var id = mongoOperations.findAll(Settings.class).get(0).getId(); - - var result = doPerform(id); - - result.andExpect(status().isOk()); - } - - @Test - @SneakyThrows - void shouldHaveStatusNotFoundForNonExisting() { - var result = doPerform("unknown"); - - result.andExpect(status().is(HttpStatus.NOT_FOUND.value())); - } - - @SneakyThrows - private ResultActions doPerform(String id) { - return mockMvc.perform(get(String.join("/", restProperties.getBasePath(), SettingsConstants.PATH, id))); - } - } - -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachITCase.java b/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachITCase.java deleted file mode 100644 index a00766eebadd1f1b62e1fad29bd46b85ab07ccc6..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachITCase.java +++ /dev/null @@ -1,291 +0,0 @@ -package de.ozgcloud.admin.settings.postfach; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import de.ozgcloud.admin.settings.Settings; -import de.ozgcloud.admin.settings.SettingsBody; -import de.ozgcloud.admin.settings.SettingsConstants; -import de.ozgcloud.admin.settings.SettingsTestFactory; -import de.ozgcloud.common.test.DataITCase; -import lombok.SneakyThrows; - -@DataITCase -@AutoConfigureMockMvc -@WithMockUser -class PostfachITCase { - private static final String POSTFACH_NAME = "Postfach"; - - @Autowired - private MockMvc mockMvc; - - @Autowired - private MongoOperations mongoOperations; - - @Autowired - private RepositoryRestProperties restProperties; - - private Settings settingsWithPostfach = SettingsTestFactory.createBuilder() - .name(POSTFACH_NAME) - .settingsBody(PostfachTestFactory.create()) - .build(); - - @BeforeEach - void clear() { - mongoOperations.dropCollection(Settings.class); - } - - @Nested - class TestSave { - - @Test - @SneakyThrows - void shouldHaveResponseStatusCreated() { - var result = performPost(settingsWithPostfach); - - result.andExpect(status().isCreated()); - } - - @Test - @SneakyThrows - void shouldCreateSettingsItem() { - performPost(settingsWithPostfach); - - assertThat(getSettingWithPostfachFromDb()) - .usingRecursiveComparison().ignoringFields("id").isEqualTo(settingsWithPostfach); - } - - @Nested - class TestPostfachSettings { - - @Test - @SneakyThrows - void shouldBeInstanceOfPostfach() { - performPost(settingsWithPostfach); - - assertThat(getSettingWithPostfachFromDb() - .getSettingsBody()).isInstanceOf(Postfach.class); - } - - @Test - @SneakyThrows - void shouldCreateEmptySignatur() { - var postfachWithEmptySignatur = PostfachTestFactory.createBuilder() - .signatur(StringUtils.EMPTY).build(); - var settingsWithPostfachEmptySignatur = createSettingsWithPostfach(postfachWithEmptySignatur); - - performPost(settingsWithPostfachEmptySignatur); - - assertThat(getPostfachFromDb().getSignatur()).isEmpty(); - } - - private Postfach getPostfachFromDb() { - return (Postfach) getSettingWithPostfachFromDb().getSettingsBody(); - } - - @Nested - class TestAbsenderValidation { - @Test - @SneakyThrows - void shouldReturnUnprocessableEntityOnEmptyName() { - var absenderWithEmptyName = AbsenderTestFactory.createBuilder().name(StringUtils.EMPTY).build(); - var settingsWithPostfachEmptyAbsenderName = createSettingsWithPostfachAbsender(absenderWithEmptyName); - - var result = performPost(settingsWithPostfachEmptyAbsenderName); - - result.andExpect(status().isUnprocessableEntity()); - } - - @Test - @SneakyThrows - void shouldReturnUnprocessableEntityOnEmptyAnschrift() { - var absenderWithEmptyAnschrift = AbsenderTestFactory.createBuilder().anschrift(StringUtils.EMPTY).build(); - var settingsWithPostfachEmptyAnschrift = createSettingsWithPostfachAbsender(absenderWithEmptyAnschrift); - - var result = performPost(settingsWithPostfachEmptyAnschrift); - - result.andExpect(status().isUnprocessableEntity()); - } - - @Test - @SneakyThrows - void shouldReturnUnprocessableEntityOnEmptyDienst() { - var absenderWithEmptyDienst = AbsenderTestFactory.createBuilder().dienst(StringUtils.EMPTY).build(); - var settingsWithPostfachEmptyDienst = createSettingsWithPostfachAbsender(absenderWithEmptyDienst); - - var result = performPost(settingsWithPostfachEmptyDienst); - - result.andExpect(status().isUnprocessableEntity()); - } - - @Test - @SneakyThrows - void shouldReturnUnprocessableEntityOnEmptyMandant() { - var absenderWithEmptyMandant = AbsenderTestFactory.createBuilder().mandant(StringUtils.EMPTY).build(); - var setttingsWithPostfachEmptyMandant = createSettingsWithPostfachAbsender(absenderWithEmptyMandant); - - var result = performPost(setttingsWithPostfachEmptyMandant); - - result.andExpect(status().isUnprocessableEntity()); - } - - @Test - @SneakyThrows - void shouldReturnUnprocessableEntityOnEmptyGemeindeSchluessel() { - var absenderWithEmptyGemeindeschluessel = AbsenderTestFactory.createBuilder().gemeindeschluessel(StringUtils.EMPTY).build(); - var settingsWithPostfachEmptyGemeindeschluessel = createSettingsWithPostfachAbsender(absenderWithEmptyGemeindeschluessel); - - var result = performPost(settingsWithPostfachEmptyGemeindeschluessel); - - result.andExpect(status().isUnprocessableEntity()); - } - - private Settings createSettingsWithPostfachAbsender(Absender absender) { - var postfach = PostfachTestFactory.createBuilder().absender(absender).build(); - return createSettingsWithPostfach(postfach); - } - - } - - private Settings createSettingsWithPostfach(SettingsBody postfach) { - return SettingsTestFactory.createBuilder().name(POSTFACH_NAME).settingsBody(postfach).build(); - } - } - - @SneakyThrows - private ResultActions performPost(Settings setting) { - var postBody = convertSettingToString(setting); - return mockMvc.perform(post(String.join("/", restProperties.getBasePath(), - SettingsConstants.PATH)) - .with(csrf()) - .contentType(MediaType.APPLICATION_JSON) - .content(postBody)); - } - } - - @Nested - class TestGet { - private String id; - @Autowired - private ObjectMapper mapper; - - @BeforeEach - void init() { - id = mongoOperations.save(settingsWithPostfach).getId(); - } - - @Test - @SneakyThrows - void shouldHaveStatusOkForExisting() { - var result = performGet(); - - result.andExpect(status().isOk()); - } - - @Test - @SneakyThrows - void shouldHaveInstanceOfPostfach() { - - var result = performGet(); - - assertThat(mapper.readValue(result.andReturn().getResponse().getContentAsString(), Settings.class).getSettingsBody()) - .isInstanceOf(Postfach.class); - } - - @SneakyThrows - private ResultActions performGet() { - return mockMvc.perform(get(String.join("/", restProperties.getBasePath(), SettingsConstants.PATH, id))); - } - } - - @Nested - class TestPut { - private String id; - private Postfach updatedPostfach = PostfachTestFactory.createBuilder() - .absender(AbsenderTestFactory.createBuilder() - .name("Neuer Name") - .anschrift("Neue Anschrift") - .build()) - .build(); - private Settings updatedSettings = SettingsTestFactory.createBuilder() - .name(POSTFACH_NAME) - .settingsBody(updatedPostfach) - .build(); - - @BeforeEach - void init() { - id = mongoOperations.save(settingsWithPostfach).getId(); - } - - @Test - @SneakyThrows - void shouldHaveStatusNoContent() { - var result = performPut(); - - result.andExpect(status().isNoContent()); - } - - @Test - @SneakyThrows - void shouldHaveUpdatedSettings() { - performPut(); - - assertThat(getSettingWithPostfachFromDb()) - .usingRecursiveComparison().ignoringFields("id").isEqualTo(updatedSettings); - } - - @Test - @SneakyThrows - void shouldHaveExactlyOnePostfachSetting() { - performPut(); - - List<Settings> postfachSettings = mongoOperations.find(createQueryForPostfach(), Settings.class); - - assertThat(postfachSettings).hasSize(1); - } - - @SneakyThrows - private ResultActions performPut() { - var body = convertSettingToString(updatedSettings); - return mockMvc.perform(put(String.join("/", restProperties.getBasePath(), SettingsConstants.PATH, id)) - .with(csrf()) - .contentType(MediaType.APPLICATION_JSON) - .content(body)); - } - - } - - private Settings getSettingWithPostfachFromDb() { - return mongoOperations.findOne(createQueryForPostfach(), Settings.class); - } - - private Query createQueryForPostfach() { - return new Query().addCriteria(Criteria.where("name").in(POSTFACH_NAME)); - } - - @SneakyThrows - private String convertSettingToString(Settings setting) { - return SettingsTestFactory.buildSettingsJson(setting, PostfachTestFactory.buildPostfachJson((Postfach) setting.getSettingsBody())); - } -} diff --git a/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachTestFactory.java b/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachTestFactory.java deleted file mode 100644 index 4f82a2a55e84d76d63b49d54be9ee8100a76b498..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/settings/postfach/PostfachTestFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.ozgcloud.admin.settings.postfach; - -import com.thedeanda.lorem.LoremIpsum; - -import de.ozgcloud.common.test.TestUtils; - -public class PostfachTestFactory { - public static final Absender ABSENDER = AbsenderTestFactory.create(); - public static final String SIGNATUR = LoremIpsum.getInstance().getHtmlParagraphs(1, 2); - - public static Postfach create() { - return createBuilder().build(); - } - - public static Postfach.PostfachBuilder createBuilder() { - return Postfach.builder() - .absender(ABSENDER) - .signatur(SIGNATUR); - } - - public static String buildPostfachJson(Postfach postfach) { - return TestUtils.loadTextFile("jsonTemplates/settings/createPostfach.json.tmpl", postfach.getAbsender().getName(), - postfach.getAbsender().getAnschrift(), postfach.getAbsender().getDienst(), postfach.getAbsender().getMandant(), - postfach.getAbsender().getGemeindeschluessel(), postfach.getSignatur()); - } - -} diff --git a/src/test/resources/META-INF/spring/org.mockito.junit.jupiter.MockitoExtension b/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from src/test/resources/META-INF/spring/org.mockito.junit.jupiter.MockitoExtension rename to src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/src/test/resources/application-itcase.yaml b/src/test/resources/application-itcase.yaml index fc717c37b9cfb97360022930cb07c950b16d1983..3082babc0c50e52484ef75f5a650980975eea15d 100644 --- a/src/test/resources/application-itcase.yaml +++ b/src/test/resources/application-itcase.yaml @@ -1,2 +1,8 @@ mongock: - enabled: false \ No newline at end of file + enabled: false + +ozgcloud: + oauth2: + auth-server-url: https://sso.it-case.de + realm: by-kiel-dev + resource: admin diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml deleted file mode 100644 index bfdb349dcc960848ab0c7815b86b4c923f718393..0000000000000000000000000000000000000000 --- a/src/test/resources/application.yaml +++ /dev/null @@ -1,25 +0,0 @@ - -management: - server: - port: 8081 -spring: - application: - name: OzgCloud_Administration - data: - mongodb: - authentication-database: admin - rest: - basePath: /api/configuration - cloud: - config: - server: - prefix: /configserver - security: - oauth2: - resourceserver: - jwt: - issuer-uri: ${ozgcloud.oauth2.auth-server-url}/realms/${ozgcloud.oauth2.realm} -ozgcloud: - oauth2: - auth-server-url: https://sso.dev.by.ozg-cloud.de - realm: by-kiel-dev \ No newline at end of file diff --git a/src/test/resources/jsonTemplates/security/resource_access.template.json b/src/test/resources/jsonTemplates/security/resource_access.template.json new file mode 100644 index 0000000000000000000000000000000000000000..44f43d8cb40f1ee887fe158bb9668c1074251f5d --- /dev/null +++ b/src/test/resources/jsonTemplates/security/resource_access.template.json @@ -0,0 +1,7 @@ +{ + "admin": { + "roles": [ + "ADMIN_ADMIN" + ] + } +} diff --git a/src/test/resources/jsonTemplates/settings/createSettings.json.tmpl b/src/test/resources/jsonTemplates/settings/createSettings.json.tmpl index a0dd5cfd77775267d1f40a54948810e02ac4e766..3c36aa163c522f47080816a55dcaaa01b758ddb6 100644 --- a/src/test/resources/jsonTemplates/settings/createSettings.json.tmpl +++ b/src/test/resources/jsonTemplates/settings/createSettings.json.tmpl @@ -1,4 +1,4 @@ { "name" : "%s", - "settingsBody" : %s + "settingBody" : %s } \ No newline at end of file diff --git a/src/test/resources/yamlTemplates/settings/alfa.yaml.tmpl b/src/test/resources/yamlTemplates/settings/alfa.yaml.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..65b37e576b646c76bf03a83b18b8ba8e911cfe4d --- /dev/null +++ b/src/test/resources/yamlTemplates/settings/alfa.yaml.tmpl @@ -0,0 +1,3 @@ +ozgcloud: + postfach: + signatur: %s diff --git a/src/test/resources/yamlTemplates/settings/vorgangManager.yaml.tmpl b/src/test/resources/yamlTemplates/settings/vorgangManager.yaml.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..8bca4ba163aa5d632fc7d38c9b1af9998662993e --- /dev/null +++ b/src/test/resources/yamlTemplates/settings/vorgangManager.yaml.tmpl @@ -0,0 +1,8 @@ +ozgcloud: + postfach: + absender: + name: %s + anschrift: %s + dienst: %s + mandant: %s + gemeinde-schluessel: '%s'