diff --git a/.gitignore b/.gitignore index 671fb0fee4dba749bd6cb2b15d03422c8ea29976..b97206eaa9037206fb36f521f938d8c355afb4da 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ target/ # Client goofy-client/.vscode/ goofy-client/apps/coverage/ +goofy-client/node/* +goofy-client/temp/* .attach** -.factorypath \ No newline at end of file +.factorypath diff --git a/alfa-service/pom.xml b/alfa-service/pom.xml index b28e9df32cc6244f0c27bfadcef451c7f3e50a72..7e4f81cd52eb0791b9f9cea0e8b1f2c4aa6a7483 100644 --- a/alfa-service/pom.xml +++ b/alfa-service/pom.xml @@ -32,7 +32,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>1.15.0-SNAPSHOT</version> + <version>1.16.0-SNAPSHOT</version> </parent> <artifactId>alfa-service</artifactId> @@ -43,6 +43,9 @@ <properties> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> + + <!-- TODO: die Version über kop-common ziehen --> + <jjwt.version>0.11.5</jjwt.version> </properties> <dependencies> @@ -51,7 +54,6 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> @@ -64,29 +66,25 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> - <dependency> <groupId>net.devh</groupId> <artifactId>grpc-client-spring-boot-starter</artifactId> </dependency> - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> - <dependency> - <groupId>org.keycloak</groupId> - <artifactId>keycloak-spring-boot-starter</artifactId> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> - <groupId>org.keycloak</groupId> - <artifactId>keycloak-admin-client</artifactId> + <groupId>com.jayway.jsonpath</groupId> + <artifactId>json-path</artifactId> </dependency> <!-- jwt --> @@ -96,7 +94,18 @@ </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt</artifactId> + <artifactId>jjwt-api</artifactId> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-impl</artifactId> + <version>${jjwt.version}</version> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-jackson</artifactId> + <version>${jjwt.version}</version> + <scope>runtime</scope> </dependency> <!-- own projects --> diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/EnvironmentController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/EnvironmentController.java index 0103a54760c7db79f34c0ab0980a0b569383108b..9dda134b2ea6580936e80d068055309175b645ce 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/EnvironmentController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/EnvironmentController.java @@ -25,7 +25,6 @@ package de.ozgcloud.alfa; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; -import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; @@ -37,23 +36,19 @@ import org.springframework.web.bind.annotation.RestController; public class EnvironmentController { @Autowired - private KeycloakSpringBootProperties kcProperties; + private OAuth2Properties oAuth2Properties; @Value("${goofy.production}") private boolean production = true; @GetMapping public FrontendEnvironment getFrontendEnvironment() { - return FrontendEnvironment.builder()// - .production(production)// - .remoteHost(apiRoot())// - .authServer(kcProperties.getAuthServerUrl())// - .clientId(kcProperties.getResource())// - .realm(kcProperties.getRealm()) + return FrontendEnvironment.builder() + .production(production) + .remoteHost(linkTo(RootController.class).toUri().toString()) + .authServer(oAuth2Properties.getAuthServerUrl()) + .clientId(oAuth2Properties.getResource()) + .realm(oAuth2Properties.getRealm()) .build(); } - - private String apiRoot() { - return linkTo(RootController.class).toUri().toString(); - } } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/GrpcConfiguration.java b/alfa-service/src/main/java/de/ozgcloud/alfa/GrpcConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..97cac2f8ea30484287afa30ab96bf522d1034bd9 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/GrpcConfiguration.java @@ -0,0 +1,23 @@ +package de.ozgcloud.alfa; + +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.context.annotation.Configuration; + +/** + * A workaround for @GrpcClient to work i.e. inject dependency until full Spring 3 support is available. + * https://github.com/yidongnan/grpc-spring-boot-starter/pull/775 + */ +@Configuration +@ImportAutoConfiguration({ + net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration.class, + net.devh.boot.grpc.client.autoconfigure.GrpcClientMetricAutoConfiguration.class, + net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration.class, + net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration.class, + net.devh.boot.grpc.client.autoconfigure.GrpcClientTraceAutoConfiguration.class, + net.devh.boot.grpc.client.autoconfigure.GrpcDiscoveryClientAutoConfiguration.class, + + net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration.class, + net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration.class +}) +public class GrpcConfiguration { +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/JwtAuthConverter.java b/alfa-service/src/main/java/de/ozgcloud/alfa/JwtAuthConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..70397ac1de728d4d94369b5b227b0228750c3897 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/JwtAuthConverter.java @@ -0,0 +1,85 @@ +package de.ozgcloud.alfa; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.collections.MapUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.NonNull; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.stereotype.Component; + +@Component +public class JwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> { + + private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + + private static final String SIMPLE_GRANT_AUTHORITY_PREFIX = "ROLE_"; + static final String RESOURCE_ACCESS_KEY = "resource_access"; + static final String ROLES_KEY = "roles"; + + @Autowired + private OAuth2Properties oAuth2Properties; + + @Override + public AbstractAuthenticationToken convert(@NonNull Jwt jwt) { + return new JwtAuthenticationToken(jwt, getAuthorities(jwt), getPrincipleClaimName(jwt)); + } + + String getPrincipleClaimName(Jwt jwt) { + return MapUtils.getString(jwt.getClaims(), getClaimName()); + } + + private String getClaimName() { + return Optional.ofNullable(oAuth2Properties.getPrincipleAttribute()).orElse(JwtClaimNames.SUB); + } + + Set<GrantedAuthority> getAuthorities(Jwt jwt) { + return Stream.concat(jwtGrantedAuthoritiesConverter.convert(jwt).stream(), extractResourceRoles(jwt).stream()).collect(Collectors.toSet()); + } + + Collection<SimpleGrantedAuthority> extractResourceRoles(Jwt jwt) { + var resourceAccess = getResourceAccess(jwt); + if (resourceAccess.isEmpty()) { + return Collections.emptySet(); + } + if (Objects.isNull(resourceAccess.get(oAuth2Properties.getResource()))) { + return Collections.emptySet(); + } + return extractRoles(getClaimMapFromMap(resourceAccess, oAuth2Properties.getResource())); + } + + private Map<String, Object> getResourceAccess(Jwt jwt) { + return jwt.getClaimAsMap(RESOURCE_ACCESS_KEY); + } + + @SuppressWarnings("unchecked") + private Map<String, Object> getClaimMapFromMap(Map<String, Object> claimMap, String claimName) { + return MapUtils.getMap(claimMap, claimName); + } + + Set<SimpleGrantedAuthority> extractRoles(Map<String, Object> resource) { + if (!resource.containsKey(ROLES_KEY)) { + return Collections.emptySet(); + } + return getRoles(resource).stream().map(role -> new SimpleGrantedAuthority(SIMPLE_GRANT_AUTHORITY_PREFIX + role)).collect(Collectors.toSet()); + } + + @SuppressWarnings("unchecked") + private Collection<String> getRoles(Map<String, Object> resource) { + return (Collection<String>) resource.get(ROLES_KEY); + } +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/OAuth2Properties.java b/alfa-service/src/main/java/de/ozgcloud/alfa/OAuth2Properties.java new file mode 100644 index 0000000000000000000000000000000000000000..6a8d79d91213599bfcc0e9ff7213ff83a9ff53c2 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/OAuth2Properties.java @@ -0,0 +1,36 @@ +package de.ozgcloud.alfa; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = OAuth2Properties.PREFIX) +public class OAuth2Properties { + + static final String PREFIX = "ozgcloud.oauth2"; + + /** + * OAuth2 auth server url + */ + private String authServerUrl; + + /** + * OAuth2 realm + */ + private String realm; + + /** + * OAuth2 resource + */ + private String resource; + + /** + * OAuth2 principle attribute + */ + private String principleAttribute; +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/RequestIdFilter.java b/alfa-service/src/main/java/de/ozgcloud/alfa/RequestIdFilter.java index c99af4d374c060118ff2930849ef4eccf40d99dc..83b33e364ebd220e94962637bc6fedc545ad2efb 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/RequestIdFilter.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/RequestIdFilter.java @@ -25,10 +25,10 @@ package de.ozgcloud.alfa; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.logging.log4j.CloseableThreadContext; import org.springframework.beans.factory.annotation.Autowired; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/SecurityConfiguration.java b/alfa-service/src/main/java/de/ozgcloud/alfa/SecurityConfiguration.java index ca8c0c09f524b0ad77df52c18d25d0451c331b69..42f7e4158e24894ebc33b10354c346c5efb3c8eb 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/SecurityConfiguration.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/SecurityConfiguration.java @@ -23,16 +23,20 @@ */ package de.ozgcloud.alfa; -import org.keycloak.adapters.springsecurity.KeycloakConfiguration; -import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; -import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @@ -42,48 +46,57 @@ import org.springframework.security.web.header.writers.frameoptions.XFrameOption import de.ozgcloud.alfa.common.downloadtoken.DownloadTokenAuthenticationFilter; -@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) -@KeycloakConfiguration -public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(securedEnabled = true) +public class SecurityConfiguration { + + @Autowired + private JwtAuthConverter jwtAuthConverter; @Autowired - private DownloadTokenAuthenticationFilter downloadTokenFilter; + private SpringJwtProperties springJwtProperties; + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http, DownloadTokenAuthenticationFilter downloadTokenFilter) throws Exception { + http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())); - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); + http.sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and().authorizeRequests()// - .antMatchers(HttpMethod.GET, "/api/environment").permitAll()// - .antMatchers(HttpMethod.GET, "/assets/**").permitAll()// - .antMatchers(HttpMethod.GET, "/vorgang/**").permitAll()// - .antMatchers(HttpMethod.GET, "/meine/**").permitAll()// - .antMatchers(HttpMethod.GET, "/alle/**").permitAll()// - .antMatchers(HttpMethod.GET, "/unassigned/**").permitAll()// - .antMatchers("/api").authenticated()// - .antMatchers("/api/**").authenticated()// - .antMatchers("/actuator").permitAll()// - .antMatchers("/actuator/**").permitAll()// - .antMatchers("/").permitAll()// - .antMatchers("/*").permitAll()// - .anyRequest().denyAll(); + http.authorizeHttpRequests(authorize -> authorize + .requestMatchers(HttpMethod.GET, "/api/environment").permitAll() + .requestMatchers(HttpMethod.GET, "/assets/**").permitAll() + .requestMatchers(HttpMethod.GET, "/vorgang/**").permitAll() + .requestMatchers(HttpMethod.GET, "/meine/**").permitAll() + .requestMatchers(HttpMethod.GET, "/alle/**").permitAll() + .requestMatchers(HttpMethod.GET, "/unassigned/**").permitAll() + .requestMatchers("/api").authenticated() + .requestMatchers("/api/**").authenticated() + .requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll() + .requestMatchers("/").permitAll() + .requestMatchers("/*").permitAll() + .anyRequest().denyAll()); - http.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)); + http.oauth2ResourceServer(this::setOAuth2ResourceServer); + http.headers(headers -> headers.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))); http.addFilterBefore(downloadTokenFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); } - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) { - KeycloakAuthenticationProvider keyCloakAuthProvider = keycloakAuthenticationProvider(); - keyCloakAuthProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); - auth.authenticationProvider(keyCloakAuthProvider); + private void setOAuth2ResourceServer(OAuth2ResourceServerConfigurer<HttpSecurity> configurer) { + configurer.jwt().jwtAuthenticationConverter(jwtAuthConverter); + Optional.ofNullable(springJwtProperties.getJwkSetUri()).ifPresent(jwkSetUri -> configurer.jwt().jwkSetUri(jwkSetUri)); } - @Override + @Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new NullAuthenticatedSessionStrategy(); } + @Bean + AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class).build(); + } } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/SpringJwtProperties.java b/alfa-service/src/main/java/de/ozgcloud/alfa/SpringJwtProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..9b81c0654242a2132f92ce8a1c2e509981951c00 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/SpringJwtProperties.java @@ -0,0 +1,21 @@ +package de.ozgcloud.alfa; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = SpringJwtProperties.PREFIX) +public class SpringJwtProperties { + + static final String PREFIX = "spring.security.oauth2.resourceserver.jwt"; + + /** + * Jwt jwk set uri + */ + private String jwkSetUri = null; +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/WebConfig.java b/alfa-service/src/main/java/de/ozgcloud/alfa/WebConfig.java index 2074b2e67f74c77f5b00ff6c9a8f6b6f8e0bd7ed..a0669a9539fecfa3fb4bb46020f5c85f9f133dde 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/WebConfig.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/WebConfig.java @@ -26,9 +26,6 @@ package de.ozgcloud.alfa; import java.io.IOException; import java.util.concurrent.TimeUnit; -import org.keycloak.adapters.KeycloakConfigResolver; -import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.http.CacheControl; @@ -36,6 +33,8 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.PathResourceResolver; +import lombok.NoArgsConstructor; + @Configuration public class WebConfig implements WebMvcConfigurer { @@ -43,7 +42,6 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/*.js", "/*.css", "/*.ttf", "/*.woff", "/*.woff2", "/*.eot", "/**/*.svg", "/*.svf", "/*.otf", "/*.ico", "/**/*.png") .addResourceLocations(RESOURCE_LOCATION) @@ -56,18 +54,20 @@ public class WebConfig implements WebMvcConfigurer { .setCacheControl(CacheControl.noStore()) .setUseLastModified(false) .resourceChain(true) - .addResolver(new PathResourceResolver() { - @Override - protected Resource getResource(String resourcePath, Resource location) throws IOException { - Resource requestedResource = location.createRelative(resourcePath); - return requestedResource.exists() && requestedResource.isReadable() ? requestedResource - : super.getResource("index.html", location); - } - }); + .addResolver(new OzgCloudPathResourceResolver()); } - @Bean - public KeycloakConfigResolver keyCloakConfigResolver() { - return new KeycloakSpringBootConfigResolver(); + @NoArgsConstructor + static class OzgCloudPathResourceResolver extends PathResourceResolver { + + @Override + protected Resource getResource(String resourcePath, Resource location) throws IOException { + var requestedResource = location.createRelative(resourcePath); + + if (requestedResource.exists() && requestedResource.isReadable()) { + return requestedResource; + } + return super.getResource("index.html", location); + } } -} +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/GermanDateTimeFormatter.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/GermanDateTimeFormatter.java new file mode 100644 index 0000000000000000000000000000000000000000..97dc0511e8c1d58ffd0de06b34820df0dfe23f03 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/GermanDateTimeFormatter.java @@ -0,0 +1,46 @@ +/* + * 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. + */ +package de.ozgcloud.alfa.common; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import lombok.NonNull; + +@Component +public class GermanDateTimeFormatter { + + private static final DateTimeFormatter DATE_TIME_ZONE = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss z").withLocale(Locale.GERMANY); + + @Autowired + private SystemProperties systemProperties; + + public String formatZonedDateTime(@NonNull ZonedDateTime zonedDateTime) { + return DATE_TIME_ZONE.format(zonedDateTime.withZoneSameInstant(systemProperties.getTimeZone())); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/TestSecurityConfiguration.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/SystemProperties.java similarity index 60% rename from alfa-service/src/test/java/de/ozgcloud/alfa/TestSecurityConfiguration.java rename to alfa-service/src/main/java/de/ozgcloud/alfa/common/SystemProperties.java index 9ea7dbf43b0ffb08b194e501e39bdd26da99d539..d58bada4d2c41ca587bf689f646d4524134c8a86 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/TestSecurityConfiguration.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/SystemProperties.java @@ -21,23 +21,26 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.alfa; +package de.ozgcloud.alfa.common; +import java.time.ZoneId; + +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.WebSecurityConfigurer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter @Configuration -@Order(50) -public class TestSecurityConfiguration extends WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { +@ConfigurationProperties(prefix = SystemProperties.PREFIX) +public class SystemProperties { - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); + static final String PREFIX = "ozgcloud.system"; - http.csrf().disable(); - } + /** + * Timezone to be used in application. + */ + private ZoneId timeZone = ZoneId.of("Europe/Berlin"); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileMaxSizeConstraint.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileMaxSizeConstraint.java index 0f252193a005ef714be422942761728f397165c5..4e2e344b837e957f0804d7cd7a48c2bdd607817e 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileMaxSizeConstraint.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileMaxSizeConstraint.java @@ -30,8 +30,8 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; import de.ozgcloud.alfa.common.ValidationMessageCodes; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileService.java index 74bfd67e7f22ec26d200d2c43e729a9c151cd54c..eebbd2caea67e481ca652f08743a50d023d09752 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileService.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandler.java index ad7639749de0b76fa04e88485ae985ba110f6718..ee46850a3953a45a759529e3ba3513766bfc62a8 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandler.java @@ -39,7 +39,7 @@ public class DownloadAuthenticationHandler { boolean check(FileId fileId, Authentication auth) { if (auth instanceof UsernamePasswordAuthenticationToken userPasswordToken) { - GoofyUserWithFileId user = (GoofyUserWithFileId) userPasswordToken.getPrincipal(); + var user = (GoofyUserWithFileId) userPasswordToken.getPrincipal(); return Objects.nonNull(fileId) && fileId.equals(user.getFileId()) && auth.isAuthenticated(); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidator.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidator.java index 6aa3dbce4860ce92368a052da6ecc0374b5ec821..1bef03838dd01d3f8da8b215395e119ea6d546a3 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidator.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidator.java @@ -26,8 +26,8 @@ package de.ozgcloud.alfa.common.binaryfile; import java.util.Map; import java.util.Optional; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; import org.springframework.beans.factory.annotation.Autowired; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandService.java index ea7534a4f95a24c47197ac3662ab60bd87362bb5..638a672caa20cd5268b4218db07f69e0e810182c 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandService.java @@ -26,7 +26,7 @@ package de.ozgcloud.alfa.common.command; import java.util.Optional; import java.util.stream.Stream; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.annotation.Autowired; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CreateCommand.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CreateCommand.java index 3cb5adcf3fcfa15f82dbd6228eeb607714691a85..0f8887ca869483fc91e817df3e986669cf29c940 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CreateCommand.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CreateCommand.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.common.command; -import javax.validation.Valid; +import jakarta.validation.Valid; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrder.java index da13800709784f1a2c7906d3bfba20def3cd4a62..f998dfe648a4457c8247c56631934ea36eeed1ec 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrder.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrder.java @@ -7,8 +7,8 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; @Constraint(validatedBy = { RequiredOrderValidator.class }) @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE }) diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrderValidator.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrderValidator.java index 79c52429309481d71020d72bb26c3949e8bb0cc7..ef8a8897172582847e3cdc9377a5f5b4122a8342 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrderValidator.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/RequiredOrderValidator.java @@ -1,7 +1,7 @@ package de.ozgcloud.alfa.common.command; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; public class RequiredOrderValidator implements ConstraintValidator<RequiredOrder, CreateCommand> { diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilter.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilter.java index 297aa8e7a22308f54ca8a3781c92e3742eeb6adf..e2824c6edfb29aababf2e32ef78a04364acdb468 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilter.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilter.java @@ -25,13 +25,12 @@ package de.ozgcloud.alfa.common.downloadtoken; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -52,7 +51,7 @@ public class DownloadTokenAuthenticationFilter extends OncePerRequestFilter { try { downloadTokenService.handleToken(request, getDownloadToken(request)); } catch (TechnicalException e) { - response.setStatus(HttpStatus.SC_UNAUTHORIZED); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenProperties.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenProperties.java index 8e63cd4b96fe65a1ef4b937c301c0f6b1ffd782a..d07c0a7956c415e8c80adeadd5205aa634e32ec3 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenProperties.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenProperties.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.common.downloadtoken; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenService.java index fa4276a91aebf8d0fae0b1ea4ae5c7aa14a08442..466881c91649dc8b37de5ec46057b33d5e717c89 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenService.java @@ -27,7 +27,7 @@ import static de.ozgcloud.alfa.JwtTokenUtil.*; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -90,12 +90,12 @@ class DownloadTokenService { Optional<Claims> claimsOptional = jwtTokenUtil.getAllClaimsFromToken(token); var downloadUserBuilder = GoofyUserWithFileId.builder(); claimsOptional.ifPresent(claims -> downloadUserBuilder.user( - UserProfile.builder() - .id(UserId.from(claims.get(USERID_CLAIM, String.class))) - .firstName(claims.get(FIRSTNAME_CLAIM, String.class)) - .lastName(claims.get(LASTNAME_CLAIM, String.class)) - .authorities(jwtTokenUtil.getRolesFromToken(token)) - .organisationseinheitIds(jwtTokenUtil.getOrganisationseinheitIdsFromToken(token)).build()) + UserProfile.builder() + .id(UserId.from(claims.get(USERID_CLAIM, String.class))) + .firstName(claims.get(FIRSTNAME_CLAIM, String.class)) + .lastName(claims.get(LASTNAME_CLAIM, String.class)) + .authorities(jwtTokenUtil.getRolesFromToken(token)) + .organisationseinheitIds(jwtTokenUtil.getOrganisationseinheitIdsFromToken(token)).build()) .fileId(FileId.from(claims.get(FILEID_CLAIM, String.class)))); return downloadUserBuilder.build(); diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/errorhandling/ExceptionController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/errorhandling/ExceptionController.java index 0772ce937792cb74d362034fea4ea1e4d58792c0..018624fea26e30f86f059b898038ce02efdf0059 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/errorhandling/ExceptionController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/errorhandling/ExceptionController.java @@ -33,10 +33,10 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Stream; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import javax.validation.Path; -import javax.validation.metadata.ConstraintDescriptor; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Path; +import jakarta.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.engine.HibernateConstraintViolation; import org.springframework.core.annotation.Order; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserHelper.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserHelper.java index c3752745433b597fec75c1eea29995c7efda3075..b0414c8fc1f7a12e718c0a8aeec96ebeedd8468b 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserHelper.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserHelper.java @@ -34,26 +34,20 @@ import org.springframework.security.authentication.AuthenticationTrustResolverIm import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; import lombok.AccessLevel; import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CurrentUserHelper { - static final String ROLE_PREFIX = "ROLE_"; - public static final Predicate<String> HAS_ROLE = CurrentUserHelper::hasRole; + static final String ROLE_PREFIX = "ROLE_"; + private static final String SUB_CLAIM_KEY = "sub"; private static final AuthenticationTrustResolver TRUST_RESOLVER = new AuthenticationTrustResolverImpl(); - private static final Predicate<Authentication> TRUSTED = auth -> !TRUST_RESOLVER.isAnonymous(auth); - - public static Authentication getAuthentication() { - return findAuthentication().orElseThrow(() -> new IllegalStateException("No authenticated User found")); - } - - public static Optional<Authentication> findAuthentication() { - return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).filter(TRUSTED); - } + private static final Predicate<Authentication> IS_TRUSTED = auth -> !TRUST_RESOLVER.isAnonymous(auth); + private static final Predicate<String> IS_ROLE_PREFIX_MISSING = role -> !role.startsWith(ROLE_PREFIX); public static boolean hasRole(String role) { var auth = getAuthentication(); @@ -61,46 +55,44 @@ public class CurrentUserHelper { if ((Objects.isNull(auth)) || (Objects.isNull(auth.getPrincipal()))) { return false; } - Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); - return containsRole(authorities, role); - + return containsRole(auth.getAuthorities(), role); } public static boolean containsRole(Collection<? extends GrantedAuthority> authorities, String role) { - String roleToCheck; - - if (Objects.nonNull(role) && !role.startsWith(ROLE_PREFIX)) { - roleToCheck = ROLE_PREFIX + role; - } else { - roleToCheck = role; - } - if (Objects.isNull(authorities)) { return false; } + return containsRole(prepareRoleForCheck(role), authorities); + } - return containsRole(roleToCheck, authorities); + static boolean containsRole(String role, Collection<? extends GrantedAuthority> authorities) { + return authorities.stream().anyMatch(a -> isAuthorityEquals(role, a.getAuthority())); } - public static String prepareRoleForCheck(String role) { - if ((Objects.nonNull(role)) && (!role.startsWith(ROLE_PREFIX))) { - return ROLE_PREFIX + role; - } else { - return role; - } + private static boolean isAuthorityEquals(String role, String authority) { + return StringUtils.equalsIgnoreCase(role, authority) || StringUtils.equalsIgnoreCase(prepareRoleForCheck(role), authority); } - public static boolean containsRole(String role, Collection<? extends GrantedAuthority> authorities) { - return authorities.stream().anyMatch(a -> isAuthorityEquals(role, a.getAuthority())); + static String prepareRoleForCheck(String roleToCheck) { + return Optional.ofNullable(roleToCheck) + .filter(IS_ROLE_PREFIX_MISSING) + .map(role -> String.format("%s%s", ROLE_PREFIX, role)) + .orElse(roleToCheck); } - private static boolean isAuthorityEquals(String role, String authority) { - String roleToCheck = prepareRoleForCheck(role); - return StringUtils.equalsIgnoreCase(role, authority) || StringUtils.equalsIgnoreCase(roleToCheck, authority); + public static UserId getCurrentUserId() { + return UserId.from(getSubClaim()); } - static UserId getCurrentUserId() { - return UserId.from(getAuthentication().getName()); + private static String getSubClaim() { + return ((Jwt) getAuthentication().getPrincipal()).getClaim(SUB_CLAIM_KEY); } + public 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/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserService.java index 94b9c3900b2f7decee285d35e220fd28463a5fe5..6ba0fc106713431e94c8974c21f50b93b6ca03a6 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/user/CurrentUserService.java @@ -25,17 +25,14 @@ package de.ozgcloud.alfa.common.user; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import org.keycloak.KeycloakPrincipal; -import org.keycloak.representations.AccessToken; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Service; import de.itvsh.kop.common.errorhandling.TechnicalException; @@ -50,6 +47,10 @@ public class CurrentUserService { static final String USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID = "organisationseinheitId"; + static final String KEYCLOAK_USER_PREFERRED_USERNAME = "preferred_username"; + static final String KEYCLOAK_USER_GIVEN_NAME = "given_name"; + static final String KEYCLOAK_USER_FAMILY_NAME = "family_name"; + @Autowired private UserService userService; @Autowired @@ -65,44 +66,47 @@ public class CurrentUserService { return CurrentUserHelper.containsRole(reachableRoles, role); } - public Collection<GrantedAuthority> getAuthorities() { - return Collections.unmodifiableCollection(new HashSet<GrantedAuthority>(CurrentUserHelper.getAuthentication().getAuthorities())); - } - public UserProfile getUser() { - var dlUser = getDownloadUser(); - if (dlUser.isPresent()) { - return dlUser.get(); - } - - Optional<AccessToken> token = getCurrentSecurityToken(); + return getDownloadUser().orElseGet(this::buildUserProfile); + } + private UserProfile buildUserProfile() { var userBuilder = UserProfile.builder() .id(getUserId()) .authorities(getAuthorities()); - token.ifPresent(t -> userBuilder.userName(t.getPreferredUsername()) - .firstName(t.getGivenName()) - .lastName(t.getFamilyName()) - .organisationseinheitIds(getOrganisationseinheitId(t.getOtherClaims()))); + getCurrentSecurityToken().ifPresent(token -> userBuilder + .userName(token.getClaimAsString(KEYCLOAK_USER_PREFERRED_USERNAME)) + .firstName(token.getClaimAsString(KEYCLOAK_USER_GIVEN_NAME)) + .lastName(token.getClaimAsString(KEYCLOAK_USER_FAMILY_NAME)) + .organisationseinheitIds(getOrganisationsEinheitIds(token))); return userBuilder.build(); } + public Collection<GrantedAuthority> getAuthorities() { + return Collections.unmodifiableCollection(CurrentUserHelper.getAuthentication().getAuthorities()); + } + public UserId getUserId() { return findUserId() .orElseThrow(() -> new TechnicalException("Cannot find internal UserId. Check sync with UserManager or Token Mapper in keycloak.")); } public Optional<UserId> findUserId() { - return Optional.ofNullable( - getSingleClaimValue(ATTRIBUTE_NAME_USER_ID).map(UserId::from) - .orElseGet(() -> userService.getInternalId(CurrentUserHelper.getCurrentUserId()).orElse(null))) + return Optional.ofNullable(getSingleClaimValue(ATTRIBUTE_NAME_USER_ID) + .map(UserId::from) + .orElseGet(this::getInternalId)) .filter(Objects::nonNull); } - List<String> getOrganisationseinheitId(Map<String, Object> claims) { - return Optional.ofNullable(claims.get(USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)) + private UserId getInternalId() { + return userService.getInternalId(CurrentUserHelper.getCurrentUserId()).orElse(null); + } + + List<String> getOrganisationsEinheitIds(Jwt jwt) { + return Optional.ofNullable(jwt) + .map(token -> token.getClaim(USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)) .map(col -> (Collection<?>) col).orElse(Collections.emptyList()) // NOSONAR - Collection.class::cast has type-safty issue .stream().map(Object::toString).toList(); } @@ -114,19 +118,19 @@ public class CurrentUserService { .map(GoofyUserWithFileId::getUser); } - Optional<AccessToken> getCurrentSecurityToken() { - Object principal = CurrentUserHelper.getAuthentication().getPrincipal(); + Optional<String> getSingleClaimValue(String attributeName) { + return getCurrentSecurityToken() + .map(token -> token.getClaim(attributeName)) + .map(String.class::cast); + } + + Optional<Jwt> getCurrentSecurityToken() { + var principal = CurrentUserHelper.getAuthentication().getPrincipal(); - if (principal instanceof KeycloakPrincipal<?> kcPrincipal) { - return Optional.of(kcPrincipal.getKeycloakSecurityContext().getToken()); + if (principal instanceof Jwt kcPrincipal) { + return Optional.of(kcPrincipal); } return Optional.empty(); } - - Optional<String> getSingleClaimValue(String attributeName) { - return getCurrentSecurityToken() - .map(token -> token.getOtherClaims().get(attributeName)) - .map(String.class::cast); - } } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieCommandHandler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieCommandHandler.java index dc735be8bf8f3da848fd631fa28612ff23a444fb..fe54b77f7a60a44ac005f05864c7819587701b36 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieCommandHandler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/historie/HistorieCommandHandler.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Optional; import java.util.function.Predicate; -import org.apache.commons.codec.binary.StringUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/Kommentar.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/Kommentar.java index a522e29cf9e9fd05ca47cc1b663b835235d2a5b0..1dde8e3c04207e104bddf8f3a5986879e6dbc666 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/Kommentar.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/Kommentar.java @@ -28,8 +28,8 @@ import static de.ozgcloud.alfa.common.ValidationMessageCodes.*; import java.time.ZonedDateTime; import java.util.List; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java index a6945fac38dcd6aec6466cb2649f248a8e38801b..7f6e99db26f4255bc06827e0f0ccfd64545b77f4 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.kommentar; -import javax.validation.Valid; +import jakarta.validation.Valid; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarService.java index 8edacd2a909f462b4bd23b20707318bb44acb1f0..4bd41879a07fffedf0e715c4d3adb65ca36575db 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarService.java @@ -26,7 +26,7 @@ package de.ozgcloud.alfa.kommentar; import java.time.ZonedDateTime; import java.util.stream.Stream; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungService.java index d215118ece078659764cff242d4a87101a9bf4a1..d478b7f56b218436f5c30404ad132c4261b331ae 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungService.java @@ -3,7 +3,7 @@ package de.ozgcloud.alfa.loeschanforderung; import java.util.List; import java.util.Optional; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.annotation.Autowired; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMail.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMail.java index 722d7f5b9f2b4e96daa4f228ab42101371d04bb9..0b5a606b86e6ab51a6cd027b4e8caecf8f4320e0 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMail.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMail.java @@ -29,8 +29,8 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Map; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfModel.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfModel.java index 011cfc7be512df6257e987a66cc80c8a0926e952..47457f9b4040d2c3a47f01843b797a1131e227d2 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfModel.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfModel.java @@ -25,9 +25,9 @@ package de.ozgcloud.alfa.postfach; import java.util.List; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlElementWrapper; +import jakarta.xml.bind.annotation.XmlRootElement; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -76,7 +76,7 @@ class PostfachNachrichtPdfModel { @Getter @Builder static class Nachricht { - + @XmlElement private boolean isFirst; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfService.java index 6a71d24ad93dd318d8064f7692b2e81f70a339f3..1ab1d3efdd79fd8b6e2790f6329efcda55278dff 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfService.java @@ -26,8 +26,6 @@ package de.ozgcloud.alfa.postfach; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.time.format.DateTimeFormatter; -import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @@ -40,6 +38,7 @@ import org.springframework.stereotype.Service; import de.itvsh.kop.common.errorhandling.TechnicalException; import de.itvsh.kop.common.pdf.PdfService; +import de.ozgcloud.alfa.common.GermanDateTimeFormatter; import de.ozgcloud.alfa.common.user.UserManagerUrlProvider; import de.ozgcloud.alfa.common.user.UserProfile; import de.ozgcloud.alfa.postfach.PostfachMail.Direction; @@ -56,11 +55,12 @@ class PostfachNachrichtPdfService { static final String FALLBACK_ANTRAGSTELLER_NAME = "Antragsteller"; - private static final DateTimeFormatter CREATED_AT_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss O").withLocale(Locale.GERMANY); - @Autowired private PdfService pdfService; + @Autowired + private GermanDateTimeFormatter germanDateTimeFormatter; + private boolean isFirstNachricht; @Value(PostfachNachrichtPdfService.PDF_TEMPLATE_PATH) @@ -111,7 +111,7 @@ class PostfachNachrichtPdfService { .isFirst(isFirstNachricht()) .subject(postfachMail.getSubject()) .mailBody(postfachMail.getMailBody()) - .createdAt(CREATED_AT_FORMATTER.format(postfachMail.getCreatedAt())) + .createdAt(germanDateTimeFormatter.formatZonedDateTime(postfachMail.getCreatedAt())) .createdBy(buildAbsenderName(postfachMail, antragsteller)) .attachments(postfachMail.getAttachmentNames()) .build(); diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/system/SystemStatusService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/system/SystemStatusService.java index 2659fe7ee8dfa9ae928f6d8b4ef645454b827c38..b188e77aade4147f71521677815a9c569c6bc5d1 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/system/SystemStatusService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/system/SystemStatusService.java @@ -43,4 +43,5 @@ public class SystemStatusService { return false; } } -} + +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java index 71072fbadc071f5fad12a96dd85bf56556e14fa0..35062413fb4a816eaf0263aa284d2bc5b32b4e6a 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java @@ -25,7 +25,7 @@ package de.ozgcloud.alfa.vorgang; import java.util.Optional; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingLandesnetzInfoService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingLandesnetzInfoService.java index 2221f8b9ccb32f3a468b398151be6722d152a568..0f011886bd0a3faf917b2119476b283b8949e5e2 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingLandesnetzInfoService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingLandesnetzInfoService.java @@ -27,7 +27,7 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordSizeConstraint.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordSizeConstraint.java index 7a09abea36f1a4233b26f4487885a884b56dbc26..5910b2d2754dead2289ec19ea5869df79d28ea31 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordSizeConstraint.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordSizeConstraint.java @@ -30,8 +30,8 @@ import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; import de.ozgcloud.alfa.common.ValidationMessageCodes; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidator.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidator.java index 30ee0e6cb971cd815b9ab4aeba9a626cfe2572c9..38ffb406bb2bd60125442acfb55734510a99fd2e 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidator.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidator.java @@ -23,8 +23,8 @@ */ package de.ozgcloud.alfa.vorgang.forwarding; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/RedirectRequest.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/RedirectRequest.java index 832f4b088c63a02a1f7d8a3eb5a10ad1a0489d1a..8ffde941d1a0a2f2747f5eace6246448131c43b0 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/RedirectRequest.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/forwarding/RedirectRequest.java @@ -25,8 +25,8 @@ package de.ozgcloud.alfa.vorgang.forwarding; import static de.ozgcloud.alfa.common.ValidationMessageCodes.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; import org.springframework.validation.annotation.Validated; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/Wiedervorlage.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/Wiedervorlage.java index bcb6b368af74344e0d18efe48f7bdb20a12cb6e3..08871f86e4d81489b0589e0caba54435c6a6f263 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/Wiedervorlage.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/Wiedervorlage.java @@ -29,9 +29,9 @@ import java.time.LocalDate; import java.time.ZonedDateTime; import java.util.List; -import javax.validation.constraints.FutureOrPresent; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java index f44964506366e8a82b60939dd1493db4393b0d79..398f2b6e0905fa5bcae4d27cc9a0703ab569a0dc 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.wiedervorlage; -import javax.validation.Valid; +import jakarta.validation.Valid; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageService.java index 9c496fa1f0b95ab94b73be62dec0d9a05a5f3fe2..660aeefbe8e6a5d1425fd04d4d3b9cedee1a6e8a 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageService.java @@ -31,7 +31,7 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; -import javax.validation.Valid; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; diff --git a/alfa-service/src/main/resources/fop/postfach-nachrichten.xsl b/alfa-service/src/main/resources/fop/postfach-nachrichten.xsl index b030aacd5e9d4f8127ce23fe7a65b7cf5402da04..7442f94c340789ee9a8929ce29d935df370fcac8 100644 --- a/alfa-service/src/main/resources/fop/postfach-nachrichten.xsl +++ b/alfa-service/src/main/resources/fop/postfach-nachrichten.xsl @@ -75,7 +75,7 @@ <xsl:template name="nachricht"> - <fo:block font-size="11pt" margin-bottom="2mm"> + <fo:block font-size="11pt" margin-bottom="2mm" linefeed-treatment="preserve"> <xsl:if test="isFirst='false'"> <fo:leader leader-pattern="rule" leader-length="175mm" rule-style="solid" rule-thickness="1pt"/> </xsl:if> diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/EnvironmentControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/EnvironmentControllerTest.java index 145d1e512f80ce0538074b74fde6272c7269e12a..7ef79567ddc28f389673dc545eab8832d6f26916 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/EnvironmentControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/EnvironmentControllerTest.java @@ -29,12 +29,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; import org.mockito.InjectMocks; import org.mockito.Mock; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import lombok.SneakyThrows; + class EnvironmentControllerTest { private final String PATH = "/api/environment"; @@ -42,7 +44,7 @@ class EnvironmentControllerTest { @InjectMocks private EnvironmentController controller; @Mock - private KeycloakSpringBootProperties kcProperties; + private OAuth2Properties oAuth2Properties; private MockMvc mockMvc; @@ -52,22 +54,30 @@ class EnvironmentControllerTest { } @Test - void loadEnvironment() throws Exception { - mockMvc.perform(get(PATH)).andExpect(status().isOk()); + void shouldReturnOk() throws Exception { + var response = doRequest(); + + response.andExpect(status().isOk()); } @Test void shouldHaveProductionTrueAsDefault() throws Exception { - mockMvc.perform(get(PATH)).andExpect(status().is2xxSuccessful())// - .andExpect(jsonPath("$.production").value(true)); + var response = doRequest(); + + response.andExpect(jsonPath("$.production").value(true)); } @Test void shouldHaveClientId() throws Exception { var client = "goofy"; + when(oAuth2Properties.getResource()).thenReturn(client); + var response = doRequest(); - when(kcProperties.getResource()).thenReturn(client); + response.andExpect(jsonPath("$.clientId").value(client)); + } - mockMvc.perform(get(PATH)).andExpect(jsonPath("$.clientId").value(client)); + @SneakyThrows + private ResultActions doRequest() { + return mockMvc.perform(get(PATH)); } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/JwtAuthConverterTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtAuthConverterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..273deca7fae29bbefd8ffaa6f9f1aec98e7a56af --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtAuthConverterTest.java @@ -0,0 +1,232 @@ +package de.ozgcloud.alfa; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +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.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.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +import com.thedeanda.lorem.LoremIpsum; + +class JwtAuthConverterTest { + + @Spy + @InjectMocks + private final JwtAuthConverter converter = new JwtAuthConverter(); + @Mock + private OAuth2Properties oAuth2Properties; + + @DisplayName("Convert") + @Nested + class TestConvert { + + @Mock + private Jwt jwt; + + @BeforeEach + void mock() { + doReturn(StringUtils.EMPTY).when(converter).getPrincipleClaimName(any()); + doReturn(Collections.emptySet()).when(converter).getAuthorities(any()); + } + + @Test + void shouldGetAuthrorities() { + converter.convert(jwt); + + verify(converter).getAuthorities(jwt); + } + + @Test + void shouldGetPrincipleClaimName() { + converter.convert(jwt); + + verify(converter).getPrincipleClaimName(jwt); + } + + @Test + void shouldReturnJwtAuthenticationToken() { + var token = converter.convert(jwt); + + assertThat(token).isInstanceOf(JwtAuthenticationToken.class); + } + } + + @DisplayName("Get principle claim name") + @Nested + class TestGetPrincipleClaimName { + + private final String subClaimValue = LoremIpsum.getInstance().getWords(1); + private final String principleClaimKey = LoremIpsum.getInstance().getWords(1); + private final String principleClaimValue = LoremIpsum.getInstance().getWords(1); + private final Map<String, Object> claimMap = Map.of(JwtClaimNames.SUB, subClaimValue, principleClaimKey, principleClaimValue); + private final Jwt jwt = JwtTestFactory.create(claimMap); + + @DisplayName("if principle attribute is set") + @Nested + class TestIfPrincipleAttributeIsSet { + + @BeforeEach + void mock() { + when(oAuth2Properties.getPrincipleAttribute()).thenReturn(null); + } + + @Test + void shouleReturnPrincipleAttributeClaimValue() { + var principleClaimName = converter.getPrincipleClaimName(jwt); + + assertThat(principleClaimName).isEqualTo(subClaimValue); + } + } + + @DisplayName("if principle attribute is not set") + @Nested + class TestIfPrincipleAttributeNotSet { + + @BeforeEach + void mock() { + when(oAuth2Properties.getPrincipleAttribute()).thenReturn(principleClaimKey); + } + + @Test + void shouldReturnSubClaimValue() { + var principleClaimName = converter.getPrincipleClaimName(jwt); + + assertThat(principleClaimName).isEqualTo(principleClaimValue); + } + } + } + + @DisplayName("Get authorities") + @Nested + class TestGetAuthorities { + + private final static String SCOPE_CLAIM_KEY = "scope"; + private final static String SCOPE_CLAIM_VALUE = "dummyClaimValue"; + private final Jwt jwt = JwtTestFactory.create(Map.of(SCOPE_CLAIM_KEY, SCOPE_CLAIM_VALUE)); + + @BeforeEach + void mock() { + doReturn(Collections.emptyList()).when(converter).extractResourceRoles(any()); + } + + @Test + void shouldConvertJwt() { + var authorities = converter.getAuthorities(jwt); + + assertThat(authorities).extracting(GrantedAuthority::toString) + .contains(String.format("%s_%s", SCOPE_CLAIM_KEY.toUpperCase(), SCOPE_CLAIM_VALUE)); + } + + @Test + void shouldExtractRoles() { + converter.getAuthorities(jwt); + + verify(converter).extractResourceRoles(jwt); + } + } + + @DisplayName("Extract resource roles") + @Nested + class TestExtractResourceRoles { + + private final Map<String, Object> claimsMap = Map.of(JwtAuthConverter.RESOURCE_ACCESS_KEY, Map.of("dummyKey", "dummyValue")); + private final Jwt jwt = JwtTestFactory.create(claimsMap); + + @Test + void shouldReturnEmptySetOnMissingResourceAccess() { + var claimsMap = Map.<String, Object>of(JwtAuthConverter.RESOURCE_ACCESS_KEY, Collections.emptyMap()); + var jwt = JwtTestFactory.create(claimsMap); + + var resourceRoles = converter.extractResourceRoles(jwt); + + assertThat(resourceRoles).isEmpty(); + } + + @DisplayName("on existing resource access") + @Nested + class TestOnExistingResourceAccess { + + @Test + void shouldCallProperties() { + converter.extractResourceRoles(jwt); + + verify(oAuth2Properties).getResource(); + } + + @Test + void shouldReturnEmptySetOnMissingResourceId() { + when(oAuth2Properties.getResource()).thenReturn(null); + + var resourceRoles = converter.extractResourceRoles(jwt); + + assertThat(resourceRoles).isEmpty(); + } + + @DisplayName("and existing resourceId") + @Nested + class TestAndExistingResourceId { + + private final static String RESOURCE_ID = "dummyResourceId"; + private final Map<String, Object> authoritiesMap = Map.of(JwtAuthConverter.RESOURCE_ACCESS_KEY, + Map.of(RESOURCE_ID, Collections.emptyMap())); + private final Jwt jwt = JwtTestFactory.create(authoritiesMap); + + @BeforeEach + void mock() { + doReturn(Collections.emptySet()).when(converter).extractRoles(any()); + when(oAuth2Properties.getResource()).thenReturn(RESOURCE_ID); + } + + @Test + void shouldExtractRoles() { + converter.extractResourceRoles(jwt); + + verify(converter).extractRoles(any()); + } + } + + } + } + + @DisplayName("Extract roles") + @Nested + class TestExtractRoles { + + @Test + void shouldReturnMappedRoleIfRolesExist() { + var authorities = converter.extractRoles(Map.of(JwtAuthConverter.ROLES_KEY, List.of("dummy"))); + + assertThat(authorities).extracting(SimpleGrantedAuthority::toString).containsExactly("ROLE_dummy"); + } + + @Test + void shouldReturnEmptySetIfNoRoleExists() { + var authorities = converter.extractRoles(Map.of(JwtAuthConverter.ROLES_KEY, Collections.emptyList())); + + assertThat(authorities).isEmpty(); + } + + @Test + void shouldReturnEmptySetIfRolesEntryNotExists() { + var authorities = converter.extractRoles(Collections.emptyMap()); + + assertThat(authorities).isEmpty(); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b3f4ea31829b65c243d3349c2837fe29a3b18aa0 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTestFactory.java @@ -0,0 +1,23 @@ +package de.ozgcloud.alfa; + +import java.time.Instant; +import java.util.Map; + +import org.springframework.security.oauth2.jwt.Jwt; + +import com.thedeanda.lorem.LoremIpsum; + +public class JwtTestFactory { + + public static final String TOKEN_VALUE = LoremIpsum.getInstance().getWords(1); + private static final Instant ISSUE_AT = Instant.now(); + private static final Instant EXPIRES_AT = Instant.now(); + + public static final String DUMMY_HEADER_KEY = "dummyHeaderKey"; + private static final String DUMMY_HEADER_VALUE = "dummyHeaderValue"; + private static final Map<String, Object> HEADERS = Map.of(DUMMY_HEADER_KEY, DUMMY_HEADER_VALUE); + + public static Jwt create(Map<String, Object> claims) { + return new Jwt(TOKEN_VALUE, ISSUE_AT, EXPIRES_AT, HEADERS, claims); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTokenUtilTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTokenUtilTest.java index b9105b305f4a32c1ff3820116743cd3a478903ce..3f1cec61922fc4b62939c88a165ad734fe72a023 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTokenUtilTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/JwtTokenUtilTest.java @@ -59,7 +59,7 @@ class JwtTokenUtilTest { @Mock private DownloadTokenProperties downloadTokenProperties; - private static final String TOKEN_SECRET = "t0pS3cr3t"; + private static final String TOKEN_SECRET = "t0pS3cr3t".repeat(8); private static final int TOKEN_VALIDITY = 60000; @BeforeEach @@ -121,7 +121,7 @@ class JwtTokenUtilTest { @Test void shouldThrowExceptionOnInvalidToken() { - var token = buildToken(UUID.randomUUID().toString(), "invalid_token", TOKEN_VALIDITY); + var token = buildToken(UUID.randomUUID().toString(), "invalid_token".repeat(5), TOKEN_VALIDITY); assertThrows(JWTVerificationException.class, () -> jwtTokenUtil.verifyToken(token)); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/RequestIdFilterTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/RequestIdFilterTest.java index bfaf794ff0962158e404c7af8d1f89720bc384a4..fbcb5735ea29412d836659128ecbcb8505f35fa5 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/RequestIdFilterTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/RequestIdFilterTest.java @@ -29,10 +29,10 @@ import static org.mockito.Mockito.*; import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/GermanDateTimeFormatterTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/GermanDateTimeFormatterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0c4b738d8cefe6f0d736c870827df8c44d7d6ded --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/GermanDateTimeFormatterTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ +package de.ozgcloud.alfa.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.stream.Stream; + +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; + +class GermanDateTimeFormatterTest { + + @Mock + private SystemProperties properties; + + @InjectMocks + private GermanDateTimeFormatter germanDateTimeFormatter; + + @ParameterizedTest + @MethodSource("provideDataForFormatZonedDateTime") + void shouldFormatZonedDateTime(ZonedDateTime date, String zoneId, String expected) { + when(properties.getTimeZone()).thenReturn(ZoneId.of(zoneId)); + + assertThat(germanDateTimeFormatter.formatZonedDateTime(date)).isEqualTo(expected); + } + + private static Stream<Arguments> provideDataForFormatZonedDateTime() { + return Stream.of( + Arguments.of(zonedDateTime(2023, 9, 25, 10, "UTC"), "Europe/Berlin", "25.09.2023 12:00:00 MESZ"), + Arguments.of(zonedDateTime(2023, 9, 25, 23, "UTC"), "Europe/Berlin", "26.09.2023 01:00:00 MESZ"), + Arguments.of(zonedDateTime(2023, 12, 1, 10, "UTC"), "Europe/Berlin", "01.12.2023 11:00:00 MEZ"), + Arguments.of(zonedDateTime(2023, 9, 25, 10, "UTC"), "America/Los_Angeles", "25.09.2023 03:00:00 PDT"), + Arguments.of(zonedDateTime(2023, 9, 25, 4, "UTC"), "America/Los_Angeles", "24.09.2023 21:00:00 PDT"), + Arguments.of(zonedDateTime(2023, 12, 1, 10, "UTC"), "America/Los_Angeles", "01.12.2023 02:00:00 PST"), + Arguments.of(zonedDateTime(2023, 12, 1, 10, "America/Los_Angeles"), "Europe/Berlin", "01.12.2023 19:00:00 MEZ") + ); + } + + private static ZonedDateTime zonedDateTime(int year, int month, int dayOfMonth, int hour, String zoneID) { + return ZonedDateTime.of(year, month, dayOfMonth, hour, 0, 0, 0, ZoneId.of(zoneID)); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileControllerITCase.java index e3a8457b6e01b8680b547ed55596152657b3afa6..b83901c79d420a68c3ebd4741782e0f22b0c7a36 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileControllerITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileControllerITCase.java @@ -26,6 +26,7 @@ package de.ozgcloud.alfa.common.binaryfile; import static de.ozgcloud.alfa.JwtTokenUtil.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -112,7 +113,8 @@ class BinaryFileControllerITCase { } void setTokenToSecuriyContext(String token) throws Exception { - mockMvc.perform(get(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token)) + mockMvc.perform(get(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token) + .with(csrf())) .andExpect(status().isOk()); } } @@ -130,10 +132,10 @@ class BinaryFileControllerITCase { } @Test - void shouldGet401WhenUnautorised() throws Exception { + void shouldGet403WhenUnautorised() throws Exception { when(authentication.isAuthenticated()).thenReturn(Boolean.FALSE); - performRequest().andExpect(status().isUnauthorized()); + performRequest().andExpect(status().isForbidden()); } @Test @@ -145,7 +147,7 @@ class BinaryFileControllerITCase { } ResultActions performRequest() throws Exception { - return mockMvc.perform(get(BinaryFileController.PATH + "/" + fileId.toString())); + return mockMvc.perform(get(BinaryFileController.PATH + "/" + fileId.toString()).with(csrf())); } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileITCase.java index 5d090e6c206eb25921d3bec103a9da73aa23eb3c..6a10133bb7a092eeae2289d4fe9002a4f608820c 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/BinaryFileITCase.java @@ -25,6 +25,7 @@ package de.ozgcloud.alfa.common.binaryfile; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -116,7 +117,7 @@ class BinaryFileITCase { } private ResultActions callEndpoint(MockMultipartFile file, String field) throws Exception { - return mockMvc.perform(multipart(SINGLE_PATH, VorgangHeaderTestFactory.ID, field).file(file)); + return mockMvc.perform(multipart(SINGLE_PATH, VorgangHeaderTestFactory.ID, field).file(file).with(csrf())); } } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandlerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandlerTest.java index 155ac009c8fc0b9604f993dd188a71fde2ed1355..d9cc748ca8211b95706ca0ad8af73fea013a1693 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandlerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/DownloadAuthenticationHandlerTest.java @@ -29,9 +29,10 @@ import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.mockito.Mock; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.jwt.Jwt; class DownloadAuthenticationHandlerTest { @@ -40,18 +41,22 @@ class DownloadAuthenticationHandlerTest { @Nested class TestAuthorizationKeycloak { + @Mock + private Jwt jwt; + private Authentication authentication = mock(Authentication.class); @BeforeEach void init() { when(authentication.isAuthenticated()).thenReturn(Boolean.TRUE); - KeycloakAuthenticationToken keycloakToken = mock(KeycloakAuthenticationToken.class); - when(authentication.getPrincipal()).thenReturn(keycloakToken); + when(authentication.getPrincipal()).thenReturn(jwt); } @Test void shouldAuthenticate() { - assertThat(downloadAuthorizationHandler.check(FileId.createNew(), authentication)).isTrue(); + var check = downloadAuthorizationHandler.check(FileId.createNew(), authentication); + + assertThat(check).isTrue(); } } @@ -69,23 +74,32 @@ class DownloadAuthenticationHandlerTest { @Test void shouldAuthenticate() { - assertThat(downloadAuthorizationHandler.check(fileId, authentication)).isTrue(); + var check = downloadAuthorizationHandler.check(fileId, authentication); + + assertThat(check).isTrue(); } @Test void shouldNotAuthenticateWrongFileId() { - assertThat(downloadAuthorizationHandler.check(FileId.createNew(), authentication)).isFalse(); + var check = downloadAuthorizationHandler.check(FileId.createNew(), authentication); + + assertThat(check).isFalse(); } @Test void shouldNotAuthenticateNoFileId() { - assertThat(downloadAuthorizationHandler.check(null, authentication)).isFalse(); + var check = downloadAuthorizationHandler.check(null, authentication); + + assertThat(check).isFalse(); } @Test void shouldNotAuthenticate() { when(authentication.isAuthenticated()).thenReturn(Boolean.FALSE); - assertThat(downloadAuthorizationHandler.check(fileId, authentication)).isFalse(); + + var check = downloadAuthorizationHandler.check(fileId, authentication); + + assertThat(check).isFalse(); } } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidatorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidatorTest.java index 4ffb8a1e0758d81027e2160f48c8617b3d187a75..ad74b10d3b54551e3cf1171d5e1093f1148957e1 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidatorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/binaryfile/UploadBinaryFileSizeValidatorTest.java @@ -30,7 +30,7 @@ import static org.mockito.Mockito.*; import java.util.Collections; import java.util.Map; -import javax.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidatorContext; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; import org.junit.jupiter.api.BeforeEach; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandITCase.java index 6720703ad4c10512df430355ae65a52841d97e7e..301aa043ce57a76ace03a859b7e79b7c3be90f8a 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandITCase.java @@ -26,6 +26,7 @@ package de.ozgcloud.alfa.common.command; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -88,7 +89,7 @@ public class CommandITCase { } private void createCommand() throws Exception { - mockMvc.perform(post(buildUrl()).content(createContent()).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(post(buildUrl()).with(csrf()).content(createContent()).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()); } @@ -298,6 +299,7 @@ public class CommandITCase { ResultActions doRequest(String content) throws Exception { return mockMvc.perform(post("/api/vorgangs/" + CommandTestFactory.VORGANG_ID + "/relations/" + CommandTestFactory.RELATION_ID + "/" + CommandTestFactory.RELATION_VERSION + "/commands") + .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(content)); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/RequiredOrderValidatorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/RequiredOrderValidatorTest.java index 58c1d44963fd8fa89d42943b42b601ae900550b5..6319e02a44ff12556ba1c5be98143fb13476d430 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/RequiredOrderValidatorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/RequiredOrderValidatorTest.java @@ -3,7 +3,7 @@ package de.ozgcloud.alfa.common.command; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import javax.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidatorContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java index d4754cd7b9f14b02c81f64b645509b9404e9f8d2..d2647b285fc74570cee9c0448eae40b7c37f13ab 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java @@ -81,7 +81,7 @@ class DownloadTokenAuthenticationFilterITCase { @Test void shouldReturnUnauthorised() throws Exception { - String token = DownloadTokenTestFactory.createToken("badSecret", downloadTokenProperties.getValidity()); + String token = DownloadTokenTestFactory.createToken("badSecret".repeat(8), downloadTokenProperties.getValidity()); performRequest(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token) .andExpect(status().isUnauthorized()); diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java index 4a8a712a21b6fad31ecd2a78ca3b68042946ed02..1d3e7c76ea1c13c1364338baf4d0839c39b7c223 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java @@ -29,10 +29,10 @@ import static org.mockito.Mockito.*; import java.io.IOException; import java.util.UUID; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenServiceTest.java index b8fa3484dac7c506414fc628c6682dffdddef280..24bf8ab7f626e0e52f8d6a89b164e3dccb2e7934 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/downloadtoken/DownloadTokenServiceTest.java @@ -33,8 +33,8 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionControllerTest.java index 376bfbc299b60769cfe5231ce72b01ef67133d79..53fcd7d7e86047df2a8a4450c903de8c652bcb19 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionControllerTest.java @@ -29,7 +29,7 @@ import static org.mockito.Mockito.*; import java.util.Collections; import java.util.Map; -import javax.validation.ConstraintViolationException; +import jakarta.validation.ConstraintViolationException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionTestFactory.java index 3f3af3bc7782ad9930753a12d7ab7c3a6d6c52e1..d96c42787390b1b99df6bd412efce278c3dd7335 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/errorhandling/ExceptionTestFactory.java @@ -29,9 +29,9 @@ import static org.mockito.Mockito.*; import java.util.Map; import java.util.UUID; -import javax.validation.ConstraintViolation; -import javax.validation.Path; -import javax.validation.metadata.ConstraintDescriptor; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Path; +import jakarta.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.engine.HibernateConstraintViolation; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserHelperTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3ceac882e23c0f86a8dfb20d11131808060ab4a --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserHelperTest.java @@ -0,0 +1,40 @@ +package de.ozgcloud.alfa.common.user; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class CurrentUserHelperTest { + + @DisplayName("Prepare role for check") + @Nested + class TestPrepareRoleForCheck { + + @Test + void shouldAddPrefixIfMissing() { + var roleWithoutPrefix = UserRole.VERWALTUNG_USER; + + var role = CurrentUserHelper.prepareRoleForCheck(roleWithoutPrefix); + + assertThat(role).isEqualTo(String.format("%s%s", CurrentUserHelper.ROLE_PREFIX, UserRole.VERWALTUNG_USER)); + } + + @Test + void shouldReturnRoleIfPrefixAlreadyExists() { + var roleWithPrefix = String.format("ROLE_%s", UserRole.VERWALTUNG_USER); + + var role = CurrentUserHelper.prepareRoleForCheck(roleWithPrefix); + + assertThat(role).isEqualTo(roleWithPrefix); + } + + @Test + void shouldReturnPassingRoleIfNonNull() { + var role = CurrentUserHelper.prepareRoleForCheck(null); + + assertThat(role).isNull(); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserServiceTest.java index f22c42b6cb253d3b2a1956e9f741d1aaa7c6a076..e363dc185c06592f6e7e11e35f25c131bd6a83d6 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/user/CurrentUserServiceTest.java @@ -32,10 +32,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.keycloak.representations.AccessToken; +import org.mockito.Mock; import org.mockito.Spy; +import org.springframework.security.oauth2.jwt.Jwt; + +import de.ozgcloud.alfa.JwtTestFactory; class CurrentUserServiceTest { @@ -45,34 +49,39 @@ class CurrentUserServiceTest { @Nested class TestGetOrganisationseinheit { + @Mock + private Jwt jwt; + @Test void shouldReturnOrganisationseinheitIdsFromMap() { - Map<String, Object> claims = Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, List.of("1", "2")); + when(jwt.getClaim(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)).thenReturn(List.of("1", "2")); - var result = service.getOrganisationseinheitId(claims); + var result = service.getOrganisationsEinheitIds(jwt); - assertThat(result).contains("1").contains("2"); + assertThat(result).containsExactly("1", "2"); } @Test void shouldReturnOrgaIdAsString() { - var result = service.getOrganisationseinheitId(Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, List.of("1", 2))); + when(jwt.getClaim(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)).thenReturn(List.of("1", 2)); + + var result = service.getOrganisationsEinheitIds(jwt); - assertThat(result).contains("1").contains("2"); + assertThat(result).containsExactly("1", "2"); } @Test void shouldReturnEmptyList() { - Map<String, Object> claims = Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, Collections.emptyList()); + when(jwt.getClaim(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)).thenReturn(Collections.emptyList()); - var result = service.getOrganisationseinheitId(claims); + var result = service.getOrganisationsEinheitIds(jwt); assertThat(result).isEmpty(); } @Test void shouldReturnEmptyListIfNotExists() { - var result = service.getOrganisationseinheitId(Collections.emptyMap()); + var result = service.getOrganisationsEinheitIds(null); assertThat(result).isEmpty(); } @@ -80,6 +89,7 @@ class CurrentUserServiceTest { @Nested class TestGetUserId { + @Test void shouldReturnValueFromClaim() { doReturn(Optional.of(UserProfileTestFactory.ID.toString())).when(service).getSingleClaimValue(any()); @@ -90,31 +100,43 @@ class CurrentUserServiceTest { } } + @DisplayName("Get single claim value") @Nested class TestGetSingleClaimValue { - private static final String ATTRIBUTE_NAME = "userId"; - private static final String ATTRIBUTE_VALUE = "deadbeef"; + @DisplayName("on existing claim") + @Nested + class TestOnExistingClaim { - @Test - void shouldReturnUserAttribute() { - var token = new AccessToken(); - token.setOtherClaims(ATTRIBUTE_NAME, ATTRIBUTE_VALUE); - doReturn(Optional.of(token)).when(service).getCurrentSecurityToken(); + private static final String ATTRIBUTE_NAME = "userId"; + private static final String ATTRIBUTE_VALUE = "deadbeef"; + private static final Map<String, Object> CLAIMS = Map.of(ATTRIBUTE_NAME, ATTRIBUTE_VALUE); - var attribute = service.getSingleClaimValue(ATTRIBUTE_NAME); + @Test + void shouldReturnUserAttribute() { + doReturn(Optional.of(JwtTestFactory.create(CLAIMS))).when(service).getCurrentSecurityToken(); - assertThat(attribute).isPresent().hasValue(ATTRIBUTE_VALUE); + var attribute = service.getSingleClaimValue(ATTRIBUTE_NAME); + + assertThat(attribute).isPresent().hasValue(ATTRIBUTE_VALUE); + } } - @Test - void shouldReturnEmpty() { - var token = new AccessToken(); - doReturn(Optional.of(token)).when(service).getCurrentSecurityToken(); + @DisplayName("on missing claim") + @Nested + class TestOnMissingClaim { + + private static final String ATTRIBUTE_NAME = "non_existing_claim"; + private static final Map<String, Object> CLAIMS = Collections.<String, Object>singletonMap("dummyClaim", "DummyClaimValue"); + + @Test + void shouldReturnEmpty() { + doReturn(Optional.of(JwtTestFactory.create(CLAIMS))).when(service).getCurrentSecurityToken(); - var attribute = service.getSingleClaimValue(ATTRIBUTE_NAME); + var attribute = service.getSingleClaimValue(ATTRIBUTE_NAME); - assertThat(attribute).isEmpty(); + assertThat(attribute).isEmpty(); + } } } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java index deff95fefd19822253e20a1cd819b4bb594af93d..426a7478778d7515642180f96a63bfcbd775db45 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java @@ -26,6 +26,7 @@ package de.ozgcloud.alfa.kommentar; import static de.ozgcloud.alfa.kommentar.KommentarCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -129,9 +130,10 @@ class KommentarCommandITCase { } private ResultActions doRequestByKommentarId(String content) throws Exception { - return mockMvc.perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION) - .contentType(MediaType.APPLICATION_JSON) - .content(content)); + return mockMvc + .perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); } } @@ -145,7 +147,7 @@ class KommentarCommandITCase { void initTest() { when(remoteService.getById(any())).thenReturn(KommentarTestFactory.create()); when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); - + doReturn(UserProfileTestFactory.ID).when(userService).getUserId(); } @@ -201,7 +203,7 @@ class KommentarCommandITCase { private ResultActions doRequestByVorgangId(String content) throws Exception { return mockMvc.perform(post(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, - RELATION_ID_ON_CREATE) + RELATION_ID_ON_CREATE).with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(content)); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandControllerITCase.java index 4ab04b7ef7a132ff4be843f5a8dacf7291e3ebbf..ad33e02b65e595b745d94a65b9f1ede29f7449fe 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandControllerITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/loeschanforderung/LoeschAnforderungCommandControllerITCase.java @@ -2,6 +2,7 @@ package de.ozgcloud.alfa.loeschanforderung; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -124,6 +125,7 @@ class LoeschAnforderungCommandControllerITCase { commandOrder.name()); return mockMvc.perform(post(LoeschAnforderungCommandController.BASE_PATH, LoeschAnforderungTestFactory.ID) + .with(csrf()) .content(requestBody).contentType(MediaType.APPLICATION_JSON).characterEncoding(StandardCharsets.UTF_8.name())); } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceITCase.java index 6788d51ca049d5cbf99a15cfcc6c3670f0857ce1..74d9ef6eda8bc319a8fd7dabdfad2065bbb0d109 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceITCase.java @@ -30,6 +30,8 @@ import static org.mockito.Mockito.*; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Collections; import java.util.List; import java.util.stream.Stream; @@ -45,6 +47,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import de.ozgcloud.alfa.common.user.UserId; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.common.user.UserService; +import de.ozgcloud.alfa.vorgang.AntragstellerTestFactory; import de.ozgcloud.alfa.vorgang.EingangTestFactory; import de.ozgcloud.alfa.vorgang.VorgangWithEingang; import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; @@ -118,6 +121,21 @@ class PostfachNachrichtPdfServiceITCase { } } + @DisplayName("Map Postfach-Nachricht") + @Nested + class TestMapPostfachNachricht { + + private final ZonedDateTime createdAt = ZonedDateTime.of(2023, 9, 25, 10, 0, 0, 0, ZoneId.of("UTC")); + private final PostfachNachrichtPdfData pdfData = PostfachNachrichtPdfDataTestFactory.createBuilder().createdAt(createdAt).build(); + + @Test + void shouldMapCreatedAtWithCorrectTimezoneAndFormatting() { + var nachricht = service.mapPostfachNachricht(pdfData, AntragstellerTestFactory.create()); + + assertThat(nachricht.getCreatedAt()).isEqualTo("25.09.2023 12:00:00 MESZ"); + } + } + private VorgangWithEingang buildVorgangAntragstellerNotSet() { return VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()).build(); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceTest.java index 454c45619a589c2481f4ea86688ef005cbd3d83a..e470d8adc1fdde352d4baaf6e11e2a7bec5ed3b5 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtPdfServiceTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.time.ZonedDateTime; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; @@ -46,6 +45,7 @@ import org.springframework.test.util.ReflectionTestUtils; import de.itvsh.kop.common.errorhandling.TechnicalException; import de.itvsh.kop.common.pdf.PdfService; +import de.ozgcloud.alfa.common.GermanDateTimeFormatter; import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.postfach.PostfachMail.Direction; @@ -62,6 +62,8 @@ class PostfachNachrichtPdfServiceTest { private PostfachNachrichtPdfService service; @Mock private PdfService pdfService; + @Mock + private GermanDateTimeFormatter germanDateTimeFormatter; @DisplayName("Get all as pdf") @Nested @@ -239,17 +241,19 @@ class PostfachNachrichtPdfServiceTest { @Test void shouldMapNachrichtCreatedAt() { + var expected = "formatted date"; + when(germanDateTimeFormatter.formatZonedDateTime(PostfachMailTestFactory.CREATED_AT)).thenReturn(expected); + var nachricht = mapNachricht(); - assertThat(nachricht.getCreatedAt()).isEqualTo("01.01.2000 01:00:00 GMT"); + assertThat(nachricht.getCreatedAt()).isEqualTo(expected); } @Test - void shouldFormatTimeIn24hFormat() { - var pdfNachricht = mapNachricht( - PostfachNachrichtPdfDataTestFactory.createBuilder().createdAt(ZonedDateTime.parse("2019-12-31T13:00:00Z")).build()); + void shouldCallDateFormatter() { + mapNachricht(); - assertThat(pdfNachricht.getCreatedAt()).isEqualTo("31.12.2019 13:00:00 GMT"); + verify(germanDateTimeFormatter).formatZonedDateTime(PostfachMailTestFactory.CREATED_AT); } @Test diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/GrpcAntragstellerTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/GrpcAntragstellerTestFactory.java index cf104ab5b8253e561da74e000b3f60f2131240a9..019daf75b2e632d7755af94667a76015a9bd2f0b 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/GrpcAntragstellerTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/GrpcAntragstellerTestFactory.java @@ -36,7 +36,7 @@ public class GrpcAntragstellerTestFactory { public final static String ANREDE = "HERR/FRAU"; public final static String NACHNAME = LoremIpsum.getInstance().getLastName(); public final static String VORNAME = LoremIpsum.getInstance().getFirstName(); - public final static String GEBURTSDATUM_STR = "1995-03-21"; + public final static String GEBURTSDATUM_STR = "21.03.1995"; public final static String GEBURTSORT = LoremIpsum.getInstance().getCountry(); public final static String GEBURTSNAME = LoremIpsum.getInstance().getFirstName(); public final static String EMAIL = LoremIpsum.getInstance().getEmail(); diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java index d914ccfe173f8a2ee6c0da25e8006821482154e5..8eea6215c5ed51e9cd4d184c627a70ba5ee5a55a 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java @@ -25,6 +25,7 @@ package de.ozgcloud.alfa.vorgang; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.*; @@ -52,6 +53,7 @@ import de.ozgcloud.alfa.common.attacheditem.VorgangAttachedItemTestFactory; import de.ozgcloud.alfa.common.clientattribute.ClientAttributeService; import de.ozgcloud.alfa.common.command.CommandController; import de.ozgcloud.alfa.common.errorhandling.ResourceNotFoundException; +import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.common.user.UserService; import de.ozgcloud.alfa.postfach.PostfachMailController; @@ -81,29 +83,23 @@ class VorgangControllerITCase { @MockBean private VorgangAttachedItemService vorgangAttachedItemService; - @Autowired - private MockMvc mockMvc; - + @MockBean + private CurrentUserService currentUserService; @MockBean private UserService userService; - @BeforeEach - void prepareUserService() { - when(userService.getInternalId(any())).thenReturn(Optional.of(UserProfileTestFactory.INTERNAL_ID)); - } + @Autowired + private MockMvc mockMvc; @BeforeEach - void prepareStatisticController() { + void mock() { + when(currentUserService.getUser()).thenReturn(UserProfileTestFactory.create()); + when(userService.getInternalId(any())).thenReturn(Optional.of(UserProfileTestFactory.INTERNAL_ID)); when(statisticController.getVorgaengeStatistic()).thenReturn(StatisticTestFactory.create()); - } - - @BeforeEach - void prepareVorgangAttachedItemService() { when(vorgangAttachedItemService.findLoeschAnforderung(anyString())).thenReturn(Stream.of(VorgangAttachedItemTestFactory.create())); } @DisplayName("Vorganglist by page and limit") - @WithMockUser @Nested class TestVorgangListByPage { @@ -119,12 +115,16 @@ class VorgangControllerITCase { @Test void shouldReturnResult() throws Exception { - doRequest().andExpect(jsonPath("$._embedded.vorgangHeaderList[0].nextFrist").value(WiedervorlageTestFactory.FRIST_STR)); + var response = doRequest(); + + response.andExpect(jsonPath("$._embedded.vorgangHeaderList[0].nextFrist").value(WiedervorlageTestFactory.FRIST_STR)); } @Test void shouldReturnStatusOk() throws Exception { - doRequest().andExpect(status().isOk()); + var response = doRequest(); + + response.andExpect(status().isOk()); } private ResultActions doRequest() throws Exception { @@ -143,43 +143,56 @@ class VorgangControllerITCase { @Test void shouldContainsNeu() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "neu").value(ByStatusTestFactory.NEU_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "neu").value(ByStatusTestFactory.NEU_COUNT)); } @Test void shouldContainsAngenommen() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "angenommen").value(ByStatusTestFactory.ANGENOMMEN_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "angenommen").value(ByStatusTestFactory.ANGENOMMEN_COUNT)); } @Test void shouldContainsInBearbeitung() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "inBearbeitung").value(ByStatusTestFactory.IN_BEARBEITUNG_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "inBearbeitung").value(ByStatusTestFactory.IN_BEARBEITUNG_COUNT)); } @Test void shouldContainsBeschieden() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "beschieden").value(ByStatusTestFactory.BESCHIEDEN_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "beschieden").value(ByStatusTestFactory.BESCHIEDEN_COUNT)); } @Test void shouldContainsAbgeschlossen() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "abgeschlossen").value(ByStatusTestFactory.ABGESCHLOSSEN_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "abgeschlossen").value(ByStatusTestFactory.ABGESCHLOSSEN_COUNT)); } @Test void shouldContainsVerworfen() throws Exception { - doRequest().andExpect(jsonPath(BY_STATUS_PATH + "verworfen").value(ByStatusTestFactory.VERWORFEN_COUNT)); + var response = doRequest(); + + response.andExpect(jsonPath(BY_STATUS_PATH + "verworfen").value(ByStatusTestFactory.VERWORFEN_COUNT)); } } @Test void shouldContainsWiedervorlagen() throws Exception { - doRequest().andExpect(jsonPath("$.statistic.wiedervorlagen").value(StatisticTestFactory.COUNT_WIEDERVORLAGEN)); + var response = doRequest(); + + response.andExpect(jsonPath("$.statistic.wiedervorlagen").value(StatisticTestFactory.COUNT_WIEDERVORLAGEN)); } } } - @WithMockUser @Nested class TestGetSingleVorgang { @@ -189,26 +202,31 @@ class VorgangControllerITCase { void shouldReturnNotFound() throws Exception { when(remoteService.findVorgangWithEingang(anyString(), any())).thenThrow(new ResourceNotFoundException(Vorgang.class, StringUtils.EMPTY)); - doRequest().andExpect(status().isNotFound()); + var response = doRequest(); + + response.andExpect(status().isNotFound()); } @Test void shouldFormatDateTime() throws Exception { when(remoteService.findVorgangWithEingang(anyString(), any())).thenReturn(VorgangWithEingangTestFactory.create()); - doRequest().andExpect(status().isOk()).andExpect(jsonPath("$.createdAt").value(VorgangHeaderTestFactory.CREATED_AT_STR)); + var response = doRequest(); + + response.andExpect(status().isOk()).andExpect(jsonPath("$.createdAt").value(VorgangHeaderTestFactory.CREATED_AT_STR)); } @Test void shouldHaveAnnehmenLink() throws Exception { when(remoteService.findVorgangWithEingang(anyString(), any())).thenReturn(VorgangWithEingangTestFactory.create()); - doRequest().andExpect(jsonPath("$._links.annehmen.href") + var response = doRequest(); + + response.andExpect(jsonPath("$._links.annehmen.href") .value("http://localhost/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/relations/" + VorgangHeaderTestFactory.ID + "/" + VorgangHeaderTestFactory.VERSION + "/commands")); } - @WithMockUser @Nested class TestLinkToAssignedUser { @@ -216,16 +234,19 @@ class VorgangControllerITCase { void shouldReturnStatusOk() throws Exception { when(remoteService.findVorgangWithEingang(anyString(), any())).thenReturn(VorgangWithEingangTestFactory.create()); - doRequest().andExpect(status().is2xxSuccessful()); + var response = doRequest(); + + response.andExpect(status().is2xxSuccessful()); } @Test void shouldBePresentIfAssigned() throws Exception { when(remoteService.findVorgangWithEingang(anyString(), any())).thenReturn(VorgangWithEingangTestFactory.create()); - doRequest() - .andExpect(jsonPath("$._links.assignedTo.href") - .value(StringEndsWith.endsWith("/api/userProfiles/" + UserProfileTestFactory.ID))); + var response = doRequest(); + + response.andExpect(jsonPath("$._links.assignedTo.href") + .value(StringEndsWith.endsWith("/api/userProfiles/" + UserProfileTestFactory.ID))); } @Test @@ -233,33 +254,42 @@ class VorgangControllerITCase { when(remoteService.findVorgangWithEingang(anyString(), any())) .thenReturn(VorgangWithEingangTestFactory.createBuilder().assignedTo(null).build()); - doRequest().andExpect(jsonPath("$._links.assignedTo.href").doesNotExist()); + var response = doRequest(); + + response.andExpect(jsonPath("$._links.assignedTo.href").doesNotExist()); } } private ResultActions doRequest() throws Exception { - return mockMvc.perform(get(PATH, VorgangHeaderTestFactory.ID)); + return mockMvc.perform(get(PATH, VorgangHeaderTestFactory.ID).with(csrf())); } } @Nested class TestClientAttribute { + @Test void shouldReturnNoContent() throws Exception { - mockMvc - .perform(put("http://localhost/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"hasNewPostfachNachricht\":false}")) - .andExpect(status().isNoContent()); + var content = VorgangHeaderTestFactory.createHasNewPostfachNachrichtContent(false); + + var response = doRequest(content); + + response.andExpect(status().isNoContent()); } @Test void shouldReturnUnprocessableEntity() throws Exception { - mockMvc - .perform(put("http://localhost/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID) - .contentType(MediaType.APPLICATION_JSON) - .content("{\"hasNewPostfachNachricht\":true}")) - .andExpect(status().isUnprocessableEntity()); + var content = VorgangHeaderTestFactory.createHasNewPostfachNachrichtContent(true); + + var response = doRequest(content); + + response.andExpect(status().isUnprocessableEntity()); + } + + private ResultActions doRequest(String content) throws Exception { + return mockMvc.perform(put("/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID).with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); } } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java index e60afed35c82377a91797c6bad99867691bff275..2eb1d65bc814907ccc77bbf9e645c64e69f9b005 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java @@ -28,6 +28,7 @@ import java.util.UUID; import com.thedeanda.lorem.LoremIpsum; +import de.itvsh.kop.common.test.TestUtils; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; @@ -57,4 +58,8 @@ public class VorgangHeaderTestFactory { .createdAt(CREATED_AT) .assignedTo(UserProfileTestFactory.ID); } + + public static String createHasNewPostfachNachrichtContent(boolean hasNewPostfachNachricht) { + return TestUtils.loadTextFile("jsonTemplates/command/hasNewPostfachNachricht.json.tmpl", String.valueOf(hasNewPostfachNachricht)); + } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidatorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidatorTest.java index 1899552eb91bc8feb8b42996b09aacec84dbfa0b..1a5a0a2227b498f3c2360fcacaae5af12f7ac7f3 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidatorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/forwarding/ForwardingPasswordValidatorTest.java @@ -27,8 +27,8 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import javax.validation.ConstraintValidatorContext; -import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidatorContext.ConstraintViolationBuilder; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java index b2948594597319e749674a7af63ece47d55d6ffc..c47e51a4d16204d0e660a16d8f93df57b612655c 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java @@ -26,12 +26,13 @@ package de.ozgcloud.alfa.wiedervorlage; import static de.ozgcloud.alfa.wiedervorlage.WiedervorlageCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +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.time.LocalDate; -import org.assertj.core.internal.bytebuddy.utility.RandomString; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -125,7 +126,7 @@ class WiedervorlageCommandITCase { @DisplayName("for 41 character in Betreff") @Test void createCommandWithLongBetreff() throws Exception { - var content = buildContentWithBetreff(RandomString.make(41)); + var content = buildContentWithBetreff(RandomStringUtils.random(41)); doRequest(content).andExpect(status().isUnprocessableEntity()); } @@ -165,6 +166,7 @@ class WiedervorlageCommandITCase { private ResultActions doRequest(String content) throws Exception { return mockMvc.perform( post(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS, WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION) + .with(csrf()) .contentType(MediaType.APPLICATION_JSON) .content(content)); } @@ -181,7 +183,7 @@ class WiedervorlageCommandITCase { void initTest() { when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); when(wiedervorlageRemoteService.getById(anyString())).thenReturn(WiedervorlageTestFactory.create()); - + doReturn(UserProfileTestFactory.ID).when(userService).getUserId(); } @@ -229,7 +231,7 @@ class WiedervorlageCommandITCase { @DisplayName("for 41 character in Betreff") @Test void createCommandWithLongBetreff() throws Exception { - var content = buildContentWithBetreff(RandomString.make(41)); + var content = buildContentWithBetreff(RandomStringUtils.random(41)); doRequest(content).andExpect(status().isUnprocessableEntity()); } @@ -267,8 +269,9 @@ class WiedervorlageCommandITCase { private ResultActions doRequest(String content) throws Exception { return mockMvc.perform(post(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, RELATION_ID_ON_CREATE) - .contentType(MediaType.APPLICATION_JSON) - .content(content)); + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); } } } \ No newline at end of file diff --git a/alfa-service/src/test/resources/application.yml b/alfa-service/src/test/resources/application.yml index a46d7989678c79a6f2338deb269565b88e026824..90b792d47554dae05fae77dd26ed00e58bf90ed2 100644 --- a/alfa-service/src/test/resources/application.yml +++ b/alfa-service/src/test/resources/application.yml @@ -4,7 +4,19 @@ logging: '[de.itvsh]': INFO '[de.ozgcloud]': INFO, '[org.springframework.security]': WARN - '[org.keycloak.adapters]': WARN + + +ozgcloud: + keycloak: + auth-server-url: https://sso.dev.by.ozg-cloud.de + realm: by-kiel-dev + resource: ${jwt.auth.converter.resource-id} + +jwt: + auth: + converter: + resource-id: alfa + principle-attribute: preferred_username spring: mvc: @@ -19,7 +31,13 @@ spring: multipart: max-file-size: 2GB max-request-size: 2GB - + security: + oauth2: + resourceserver: + jwt: + issuer-uri: ${ozgcloud.keycloak.auth-server-url}/realms/${ozgcloud.keycloak.realm} + jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs + server: http2: enabled: true @@ -51,14 +69,7 @@ management: goofy: production: true - -keycloak: - auth-server-url: http://localhost:8088 - realm: sh-kiel-dev - resource: sh-kiel-dev-goofy - public-client: true - use-resource-role-mappings: true - + grpc: client: pluto: @@ -71,7 +82,7 @@ grpc: kop: auth: token: - secret: XPPWagXn3rDwKG6Ywoir + secret: XPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6Ywoir validity: 60000 upload: maxFileSize: diff --git a/alfa-service/src/test/resources/jsonTemplates/command/hasNewPostfachNachricht.json.tmpl b/alfa-service/src/test/resources/jsonTemplates/command/hasNewPostfachNachricht.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..419bb5bcbb5a8951482425d305d39aa40a9dd9e0 --- /dev/null +++ b/alfa-service/src/test/resources/jsonTemplates/command/hasNewPostfachNachricht.json.tmpl @@ -0,0 +1,3 @@ +{ + "hasNewPostfachNachricht": %s +} \ No newline at end of file diff --git a/alfa-xdomea/pom.xml b/alfa-xdomea/pom.xml index 63bfe4d5a9f21ab7f5d788d9dd52c41b7d5fb68b..61f04572ae58e69f87f3cb228c4682338f5942a0 100644 --- a/alfa-xdomea/pom.xml +++ b/alfa-xdomea/pom.xml @@ -32,7 +32,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>1.15.0-SNAPSHOT</version> + <version>1.16.0-SNAPSHOT</version> </parent> <artifactId>alfa-xdomea</artifactId> @@ -43,8 +43,7 @@ <properties> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> - <jaxb2-maven-plugin.version>2.5.0</jaxb2-maven-plugin.version> - + <jaxb2-maven-plugin.version>3.1.0</jaxb2-maven-plugin.version> </properties> <dependencies> diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DateConverter.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DateConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..b41173ea1bcc9246309dce3ca752d24dc4dbb715 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DateConverter.java @@ -0,0 +1,30 @@ +package de.ozgcloud.alfa.export; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import java.util.Optional; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.springframework.stereotype.Component; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +@Component +class DateConverter { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.GERMAN); + + public Optional<XMLGregorianCalendar> convertGermanFormatToISO(String dateStr) { + try { + return Optional.of(DatatypeFactory.newInstance().newXMLGregorianCalendar(LocalDate.parse(dateStr, DATE_TIME_FORMATTER).toString())); + } catch (DatatypeConfigurationException e) { + throw new TechnicalException(String.format("Error converting date %s to ISO.", dateStr), e); + } catch (Exception e) { + return Optional.empty(); + } + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportData.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportData.java new file mode 100644 index 0000000000000000000000000000000000000000..c6e4888c0865072a025a6cf2ecf2729c408dd078 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportData.java @@ -0,0 +1,22 @@ +package de.ozgcloud.alfa.export; + +import java.util.stream.Stream; + +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +class ExportData { + + private VorgangWithEingang vorgang; + private String exportFilename; + private String xmlFileContent; + private Stream<OzgFile> exportFiles; + +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportFilenameGenerator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportFilenameGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..13471e6d59b8ffd385aad5532c1c9bd982172c04 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportFilenameGenerator.java @@ -0,0 +1,17 @@ +package de.ozgcloud.alfa.export; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import de.ozgcloud.alfa.common.file.OzgFile; + +@Component +class ExportFilenameGenerator { + + @Autowired + private UUIDConverter uuidConverter; + + public String generateExportFilename(OzgFile ozgFile) { + return String.format("%s_%s", uuidConverter.fromObjectId(ozgFile.getId().toString()), ozgFile.getName()); + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/KontaktTypeCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/KontaktTypeCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..0f13079a7a8acba8763844ca936ee785c27ba62e --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/KontaktTypeCreator.java @@ -0,0 +1,62 @@ +package de.ozgcloud.alfa.export; + +import java.util.Optional; +import java.util.function.Predicate; + +import javax.xml.datatype.XMLGregorianCalendar; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import de.ozgcloud.alfa.vorgang.Antragsteller; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.xoev.xdomea.GeburtType; +import de.xoev.xdomea.KontaktType; + +@Component +class KontaktTypeCreator { + + private final Predicate<Antragsteller> HAS_VORNAME = antragsteller -> StringUtils.isNotBlank(antragsteller.getVorname()); + private final Predicate<Antragsteller> HAS_NACHNAME = antragsteller -> StringUtils.isNotBlank(antragsteller.getNachname()); + private final Predicate<Antragsteller> HAS_ANREDE = antragsteller -> StringUtils.isNotBlank(antragsteller.getAnrede()); + private final Predicate<Antragsteller> HAS_VALID_GEBURTSDATUM = this::hasValidGeburtsdatum; + + @Autowired + private NameNatuerlichePersonTypeCreator nameNatuerlichePersonTypeCreator; + + @Autowired + private DateConverter dateConverter; + + public Optional<KontaktType> create(VorgangWithEingang vorgang) { + return getAntragstellerIfHasRequiredData(vorgang).map(this::toKontaktType); + } + + Optional<Antragsteller> getAntragstellerIfHasRequiredData(VorgangWithEingang vorgang) { + return Optional.ofNullable(vorgang.getEingang().getAntragsteller()) + .filter(HAS_VORNAME.or(HAS_NACHNAME).or(HAS_ANREDE).or(HAS_VALID_GEBURTSDATUM)); + } + + KontaktType toKontaktType(Antragsteller antragsteller) { + var kontakt = new KontaktType(); + kontakt.setName(nameNatuerlichePersonTypeCreator.create(antragsteller)); + var geburtsdatumIso = dateConverter.convertGermanFormatToISO(antragsteller.getGeburtsdatum()); + kontakt.setGeburt(createGeburtType(geburtsdatumIso)); + return kontakt; + } + + GeburtType createGeburtType(Optional<XMLGregorianCalendar> geburtsdatum) { + return geburtsdatum.map(this::createGeburtType).orElseGet(GeburtType::new); + } + + private GeburtType createGeburtType(XMLGregorianCalendar datum) { + var geburtType = new GeburtType(); + geburtType.setDatum(datum); + return geburtType; + } + + private boolean hasValidGeburtsdatum(Antragsteller antragsteller) { + return dateConverter.convertGermanFormatToISO(antragsteller.getGeburtsdatum()).isPresent(); + } + +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..285854614408573b9f9238c000e7943ed94800e9 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreator.java @@ -0,0 +1,30 @@ +package de.ozgcloud.alfa.export; + +import static java.util.Optional.*; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import de.ozgcloud.alfa.vorgang.Antragsteller; +import de.xoev.xdomea.AllgemeinerNameType; +import de.xoev.xdomea.NameNatuerlichePersonType; + +@Component +class NameNatuerlichePersonTypeCreator { + + public NameNatuerlichePersonType create(Antragsteller antragsteller) { + var nameNatuerlichPerson = new NameNatuerlichePersonType(); + nameNatuerlichPerson.setAnrede(ofNullable(antragsteller).map(Antragsteller::getAnrede).orElse(StringUtils.EMPTY)); + nameNatuerlichPerson.setVorname( + createAllgemeinerNameType(ofNullable(antragsteller).map(Antragsteller::getVorname).orElse(StringUtils.EMPTY))); + nameNatuerlichPerson.setFamilienname( + createAllgemeinerNameType(ofNullable(antragsteller).map(Antragsteller::getNachname).orElse(StringUtils.EMPTY))); + return nameNatuerlichPerson; + } + + private AllgemeinerNameType createAllgemeinerNameType(String name) { + var allgemeinerName = new AllgemeinerNameType(); + allgemeinerName.setName(name); + return allgemeinerName; + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreator.java index d1fb7a35542796cc1cfe465edb13f9852d50fd1d..593e0ef8195aed2d85c3d8290317e80f5efadf5c 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreator.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreator.java @@ -11,17 +11,13 @@ import de.xoev.xdomea.PrimaerdokumentType; class PrimaerdokumentTypeCreator { @Autowired - private UUIDConverter uuidConverter; + private ExportFilenameGenerator exportFilenameGenerator; public PrimaerdokumentType create(VorgangWithEingang vorgang, OzgFile ozgFile) { var primaerdokument = new PrimaerdokumentType(); - primaerdokument.setDateiname(createDateiname(ozgFile)); + primaerdokument.setDateiname(exportFilenameGenerator.generateExportFilename(ozgFile)); primaerdokument.setDateinameOriginal(ozgFile.getName()); primaerdokument.setErsteller(vorgang.getEingang().getHeader().getFormEngineName()); return primaerdokument; } - - String createDateiname(OzgFile ozgFile) { - return String.format("%s_%s", uuidConverter.fromObjectId(ozgFile.getId().toString()), ozgFile.getName()); - } } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/VorgangTypeCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/VorgangTypeCreator.java index 6fd299fbea09cdd2bfcbe25324412bc7cf14f930..792143b96e0074e5ffdbcd1c48a204759d8d710e 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/VorgangTypeCreator.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/VorgangTypeCreator.java @@ -22,12 +22,16 @@ class VorgangTypeCreator { @Autowired private DokumentTypeCreator dokumentTypeCreator; + @Autowired + private KontaktTypeCreator kontaktTypeCreator; + public VorgangType create(VorgangWithEingang vorgangWithEingang, AnwendungsspezifischeErweiterungType erweiterung) { var vorgang = new VorgangType(); vorgang.setAnwendungsspezifischeErweiterung(erweiterung); vorgang.setIdentifikation(createIdentifikation()); vorgang.setAllgemeineMetadaten(createAllgemeineMetadaten(vorgangWithEingang)); vorgang.getDokument().addAll(dokumentTypeCreator.create(vorgangWithEingang).toList()); + kontaktTypeCreator.create(vorgangWithEingang).ifPresent(vorgang.getKontakt()::add); return vorgang; } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaNamespacePrefixMapper.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaNamespacePrefixMapper.java index 4730cee16db3ad6c5819946deb8e87ebc5389672..81bb3fd6a3188265b05f3d731bd096a6a78ae8b8 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaNamespacePrefixMapper.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaNamespacePrefixMapper.java @@ -2,10 +2,9 @@ package de.ozgcloud.alfa.export; import java.util.Map; +import org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper; import org.springframework.stereotype.Component; -import com.sun.xml.bind.marshaller.NamespacePrefixMapper; - @Component class XDomeaNamespacePrefixMapper extends NamespacePrefixMapper { diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaService.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaService.java index c7012b1dd4b9d9d65186a25aaf3ceedf217864a1..ae924df290489e4851084caec61c45654da41d89 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaService.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaService.java @@ -1,5 +1,8 @@ package de.ozgcloud.alfa.export; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -10,6 +13,7 @@ import java.util.zip.ZipOutputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import de.itvsh.kop.common.binaryfile.TempFileUtils; import de.itvsh.kop.common.errorhandling.TechnicalException; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.vorgang.VorgangController; @@ -34,18 +38,21 @@ class XDomeaService { @Autowired private ExportFileService exportFileService; + @Autowired + private ExportFilenameGenerator exportFilenameGenerator; + public void writeExport(String vorgangId, String filenameId, OutputStream out) { + var exportData = collectExportData(vorgangId, filenameId); + var zipFile = createZipFile(exportData); + writeZipFileContent(zipFile, out); + } + + ExportData collectExportData(String vorgangId, String filenameId) { var vorgang = vorgangController.getVorgang(vorgangId); - var fileName = buildXmlFilename(filenameId); var xmlContent = createXmlContent(vorgang); + var filename = buildXmlFilename(filenameId); var ozgFiles = exportFileService.getAllPdfs(vorgang.getEingang().getId()); - - try (var zipOutputStream = new ZipOutputStream(out)) { - putZipEntry(fileName, xmlContent, zipOutputStream); - putFilesIntoZip(ozgFiles, zipOutputStream); - } catch (Exception e) { - throw new TechnicalException("Error creating XDomea zip file", e); - } + return ExportData.builder().vorgang(vorgang).xmlFileContent(xmlContent).exportFilename(filename).exportFiles(ozgFiles).build(); } String buildXmlFilename(String filenameId) { @@ -56,6 +63,17 @@ class XDomeaService { return xDomeaXmlMarshaller.marshal(abgabeCreator.create(vorgang)); } + File createZipFile(ExportData exportData) { + var file = TempFileUtils.createTmpFile().toFile(); + try (var zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) { + putZipEntry(exportData.getExportFilename(), exportData.getXmlFileContent(), zipOutputStream); + putFilesIntoZip(exportData.getExportFiles(), zipOutputStream); + return file; + } catch (Exception e) { + throw new TechnicalException("Error creating XDomea zip file", e); + } + } + void putZipEntry(String fileName, String fileData, ZipOutputStream zipOutputStream) throws IOException { var entry = new ZipEntry(fileName); zipOutputStream.putNextEntry(entry); @@ -70,9 +88,17 @@ class XDomeaService { } void putOzgFileIntoZip(OzgFile ozgFile, ZipOutputStream zipOutputStream) throws IOException { - var entry = new ZipEntry(ozgFile.getName()); + var entry = new ZipEntry(exportFilenameGenerator.generateExportFilename(ozgFile)); zipOutputStream.putNextEntry(entry); exportFileService.writeOzgFile(ozgFile.getId(), zipOutputStream); zipOutputStream.closeEntry(); } + + void writeZipFileContent(File file, OutputStream outputStream) { + try (var fileInputStream = new FileInputStream(file)) { + fileInputStream.transferTo(outputStream); + } catch (Exception e) { + throw new TechnicalException("Error writting XDomea zip file to output stream", e); + } + } } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaXmlMarshallerConfiguration.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaXmlMarshallerConfiguration.java index 964d431c324c4e4af2a063a8dce2d2b473cab7ad..b6b8bd1a0dd4ae9d91e6a8442cb1dc9828da3e4f 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaXmlMarshallerConfiguration.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XDomeaXmlMarshallerConfiguration.java @@ -3,7 +3,7 @@ package de.ozgcloud.alfa.export; import java.util.HashMap; import java.util.Map; -import javax.xml.bind.Marshaller; +import jakarta.xml.bind.Marshaller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -14,7 +14,7 @@ import org.springframework.oxm.jaxb.Jaxb2Marshaller; class XDomeaXmlMarshallerConfiguration { static final boolean JAXB_FORMATTED_OUTPUT = true; - static final String PROPERTY_NAMESPACE_PREFIX_MAPPER = "com.sun.xml.bind.namespacePrefixMapper"; + static final String PROPERTY_NAMESPACE_PREFIX_MAPPER = "org.glassfish.jaxb.namespacePrefixMapper"; static final String CONTEXT_PATH = "de.xoev.xdomea"; @Autowired diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DateConverterTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DateConverterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c296eff263a96f41fe254f6fdfe07e8520766a16 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DateConverterTest.java @@ -0,0 +1,72 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Optional; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +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.InjectMocks; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +class DateConverterTest { + + @InjectMocks + private DateConverter converter; + + @Nested + class TestConvertGermanFormatToISO { + + private static final int YEAR = 2023; + private static final int MONTH = 2; + private static final int DAY = 14; + + private static final String DATE = String.format("%s.%s%s.%s", DAY, 0, MONTH, YEAR); + + @Test + void shouldConvert() { + var converted = callConverter(DATE); + + assertThat(converted).get().extracting(XMLGregorianCalendar::getYear, XMLGregorianCalendar::getMonth, XMLGregorianCalendar::getDay) + .containsExactly(YEAR, MONTH, DAY); + } + + @ParameterizedTest + @ValueSource(strings = { "2005-10-10", "abc", "19. February 1970", " ", "" }) + void shouldReturnEmpty(String dateStr) { + var converted = callConverter(dateStr); + + assertThat(converted).isEmpty(); + } + + @Test + void shouldReturnEmptyIfDateIsNull() { + var converted = callConverter(null); + + assertThat(converted).isEmpty(); + } + + @Test + void shouldThrowTechnicalException() { + try (MockedStatic<DatatypeFactory> datatypeFactory = Mockito.mockStatic(DatatypeFactory.class)) { + datatypeFactory.when(DatatypeFactory::newInstance).thenThrow(DatatypeConfigurationException.class); + + assertThatThrownBy(() -> callConverter(DATE)).isInstanceOf(TechnicalException.class); + } + } + + private Optional<XMLGregorianCalendar> callConverter(String dateStr) { + return converter.convertGermanFormatToISO(dateStr); + } + } + +} \ No newline at end of file diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DokumentTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DokumentTypeCreatorTest.java index 0bf790c21d424b1c64d84ffe28a6e076287872a6..45015d4e9a5f95dd66bf6bc57c7e4f3970067425 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DokumentTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/DokumentTypeCreatorTest.java @@ -206,14 +206,14 @@ class DokumentTypeCreatorTest { void shouldHaveKennzeichen() { var allgemeineMetadaten = creator.createAllgemeineMetadaten(ozgFile); - assertThat(allgemeineMetadaten.getKennzeichen()).isEqualTo(StringUtils.EMPTY); + assertThat(allgemeineMetadaten.getKennzeichen()).isEmpty(); } @Test void shouldHaveBemerkung() { var allgemeineMetadaten = creator.createAllgemeineMetadaten(ozgFile); - assertThat(allgemeineMetadaten.getBemerkung()).isEqualTo(StringUtils.EMPTY); + assertThat(allgemeineMetadaten.getBemerkung()).isEmpty(); } @Test diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportDataTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2be3ee462e90de9997dff74f5b9d145cbb873256 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportDataTestFactory.java @@ -0,0 +1,25 @@ +package de.ozgcloud.alfa.export; + +import java.util.stream.Stream; + +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; + +class ExportDataTestFactory { + + public static final String EXPORT_FILENAME = "file.zip"; + public static final String XML_FILE_CONTENT = "<xml></xml>"; + public static final OzgFile OZG_FILE = OzgFileTestFactory.create(); + public static final VorgangWithEingang VORGANG = VorgangWithEingangTestFactory.create(); + + public static ExportData create() { + return createBuilder().build(); + } + + public static ExportData.ExportDataBuilder createBuilder() { + return ExportData.builder().exportFilename(EXPORT_FILENAME).xmlFileContent(XML_FILE_CONTENT).vorgang(VORGANG) + .exportFiles(Stream.of(OZG_FILE)); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..66d6480e708665d465935f20010c1bdc5cba38f6 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java @@ -0,0 +1,55 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +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 de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; + +class ExportFilenameGeneratorTest { + + @InjectMocks + private ExportFilenameGenerator generator; + + @Mock + private UUIDConverter uuidConverter; + + @Nested + class TestGenerateExportFilename { + + private static final String UUID = "64a820d3-6285-172a-c028-0000000026d0"; + + private final OzgFile ozgFile = OzgFileTestFactory.create(); + + @BeforeEach + void init() { + when(uuidConverter.fromObjectId(ozgFile.getId().toString())).thenReturn(UUID); + } + + @Test + void shouldConvertObjectId() { + callGenerator(); + + verify(uuidConverter).fromObjectId(ozgFile.getId().toString()); + } + + @Test + void shouldGenerate() { + var filename = callGenerator(); + + assertThat(filename).matches(String.format("^%s_%s$", TestUtils.UUID_REGEX, ozgFile.getName())); + } + + private String callGenerator() { + return generator.generateExportFilename(ozgFile); + } + } + +} \ No newline at end of file diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FeldGruppeTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FeldGruppeTypeCreatorTest.java index 95027d111a4d367dcdfa559b31a28c4ceea41491..2ca1494a2bd482efb4d3fd9a3deb931ad8ec1b8b 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FeldGruppeTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FeldGruppeTypeCreatorTest.java @@ -205,7 +205,7 @@ class FeldGruppeTypeCreatorTest { void shouldBeEmptyStringForNullEingang() { var created = create(VorgangWithEingangTestFactory.createBuilder().eingang(null).build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -214,7 +214,7 @@ class FeldGruppeTypeCreatorTest { VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -225,7 +225,7 @@ class FeldGruppeTypeCreatorTest { .build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @DisplayName("DatentypCode") @@ -293,7 +293,7 @@ class FeldGruppeTypeCreatorTest { void shouldBeEmptyStringForNullEingang() { var created = create(VorgangWithEingangTestFactory.createBuilder().eingang(null).build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -302,7 +302,7 @@ class FeldGruppeTypeCreatorTest { VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -313,7 +313,7 @@ class FeldGruppeTypeCreatorTest { .build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @DisplayName("DatentypCode") @@ -381,7 +381,7 @@ class FeldGruppeTypeCreatorTest { void shouldBeEmptyStringForNullEingang() { var created = create(VorgangWithEingangTestFactory.createBuilder().eingang(null).build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -390,7 +390,7 @@ class FeldGruppeTypeCreatorTest { VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -401,7 +401,7 @@ class FeldGruppeTypeCreatorTest { .build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @DisplayName("DatentypCode") @@ -469,7 +469,7 @@ class FeldGruppeTypeCreatorTest { void shouldBeEmptyStringForNullEingang() { var created = create(VorgangWithEingangTestFactory.createBuilder().eingang(null).build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -478,7 +478,7 @@ class FeldGruppeTypeCreatorTest { VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @Test @@ -489,7 +489,7 @@ class FeldGruppeTypeCreatorTest { .build()) .build()); - assertThat(created).extracting(FeldType::getWert).isEqualTo(StringUtils.EMPTY); + assertThat(created.getWert()).isEmpty(); } @DisplayName("DatentypCode") diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FormatTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FormatTypeCreatorTest.java index be881eb41bb7ea565008bf5c9d0bcb2f94ba114b..a15c43215ff5d4ebfd44461fc74a75d44fb70010 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FormatTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/FormatTypeCreatorTest.java @@ -64,14 +64,14 @@ class FormatTypeCreatorTest { void shouldHaveSonstigerName() { var format = creator.create(vorgang, ozgFile); - assertThat(format.getSonstigerName()).isEqualTo(StringUtils.EMPTY); + assertThat(format.getSonstigerName()).isEmpty(); } @Test void shouldHaveVersion() { var format = creator.create(vorgang, ozgFile); - assertThat(format.getVersion()).isEqualTo(StringUtils.EMPTY); + assertThat(format.getVersion()).isEmpty(); } } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/GeburtTypeTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/GeburtTypeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..34c8fef41b03a4f5c1c31c51a0c23735f1d61522 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/GeburtTypeTestFactory.java @@ -0,0 +1,10 @@ +package de.ozgcloud.alfa.export; + +import de.xoev.xdomea.GeburtType; + +public class GeburtTypeTestFactory { + + public static GeburtType create() { + return new GeburtType(); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeCreatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9a7d11824af1db52a8e6c21031f132194149b68 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeCreatorTest.java @@ -0,0 +1,244 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import javax.xml.datatype.XMLGregorianCalendar; + +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 de.ozgcloud.alfa.vorgang.Antragsteller; +import de.ozgcloud.alfa.vorgang.AntragstellerTestFactory; +import de.ozgcloud.alfa.vorgang.EingangTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; +import de.xoev.xdomea.GeburtType; +import de.xoev.xdomea.KontaktType; +import de.xoev.xdomea.NameNatuerlichePersonType; + +class KontaktTypeCreatorTest { + + @Spy + @InjectMocks + private KontaktTypeCreator creator; + + @Mock + private NameNatuerlichePersonTypeCreator nameNatuerlichePersonTypeCreator; + + @Mock + private DateConverter dateConverter; + + @Nested + class TestCreate { + + @Nested + class TestWithAntragsteller { + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + private final KontaktType kontaktType = KontaktTypeTestFactory.create(); + + @BeforeEach + void setUp() { + doReturn(Optional.of(vorgang.getEingang().getAntragsteller())).when(creator).getAntragstellerIfHasRequiredData(vorgang); + doReturn(kontaktType).when(creator).toKontaktType(vorgang.getEingang().getAntragsteller()); + } + + @Test + void shouldGetAntragsteller() { + callCreator(vorgang); + + verify(creator).getAntragstellerIfHasRequiredData(vorgang); + } + + @Test + void shouldMapToKontaktType() { + callCreator(vorgang); + + verify(creator).toKontaktType(vorgang.getEingang().getAntragsteller()); + } + + @Test + void shouldReturnKontakt() { + var kontakt = callCreator(vorgang); + + assertThat(kontakt).get().isEqualTo(kontaktType); + } + } + + @Nested + class TestWithoutAntragsteller { + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.createBuilder() + .eingang(EingangTestFactory.createBuilder().antragsteller(null).build()) + .build(); + + @BeforeEach + void setUp() { + doReturn(Optional.empty()).when(creator).getAntragstellerIfHasRequiredData(vorgang); + } + + @Test + void shouldBeEmpty() { + var kontakt = callCreator(vorgang); + + assertThat(kontakt).isEmpty(); + } + } + + private Optional<KontaktType> callCreator(VorgangWithEingang vorgang) { + return creator.create(vorgang); + } + } + + @Nested + class TestToKontaktType { + + @Mock + private XMLGregorianCalendar geburtsdatum; + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + private final NameNatuerlichePersonType nameNatuerlichePerson = NameNatuerlichePersonTypeTestFactory.create(); + private final GeburtType geburtType = GeburtTypeTestFactory.create(); + + @BeforeEach + void setUp() { + when(nameNatuerlichePersonTypeCreator.create(vorgang.getEingang().getAntragsteller())).thenReturn(nameNatuerlichePerson); + when(dateConverter.convertGermanFormatToISO(AntragstellerTestFactory.GEBURTSDATUM_STR)).thenReturn(Optional.of(geburtsdatum)); + doReturn(geburtType).when(creator).createGeburtType(Optional.of(geburtsdatum)); + } + + @Test + void shouldCreateNameNatuerlichePerson() { + callCreator(vorgang); + + verify(nameNatuerlichePersonTypeCreator).create(vorgang.getEingang().getAntragsteller()); + } + + @Test + void shouldHaveNameNatuerlichePerson() { + var kontakt = callCreator(vorgang); + + assertThat(kontakt.getName()).isEqualTo(nameNatuerlichePerson); + } + + @Test + void shouldCallDateConverter() { + callCreator(vorgang); + + verify(dateConverter).convertGermanFormatToISO(AntragstellerTestFactory.GEBURTSDATUM_STR); + } + + @Test + void shouldCreateGeburtType() { + callCreator(vorgang); + + verify(creator).createGeburtType(Optional.of(geburtsdatum)); + } + + @Test + void shouldHaveGeburtType() { + var kontakt = callCreator(vorgang); + + assertThat(kontakt.getGeburt()).isEqualTo(geburtType); + } + + private KontaktType callCreator(VorgangWithEingang vorgang) { + return creator.toKontaktType(vorgang.getEingang().getAntragsteller()); + } + } + + @Nested + class TestCreateGeburtType { + + @Mock + private XMLGregorianCalendar geburtsdatum; + + @Test + void shouldCreate() { + var geburtType = callCreator(Optional.of(geburtsdatum)); + + assertThat(geburtType.getDatum()).isEqualTo(geburtsdatum); + } + + @Test + void shouldCreateEmptyGeburt() { + var geburtType = callCreator(Optional.empty()); + + assertThat(geburtType.getDatum()).isNull(); + } + + private GeburtType callCreator(Optional<XMLGregorianCalendar> geburtsdatum) { + return creator.createGeburtType(geburtsdatum); + } + } + + @Nested + class TestGetAntragstellerIfHasRequiredData { + + @Mock + private XMLGregorianCalendar geburtsdatum; + + @Test + void shouldBeEmptyIfAntragstellerNull() { + var antragsteller = callCreator(buildVorgang(null)); + + assertThat(antragsteller).isEmpty(); + } + + @Test + void shouldBeEmptyIfNoneOfRequiredDataExists() { + when(dateConverter.convertGermanFormatToISO(null)).thenReturn(Optional.empty()); + + var antragsteller = callCreator(buildVorgang(Antragsteller.builder().build())); + + assertThat(antragsteller).isEmpty(); + } + + @Test + void shouldNotBeEmptyIfHasVorname() { + var antragsteller = callCreator(buildVorgang(Antragsteller.builder().vorname(AntragstellerTestFactory.VORNAME).build())); + + assertThat(antragsteller).isNotEmpty(); + } + + @Test + void shouldNotBeEmptyIfHasNachname() { + var antragsteller = callCreator(buildVorgang(Antragsteller.builder().nachname(AntragstellerTestFactory.NACHNAME).build())); + + assertThat(antragsteller).isNotEmpty(); + } + + @Test + void shouldNotBeEmptyIfHasAnrede() { + var antragsteller = callCreator(buildVorgang(Antragsteller.builder().anrede(AntragstellerTestFactory.ANREDE).build())); + + assertThat(antragsteller).isNotEmpty(); + } + + @Test + void shouldNotBeEmptyIfHasGeburtsdatum() { + when(dateConverter.convertGermanFormatToISO(AntragstellerTestFactory.GEBURTSDATUM_STR)).thenReturn(Optional.of(geburtsdatum)); + + var antragsteller = callCreator(buildVorgang(Antragsteller.builder().geburtsdatum(AntragstellerTestFactory.GEBURTSDATUM_STR).build())); + + assertThat(antragsteller).isNotEmpty(); + } + + private Optional<Antragsteller> callCreator(VorgangWithEingang vorgang) { + return creator.getAntragstellerIfHasRequiredData(vorgang); + } + + private VorgangWithEingang buildVorgang(Antragsteller antragsteller) { + return VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(antragsteller).build()) + .build(); + } + } + +} \ No newline at end of file diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3daad690a0d408df23c9e0b75dbee73720a128cd --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/KontaktTypeTestFactory.java @@ -0,0 +1,10 @@ +package de.ozgcloud.alfa.export; + +import de.xoev.xdomea.KontaktType; + +public class KontaktTypeTestFactory { + + public static KontaktType create() { + return new KontaktType(); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7124b6f846a1cd545df3a71c4567f2f42c166f70 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeCreatorTest.java @@ -0,0 +1,93 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; + +import de.ozgcloud.alfa.vorgang.Antragsteller; +import de.ozgcloud.alfa.vorgang.AntragstellerTestFactory; +import de.xoev.xdomea.NameNatuerlichePersonType; + +class NameNatuerlichePersonTypeCreatorTest { + + @InjectMocks + private NameNatuerlichePersonTypeCreator creator; + + @Nested + class TestCreate { + + @Test + void shouldHaveAnrede() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.create()); + + assertThat(nameNatuerlichePerson.getAnrede()).isEqualTo(AntragstellerTestFactory.ANREDE); + } + + @Test + void shouldHaveEmptyAnrede() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.createBuilder().anrede(null).build()); + + assertThat(nameNatuerlichePerson.getAnrede()).isEmpty(); + } + + @Test + void shouldHaveVorname() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.create()); + + assertThat(nameNatuerlichePerson.getVorname().getName()).isEqualTo(AntragstellerTestFactory.VORNAME); + } + + @Test + void shouldHaveEmptyVorname() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.createBuilder().vorname(null).build()); + + assertThat(nameNatuerlichePerson.getVorname().getName()).isEmpty(); + } + + @Test + void shouldHaveFamilienname() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.create()); + + assertThat(nameNatuerlichePerson.getFamilienname().getName()).isEqualTo(AntragstellerTestFactory.NACHNAME); + } + + @Test + void shouldHaveEmptyFamilienname() { + var nameNatuerlichePerson = callCreator(AntragstellerTestFactory.createBuilder().nachname(null).build()); + + assertThat(nameNatuerlichePerson.getFamilienname().getName()).isEmpty(); + } + + @Nested + class TestWhenNullAntragsteller { + + @Test + void shouldHaveEmptyAnrede() { + var nameNatuerlichePerson = callCreator(null); + + assertThat(nameNatuerlichePerson.getAnrede()).isEmpty(); + } + + @Test + void shouldHaveEmptyVorname() { + var nameNatuerlichePerson = callCreator(null); + + assertThat(nameNatuerlichePerson.getVorname().getName()).isEmpty(); + } + + @Test + void shouldHaveEmptyFamilienname() { + var nameNatuerlichePerson = callCreator(null); + + assertThat(nameNatuerlichePerson.getFamilienname().getName()).isEmpty(); + } + } + + private NameNatuerlichePersonType callCreator(Antragsteller antragsteller) { + return creator.create(antragsteller); + } + } + +} \ No newline at end of file diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..79abd234311aa7c864b7b146f932aa9197c5d980 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/NameNatuerlichePersonTypeTestFactory.java @@ -0,0 +1,10 @@ +package de.ozgcloud.alfa.export; + +import de.xoev.xdomea.NameNatuerlichePersonType; + +public class NameNatuerlichePersonTypeTestFactory { + + public static NameNatuerlichePersonType create() { + return new NameNatuerlichePersonType(); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreatorTest.java index 4446ecacf545501b69dc7b0a45da999411ef8ce2..db307b1e5da896283a48d60c7e31da70ecba26d9 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/PrimaerdokumentTypeCreatorTest.java @@ -10,7 +10,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.alfa.common.TestUtils; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; import de.ozgcloud.alfa.vorgang.EingangHeaderTestFactory; @@ -24,7 +23,7 @@ class PrimaerdokumentTypeCreatorTest { private PrimaerdokumentTypeCreator creator; @Mock - private UUIDConverter uuidConverter; + private ExportFilenameGenerator exportFilenameGenerator; @Nested class TestCreate { @@ -34,14 +33,14 @@ class PrimaerdokumentTypeCreatorTest { @BeforeEach void init() { - doReturn(ozgFile.getName()).when(creator).createDateiname(ozgFile); + when(exportFilenameGenerator.generateExportFilename(ozgFile)).thenReturn(ozgFile.getName()); } @Test - void shouldCreateDateiname() { + void shouldGenerateExportFilename() { creator.create(vorgang, ozgFile); - verify(creator).createDateiname(ozgFile); + verify(exportFilenameGenerator).generateExportFilename(ozgFile); } @Test @@ -65,29 +64,4 @@ class PrimaerdokumentTypeCreatorTest { assertThat(primaerdokument.getErsteller()).isEqualTo(EingangHeaderTestFactory.FORM_ENGINE_NAME); } } - - @Nested - class TestCreateDateiname { - - private final OzgFile ozgFile = OzgFileTestFactory.create(); - - @BeforeEach - void init() { - when(uuidConverter.fromObjectId(ozgFile.getId().toString())).thenReturn("64a820d3-6285-172a-c028-0000000026d0"); - } - - @Test - void shouldConvertObjectId() { - creator.createDateiname(ozgFile); - - verify(uuidConverter).fromObjectId(ozgFile.getId().toString()); - } - - @Test - void shouldCreate() { - var dateiname = creator.createDateiname(ozgFile); - - assertThat(dateiname).matches(String.format("^%s_%s$", TestUtils.UUID_REGEX, ozgFile.getName())); - } - } } \ No newline at end of file diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/VorgangTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/VorgangTypeCreatorTest.java index 62cd4dd2e55337719205f5586d24aa3a9234b180..f97c9906d1eaa1c8cfe8414b2b574c95983e8c5b 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/VorgangTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/VorgangTypeCreatorTest.java @@ -3,9 +3,9 @@ package de.ozgcloud.alfa.export; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.Optional; import java.util.stream.Stream; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -21,6 +21,8 @@ import de.xoev.xdomea.AllgemeineMetadatenType; import de.xoev.xdomea.AnwendungsspezifischeErweiterungType; import de.xoev.xdomea.DokumentType; import de.xoev.xdomea.IdentifikationObjektType; +import de.xoev.xdomea.KontaktType; +import de.xoev.xdomea.VorgangType; class VorgangTypeCreatorTest { @@ -31,6 +33,9 @@ class VorgangTypeCreatorTest { @Mock private DokumentTypeCreator dokumentTypeCreator; + @Mock + private KontaktTypeCreator kontaktTypeCreator; + @Nested class TestCreate { @@ -40,6 +45,7 @@ class VorgangTypeCreatorTest { private final AllgemeineMetadatenType allgemeineMetadaten = AllgemeineMetadatenTypeTestFactory.create(); private final AnwendungsspezifischeErweiterungType anwendungsspezifischeErweiterung = AnwendungsspezifischeErweiterungTypeTestFactory.create(); private final DokumentType dokument = DokumentTypeTestFactory.create(); + private final KontaktType kontakt = KontaktTypeTestFactory.create(); @BeforeEach void init() { @@ -47,50 +53,77 @@ class VorgangTypeCreatorTest { doReturn(allgemeineMetadaten).when(creator).createAllgemeineMetadaten(vorgang); when(dokumentTypeCreator.create(vorgang)).thenReturn(Stream.of(dokument)); + when(kontaktTypeCreator.create(vorgang)).thenReturn(Optional.of(kontakt)); } @Test void shouldHaveAnwendungsspezifischeErweiterungType() { - var vorgangType = creator.create(vorgang, anwendungsspezifischeErweiterung); + var vorgangType = callCreator(); assertThat(vorgangType.getAnwendungsspezifischeErweiterung()).isEqualTo(anwendungsspezifischeErweiterung); } @Test void shouldCallCreateIdentifikation() { - creator.create(vorgang, anwendungsspezifischeErweiterung); + callCreator(); verify(creator).createIdentifikation(); } @Test void shouldHaveIdentifikation() { - var vorgangType = creator.create(vorgang, anwendungsspezifischeErweiterung); + var vorgangType = callCreator(); assertThat(vorgangType.getIdentifikation()).isEqualTo(identifikationObjekt); } @Test void shouldCallCreateAllgemeineMetadaten() { - creator.create(vorgang, anwendungsspezifischeErweiterung); + callCreator(); verify(creator).createAllgemeineMetadaten(vorgang); } @Test void shouldHaveAllgemeineMetadaten() { - var vorgangType = creator.create(vorgang, anwendungsspezifischeErweiterung); + var vorgangType = callCreator(); assertThat(vorgangType.getAllgemeineMetadaten()).isEqualTo(allgemeineMetadaten); } @Test void shouldCreateDokumenType() { - var vorgangType = creator.create(vorgang, anwendungsspezifischeErweiterung); + var vorgangType = callCreator(); verify(dokumentTypeCreator).create(vorgang); assertThat(vorgangType.getDokument()).hasSize(1).containsExactly(dokument); } + + @Test + void shouldCallKontaktTypeCreator() { + callCreator(); + + verify(kontaktTypeCreator).create(vorgang); + } + + @Test + void shouldHaveKontakt() { + var vorgangType = callCreator(); + + assertThat(vorgangType.getKontakt()).containsExactly(kontakt); + } + + @Test + void shouldNotHaveKontakt() { + when(kontaktTypeCreator.create(vorgang)).thenReturn(Optional.empty()); + var vorgangType = callCreator(); + + assertThat(vorgangType.getKontakt()).isEmpty(); + } + + private VorgangType callCreator() { + return creator.create(vorgang, anwendungsspezifischeErweiterung); + } } @Nested @@ -116,7 +149,7 @@ class VorgangTypeCreatorTest { void shouldHaveBemerkung() { var allgemeineMetadaten = creator.createAllgemeineMetadaten(vorgang); - assertThat(allgemeineMetadaten.getBemerkung()).isEqualTo(StringUtils.EMPTY); + assertThat(allgemeineMetadaten.getBemerkung()).isEmpty(); } @Test diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XDomeaServiceTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XDomeaServiceTest.java index 1f204306638b352ecae3d1ec6f32ac1e70893a93..f2471d907ae8b11e38264726ff7fb1245c72435d 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XDomeaServiceTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XDomeaServiceTest.java @@ -6,6 +6,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.UUID; @@ -53,22 +54,68 @@ class XDomeaServiceTest { @Mock private ExportFileService exportFileService; + @Mock + private ExportFilenameGenerator exportFilenameGenerator; + @DisplayName("Write exportToXdomea") @Nested class TestWriteExport { - private static final String XML_STRING = "<xml>"; private static final String FILENAME_ID = UUID.randomUUID().toString(); + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private final ExportData exportData = ExportDataTestFactory.create(); + + @Mock + private File zipFile; + + @BeforeEach + void setUp() { + doReturn(exportData).when(service).collectExportData(VorgangHeaderTestFactory.ID, FILENAME_ID); + doReturn(zipFile).when(service).createZipFile(exportData); + doNothing().when(service).writeZipFileContent(zipFile, outputStream); + } + + @Test + void shouldCollectExportData() { + callService(); + + verify(service).collectExportData(VorgangHeaderTestFactory.ID, FILENAME_ID); + } + + @Test + void shouldCreateZipFile() { + callService(); + + verify(service).createZipFile(exportData); + } + + @Test + void shouldWriteZipFileContentToOutputStream() { + callService(); + + verify(service).writeZipFileContent(zipFile, outputStream); + } + + private void callService() { + service.writeExport(VorgangHeaderTestFactory.ID, FILENAME_ID, outputStream); + } + } + + @Nested + class TestCollectExportData { + private static final String FILENAME_ID = UUID.randomUUID().toString(); + private static final String FILE_NAME = "file.zip"; + private static final String XML_STRING = "<xml>"; private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); - private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - private final OzgFile ozgFile = OzgFileTestFactory.create(); + private final Stream<OzgFile> ozgFiles = Stream.of(OzgFileTestFactory.create()); @BeforeEach - void init() { + void setUp() { + when(exportFileService.getAllPdfs(EingangTestFactory.ID)).thenReturn(ozgFiles); when(vorgangController.getVorgang(VorgangHeaderTestFactory.ID)).thenReturn(vorgang); doReturn(XML_STRING).when(service).createXmlContent(vorgang); - when(exportFileService.getAllPdfs(EingangTestFactory.ID)).thenReturn(Stream.of(ozgFile)); + doReturn(FILE_NAME).when(service).buildXmlFilename(FILENAME_ID); } @Test @@ -86,51 +133,49 @@ class XDomeaServiceTest { } @Test - void shouldCreateZipEntry() throws IOException { + void shouldGenerateXmlFilename() { callService(); - verify(service).putZipEntry(anyString(), eq(XML_STRING), any(ZipOutputStream.class)); + verify(service).buildXmlFilename(FILENAME_ID); } @Test - void shouldGenerateXmlFilename() { + void shouldGetPdfFiles() { callService(); - verify(service).buildXmlFilename(FILENAME_ID); + verify(exportFileService).getAllPdfs(EingangTestFactory.ID); } @Test - void shouldWriteBytes() { - callService(); + void shouldContainsVorgang() { + var exportData = callService(); - assertThat(outputStream).isNotNull(); - assertThat(outputStream.toByteArray()).hasSizeGreaterThan(100); + assertThat(exportData.getVorgang()).isEqualTo(vorgang); } @Test - void shouldThrowTechnicalException() throws IOException { - doThrow(IOException.class).when(service).putZipEntry(anyString(), eq(XML_STRING), any(ZipOutputStream.class)); + void shouldContainsXmlContent() { + var exportData = callService(); - assertThatThrownBy(this::callService).isInstanceOf(TechnicalException.class); + assertThat(exportData.getXmlFileContent()).isEqualTo(XML_STRING); } @Test - void shouldGetPdfFiles() { - callService(); + void shouldContainsExportFilename() { + var exportData = callService(); - verify(exportFileService).getAllPdfs(EingangTestFactory.ID); + assertThat(exportData.getExportFilename()).isEqualTo(FILE_NAME); } - @SneakyThrows @Test - void shouldWritePdfFiles() { - callService(); + void shouldContainsExportExportFiles() { + var exportData = callService(); - verify(service).putOzgFileIntoZip(eq(ozgFile), any(ZipOutputStream.class)); + assertThat(exportData.getExportFiles()).isEqualTo(ozgFiles); } - private void callService() { - service.writeExport(VorgangHeaderTestFactory.ID, FILENAME_ID, outputStream); + private ExportData callService() { + return service.collectExportData(VorgangHeaderTestFactory.ID, FILENAME_ID); } } @@ -188,6 +233,66 @@ class XDomeaServiceTest { } } + @Nested + class TestCreateZipFile { + + private static final String XML_STRING = "<xml>"; + private final ExportData exportData = ExportDataTestFactory.create(); + + @Captor + private ArgumentCaptor<ZipOutputStream> zipOutputStreamArgumentCaptor; + + @SneakyThrows + @BeforeEach + void setUp() { + doNothing().when(service).putOzgFileIntoZip(any(OzgFile.class), any(ZipOutputStream.class)); + } + + @Test + void shouldCreateZipEntry() throws IOException { + callService(); + + verify(service).putZipEntry(eq(ExportDataTestFactory.EXPORT_FILENAME), eq(ExportDataTestFactory.XML_FILE_CONTENT), + any(ZipOutputStream.class)); + } + + @Test + void shouldCreateZipOutputStream() throws IOException { + callService(); + + verify(service).putZipEntry(eq(ExportDataTestFactory.EXPORT_FILENAME), eq(ExportDataTestFactory.XML_FILE_CONTENT), + zipOutputStreamArgumentCaptor.capture()); + assertThat(zipOutputStreamArgumentCaptor.getValue()).isInstanceOf(ZipOutputStream.class); + } + + @Test + void shouldWriteBytes() { + var file = callService(); + + assertThat(file).isNotEmpty().content().hasSizeGreaterThan(100); + } + + @SneakyThrows + @Test + void shouldWritePdfFiles() { + callService(); + + verify(service).putOzgFileIntoZip(eq(ExportDataTestFactory.OZG_FILE), any(ZipOutputStream.class)); + } + + @Test + void shouldThrowTechnicalException() throws IOException { + doThrow(IOException.class).when(service).putZipEntry(anyString(), eq(XML_STRING), any(ZipOutputStream.class)); + + assertThatThrownBy(this::callService).isInstanceOf(TechnicalException.class); + } + + private File callService() { + return service.createZipFile(exportData); + } + + } + @Nested class TestPutZipEntry { @@ -246,6 +351,18 @@ class XDomeaServiceTest { private final OzgFile ozgFile = OzgFileTestFactory.create(); + @BeforeEach + void setUp() { + when(exportFilenameGenerator.generateExportFilename(ozgFile)).thenReturn(OzgFileTestFactory.NAME); + } + + @Test + void shouldGenerateExportFilename() { + callService(); + + verify(exportFilenameGenerator).generateExportFilename(ozgFile); + } + @SneakyThrows @Test void shouldPutNextEntry() { diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XmlMarshallerConfigurationTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XmlMarshallerConfigurationTest.java index 91d53e4afc8c6f934d904c1bef9df3ea87adfb8c..46ddabac96c193f78b8ec3e8a448cbb68897c90f 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XmlMarshallerConfigurationTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XmlMarshallerConfigurationTest.java @@ -5,7 +5,7 @@ import static org.mockito.Mockito.*; import java.util.Map; -import javax.xml.bind.Marshaller; +import jakarta.xml.bind.Marshaller; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; diff --git a/goofy-client/apps/goofy-e2e/Jenkinsfile b/goofy-client/apps/goofy-e2e/Jenkinsfile index 50fd697a3fa21b9a62ef0f9198fefe61ebb08277..c25dede479bf24c9037d9c13d44f45abb864d9c3 100644 --- a/goofy-client/apps/goofy-e2e/Jenkinsfile +++ b/goofy-client/apps/goofy-e2e/Jenkinsfile @@ -190,15 +190,6 @@ pipeline { } } -// stage('Pausing to wait for ozg-operator') { -// when { -// expression { !SKIP_RUN } -// } -// steps { -// sleep(time: 3, unit: 'MINUTES') -// } -// } - stage('Run E2E-Tests') { when { expression { !SKIP_RUN } @@ -615,11 +606,12 @@ Void publishE2ETestResult(String reportFolder, String reportName) { String runTests(String bezeichner, String reportFolder, Integer dbPort, String stageName) { def config = generateCypressConfig(bezeichner, reportFolder, dbPort) + def spec = "apps/goofy-e2e/src/e2e/${reportFolder}" try { - dir("goofy-client") { - sh "npm run cypress:version" - sh "npm run cypress:ci-run --CONFIG='${config}' --REPORT_FOLDER=${reportFolder}" + dir('goofy-client'){ + sh "npm run cypress:version" + sh "npm run cypress:ci-run --CONFIG=./${config} --REPORT_FOLDER=${reportFolder}" } } catch (Exception e) { printNpmDebugLog() @@ -658,26 +650,35 @@ String cutBranchNameForKeycloakRealm(String branchName, String stageName) { String generateCypressConfig(String bezeichner, String testFolder, Integer dbPort) { def namespace = generateNamespace(bezeichner) + def configName = "cypress-ci-"+testFolder+".json" - def plutoDatabaseSecret = getPlutoDatabaseSecret(namespace); - def decodedPassword = decodeString(plutoDatabaseSecret.password); - def parsablePassword = makePasswordUrlConform(decodedPassword); + dir('goofy-client/apps/goofy-e2e/'){ + def config = readJSON file: 'cypress-ci.json' + + def plutoDatabaseSecret = getPlutoDatabaseSecret(namespace); + def decodedPassword = decodeString(plutoDatabaseSecret.password); + def parsablePassword = makePasswordUrlConform(decodedPassword); + + config.baseUrl = "https://${bezeichner}.${env.CLUSTER_BASE_URL}" as String + config.env.dbUrl = "mongodb://${decodeString(plutoDatabaseSecret.username)}:${parsablePassword}@localhost:${dbPort}/admin?ssl=false&directConnection=true" as String + config.env.keycloakUrl = "https://${env.SSO_URL}/" as String + config.env.keycloakRealm = namespace as String + config.env.sabineUuid = getKeycloakUuid(namespace, "sabine") as String + config.videosFolder = "./reports/${testFolder}/videos" as String + config.screenshotsFolder = "./reports/${testFolder}/screenshots" as String + config.reporterOptions.reportDir = "./reports/${testFolder}/mochawesome-report" as String + + config.specPattern = "src/e2e/${testFolder}/**/*.cy.{js,jsx,ts,tsx}" as String + + config.env.put("search", getElasticsearchEnv(namespace)) + config.env.put("userManager", getUserManagerEnv(namespace, dbPort)) + + writeJSON file: configName, json: config + + sh "cat ${configName}" + } - config.baseUrl = "https://${bezeichner}.${env.CLUSTER_BASE_URL}" as String - config.env.dbUrl = "mongodb://${decodeString(plutoDatabaseSecret.username)}:${parsablePassword}@localhost:${dbPort}/admin?ssl=false&directConnection=true" as String - config.env.keycloakUrl = "https://${env.SSO_URL}/" as String - config.env.keycloakRealm = namespace as String - config.env.keycloakClient = "alfa" as String - config.env.sabineUuid = getKeycloakUuid(namespace, "sabine") as String - config.integrationFolder = "./src/integration/${testFolder}" as String - config.videosFolder = "./reports/${testFolder}/videos" as String - config.screenshotsFolder = "./reports/${testFolder}/screenshots" as String - config.reporterOptions.reportDir = "./reports/${testFolder}/mochawesome-report" as String - - config.env.put("search", getElasticsearchEnv(namespace)) - config.env.put("userManager", getUserManagerEnv(namespace, dbPort)) - - return JSONObject.fromObject(config) + return "cypress-ci-"+testFolder+".config.ts" } String makePasswordUrlConform(String password) { diff --git a/goofy-client/apps/goofy-e2e/cypress-ci-einheitlicher-ansprechpartner.config.ts b/goofy-client/apps/goofy-e2e/cypress-ci-einheitlicher-ansprechpartner.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..5ef91724365a1ed66f94db34d0c762d8b1b3b2b7 --- /dev/null +++ b/goofy-client/apps/goofy-e2e/cypress-ci-einheitlicher-ansprechpartner.config.ts @@ -0,0 +1,16 @@ +import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; +import { defineConfig } from 'cypress'; + +//Cypress config is generated by JenkinsFile +const cypressConfig = require('./cypress-ci-einheitlicher-ansprechpartner.json'); +const cypressEvents = require('./src/support/cypress-tasks.ts'); + +export default defineConfig({ + e2e: { + ...nxE2EPreset(__dirname), + ...cypressConfig, + setupNodeEvents(on, config) { + return cypressEvents(on, config); + } + }, +}); \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/cypress-ci-main-tests.config.ts b/goofy-client/apps/goofy-e2e/cypress-ci-main-tests.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3b17666b23e7ef150bfc88da510380739f7f49a --- /dev/null +++ b/goofy-client/apps/goofy-e2e/cypress-ci-main-tests.config.ts @@ -0,0 +1,16 @@ +import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; +import { defineConfig } from 'cypress'; + +//Cypress config is generated by JenkinsFile +const cypressConfig = require('./cypress-ci-main-tests.json'); +const cypressEvents = require('./src/support/cypress-tasks.ts'); + +export default defineConfig({ + e2e: { + ...nxE2EPreset(__dirname), + ...cypressConfig, + setupNodeEvents(on, config) { + return cypressEvents(on, config); + } + }, +}); \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/cypress-ci.json b/goofy-client/apps/goofy-e2e/cypress-ci.json index cbcd4bfb981c2d6443c05fe4925c05646c1c9325..084a420ec79ba1ddf02340bfe17f62db94a8b985 100644 --- a/goofy-client/apps/goofy-e2e/cypress-ci.json +++ b/goofy-client/apps/goofy-e2e/cypress-ci.json @@ -1,30 +1,22 @@ { - "baseUrl": "https://e2e.dev.by.ozg-cloud.de", "env": { - "dbUrl": "mongodb+srv://pluto-database-user:XnHhfznNWg65NNd@pluto-database-svc.sh-e2e-dev.svc.cluster.local/admin?ssl=false", "database": "pluto-database", - "keycloakRealm": "by-e2e-local-dev", - "keycloakUrl": "https://sso.dev.by.ozg-cloud.de/", "keycloakClient": "alfa" }, "fileServerFolder": ".", "fixturesFolder": "./src/fixtures", - "integrationFolder": "./src/integration", "modifyObstructiveCode": false, - "pluginsFile": "./src/plugins/index", - "supportFile": "./src/support/index.ts", "video": true, - "videosFolder": "./reports/videos", - "screenshotsFolder": "./reports/screenshots", "chromeWebSecurity": false, "reporter": "../../node_modules/cypress-mochawesome-reporter", "defaultCommandTimeout": 10000, + "supportFile": "./src/support/e2e.ts", + "testIsolation": false, "reporterOptions": { "html": false, "json": true, "quite": true, - "reportDir": "./reports/mochawesome-report", "reportFilename": "report", "overwrite": false } -} \ No newline at end of file +} diff --git a/goofy-client/apps/goofy-e2e/cypress.config.json b/goofy-client/apps/goofy-e2e/cypress.config.json new file mode 100644 index 0000000000000000000000000000000000000000..2eaa211f04655dea29923a5a9228dc5a98dcc19b --- /dev/null +++ b/goofy-client/apps/goofy-e2e/cypress.config.json @@ -0,0 +1,39 @@ +{ + "baseUrl": "http://localhost:4300", + "env": { + "dbUrl": "mongodb://localhost:27018", + "database": "test", + "keycloakRealm": "by-e2e-local-dev", + "keycloakUrl": "https://sso.dev.by.ozg-cloud.de/", + "keycloakClient": "alfa", + "sabineUuid": "2ccf0c13-da74-4516-ae3d-f46d30e8ec0c", + "search": { + "url": "http://localhost:9200", + "index": "e2e-test-index", + "user": "elastic", + "password": "password" + }, + "userManager": { + "dbUrl": "mongodb://localhost:27018", + "database": "usermanager" + } + }, + "fileServerFolder": ".", + "fixturesFolder": "./src/fixtures", + "video": false, + "videosFolder": "./reports/videos", + "screenshotsFolder": "./reports/screenshots", + "chromeWebSecurity": false, + "reporter": "../../node_modules/cypress-mochawesome-reporter", + "defaultCommandTimeout": 10000, + "specPattern": "src/e2e/**/*.cy.{js,jsx,ts,tsx}", + "supportFile": "src/support/e2e.ts", + "testIsolation": false, + "reporterOptions": { + "html": false, + "json": true, + "reportDir": "./reports/mochawesome-report", + "reportFilename": "report", + "overwrite": true + } +} diff --git a/goofy-client/apps/goofy-e2e/cypress.config.ts b/goofy-client/apps/goofy-e2e/cypress.config.ts index f202506b3f402b3aa90fd1959a868b249fda09a8..21b3aad9fb32db1ae98081e63a3facb256b3593a 100644 --- a/goofy-client/apps/goofy-e2e/cypress.config.ts +++ b/goofy-client/apps/goofy-e2e/cypress.config.ts @@ -1,383 +1,15 @@ import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset'; import { defineConfig } from 'cypress'; -const MongoClient = require('mongodb').MongoClient; -const Binary = require('mongodb').Binary; -const ObjectId = require('mongodb').ObjectId; -const Long = require('mongodb').Long; -const fs = require('fs'); -const {rmdir} = require('fs'); - -const cypressJsonConfig = { - baseUrl: 'http://localhost:4300', - env: { - dbUrl: 'mongodb://localhost:27018', - database: 'test', - keycloakRealm: 'by-e2e-local-dev', - keycloakUrl: 'https://sso.dev.by.ozg-cloud.de/', - keycloakClient: 'alfa', - sabineUuid: '2ccf0c13-da74-4516-ae3d-f46d30e8ec0c', - search: { - url: 'http://localhost:9200', - index: 'e2e-test-index', - user: 'elastic', - password: 'password', - }, - userManager: { - dbUrl: 'mongodb://localhost:27018', - database: 'usermanager', - }, - }, - fileServerFolder: '.', - fixturesFolder: './src/fixtures', - video: false, - videosFolder: './reports/videos', - screenshotsFolder: './reports/screenshots', - chromeWebSecurity: false, - reporter: '../../node_modules/cypress-mochawesome-reporter', - defaultCommandTimeout: 10000, - reporterOptions: { - html: false, - json: true, - reportDir: './reports/mochawesome-report', - reportFilename: 'report', - overwrite: true, - }, - specPattern: 'src/e2e/**/*.cy.{js,jsx,ts,tsx}', - supportFile: 'src/support/e2e.ts', - testIsolation: false -}; +const cypressConfig = require('./cypress.config.json'); +const cypressEvents = require('./src/support/cypress-tasks.ts'); export default defineConfig({ e2e: { ...nxE2EPreset(__dirname), - ...cypressJsonConfig, + ...cypressConfig, setupNodeEvents(on, config) { - on('task', { - initCommandData({collection, data}) { - console.log('initCommandData'); - insertIntoDatabase(config, collection, parseCommandData(data)); - return 0; - }, - initGridFsFileData({collection, data}) { - console.log('initGridFsFileData'); - insertIntoDatabase(config, collection, parseGridFsFileData(data)); - return 0; - }, - initGridFsChunkData({collection, data}) { - console.log('initGridFsChunkData'); - insertIntoDatabase(config, collection, parseGridFsChunkData(data)); - return 0; - }, - initVorgangData({collection, data}) { - console.log('initVorgangData'); - insertIntoDatabase(config, collection, parseVorgangData(data)); - return 0; - }, - initVorgangAttachedItemData({collection, data}) { - console.log('initVorgangAttachedItemData'); - insertIntoDatabase(config, collection, parseVorgangAttachedItemData(data)); - return 0; - }, - initUsermanagerData({collection, data}) { - console.log('initUsermanagerData'); - insertIntoUserManagerDatabase(config, collection, parseUserData(data)); - return 0; - }, - dropCollections(collections) { - console.log('dropCollections: ', collections); - dropCollectionsFromDatabase(config, collections); - return 0; - }, - dropUserManagerCollections(collections) { - console.log('dropUserManagerCollections: ', collections); - dropUserManagerCollectionsFromDatabase(config, collections); - return 0; - }, - countFiles(folderName) { - console.log('counting files in folder %s', folderName); - return countFiles(folderName); - }, - deleteFolder(folderName) { - console.log('deleting folder %s', folderName); - deleteFolder(folderName); - return 0; - }, - }); - - // Workaround für Angular 13 und Cypress mit Webpack 4, - // Siehe https://github.com/cypress-io/cypress/issues/19066#issuecomment-1012055705 - // Entfernen, sobald Cypress Webpack 5 nutzt - https://github.com/cypress-io/cypress/issues/19555 - // Ursache: Angular linker needed to link partial-ivy code, - // see https://angular.io/guide/creating-libraries#consuming-partial-ivy-code-outside-the-angular-cli - // Fehlerbild: - // - Anwendung läuft im Browser, aber nicht in Cypress. - // - Fehlermeldung in Cypress: The injectable 'SystemDateTimeProvider' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available. - // Lösung: - // - NPM-Paket identifizieren, dass "SystemDateTimeProvider" enthält. - // - NPM-Paket im "test" Attribut unten hinzufügen. - const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor'); - const webpackOptions = webpackPreprocessor.defaultOptions.webpackOptions; - - webpackOptions.module.rules.unshift({ - test: /[/\\](@angular|@ngxp|angular-oauth2-oidc)[/\\].+\.m?js$/, - resolve: { - fullySpecified: false, - }, - use: { - loader: 'babel-loader', - options: { - plugins: ['@angular/compiler-cli/linker/babel'], - compact: false, - cacheDirectory: true - } - } - }); - - on('file:preprocessor', webpackPreprocessor({ - webpackOptions: webpackOptions, - typescript: require.resolve('typescript') - })); - - return config; - // Ende - Workaround für Angular 13 und Cypress mit Webpack 4 + return cypressEvents(on, config); } }, }); - -function parseCommandData(commands) { - commands.forEach(command => parseCommand(command)) - return commands; -} - -function parseCommand(command) { - command.createdAt = createDate(command.createdAt); - if(command.finishedAt){ - command.finishedAt = createDate(command.finishedAt); - } - command.relationVersion = createNumberLong(command.relationVersion); -} - -function parseGridFsFileData(gridFsFiles) { - gridFsFiles.forEach(gridFsFile => parseGridFsFile(gridFsFile)) - return gridFsFiles; -} - -function parseGridFsFile(gridFsFile) { - gridFsFile._id = createObjectId(gridFsFile); - gridFsFile.length = createNumberLong(gridFsFile.length); - gridFsFile.uploadDate = createDate(gridFsFile.uploadDate); -} - -function parseGridFsChunkData(gridFsChunks) { - gridFsChunks.forEach(gridFsChunk => parseGridFsChunk(gridFsChunk)); - return gridFsChunks; -} - -function parseGridFsChunk(gridFsChunk) { - gridFsChunk._id = createObjectId(gridFsChunk); - //TODO createObjectId nutzen, sobald diese umgestellt ist - gridFsChunk.files_id = new ObjectId(gridFsChunk.files_id.$oid); - gridFsChunk.data = createBinData(gridFsChunk.data); -} - -function parseVorgangData(data) { - data.forEach(vorgang => parseVorgang(vorgang)); - return data; -} - -function parseVorgang(vorgang) { - vorgang._id = createObjectId(vorgang); - vorgang.createdAt = createDate(vorgang.createdAt); - - vorgang.eingangs.forEach(eingang => parseEingang(eingang)); - - if (vorgang.wiedervorlages) { - vorgang.wiedervorlages.forEach(wiedervorlage => parseWiedervorlage(wiedervorlage)); - } - if(vorgang.kommentars){ - vorgang.kommentars.forEach(kommentar => parseKommentar(kommentar)); - } -} - -function parseEingang(eingang) { - eingang.header.createdAt = createDate(eingang.header.createdAt); - - if (eingang.attachments) { - eingang.attachments.forEach(attachment => parseAttachment(attachment)); - } - if (eingang.representations) { - eingang.representations.forEach(representation => parseRepresentations(representation)); - } -} - -function parseAttachment(attachment) { - attachment.files[ 0 ].content = createBinData(''); -} - -function createBinData(encoded64Value){ - return Binary(Buffer.from(encoded64Value, 'base64'), 0); -} - -function parseRepresentations(representation) { - representation.content = createBinary(representation); -} - -//TODO Code entfernen und stattdessen createBinData(value) nutzen -function createBinary(field) { - return Binary(field.content.$binary.base64, 'base64'); -} - -function parseWiedervorlage(wiedervorlage) { - wiedervorlage.frist = createDate(wiedervorlage.frist); - wiedervorlage.createdAt = createDate(wiedervorlage.createdAt); -} - -function parseKommentar(kommentar) { - kommentar.createdAt = createDate(kommentar.createdAt); -} - -function createDate(field) { - return new Date(field.$date); -} - -function parseVorgangAttachedItemData(vorgangAttachedItems){ - vorgangAttachedItems.forEach(vorgangAttachedItem => parseVorgangAttachedItem(vorgangAttachedItem)); - return vorgangAttachedItems; -} - -function parseVorgangAttachedItem(parseVorgangAttachedItem) { - parseVorgangAttachedItem._id = createObjectId(parseVorgangAttachedItem); - parseVorgangAttachedItem.version = createNumberLong(parseVorgangAttachedItem.version); -} - -function parseUserData(data) { - data.forEach(user => parseUser(user)); - return data; -} - -function parseUser(user) { - user._id = createObjectId(user); - user.createdAt = createDate(user.createdAt); -} - -//TOOD Beschraenkung auf _id aufheben -function createObjectId(field) { - return new ObjectId(field._id.$oid); -} - -function createNumberLong(numberValue){ - return Long.fromNumber(numberValue); -} - -function insertIntoDatabase(config, collection, data) { - insert(getDatabaseUrl(config), getDatabase(config), collection, data); -} - -function insertIntoUserManagerDatabase(config, collection, data){ - insert(getUserManagerDatabaseUrl(config), getUserManagerDatabase(config), collection, data); -} - -function insert(databaseUrl, databaseName, collection, data){ - MongoClient.connect(databaseUrl, (error, connection) => { - console.log(`connect to ${databaseName} database with ${databaseUrl}`); - if (!error) { - console.log('success'); - var db = connection.db(databaseName); - - db.collection(collection).drop(() => { - db.createCollection(collection, (error) => handleCreateCollection(db, connection, collection, data, error)); - }); - } else { - console.log('fail', error); - } - }); -} - -function handleCreateCollection(db, connection, collection, data, error) { - if (error) { - console.log(`Fehler beim Erstellen der Collection "${collection}": `, error); - } else { - console.log(`Collection ${collection} erfolgreich erstellt`); - insertManyToDatabase(db, connection, collection, data); - } -} - -function insertManyToDatabase(db, connection, collection, data) { - db.collection(collection).insertMany(data, (error) => handleInsertMany(connection, error)); -} - -function handleInsertMany(connection, error) { - if (error) { - console.log('Fehler beim Einlesen der Daten: ', error); - } else { - console.log('Die Daten wurden erfolgreich eingelesen.'); - } - connection.close(); -} - -function dropCollectionsFromDatabase(config, collections) { - dropCollections(getDatabaseUrl(config), getDatabase(config), collections); -} - -function getDatabaseUrl(config){ - return config.env.dbUrl; -} - -function getDatabase(config){ - return config.env.database -} - -function dropUserManagerCollectionsFromDatabase(config, collections){ - dropCollections(getUserManagerDatabaseUrl(config), getUserManagerDatabase(config), collections); -} - -function getUserManagerDatabaseUrl(config){ - return config.env.userManager.dbUrl; -} - -function getUserManagerDatabase(config){ - return config.env.userManager.database; -} - -function dropCollections(databaseUrl, databaseName, collections){ - MongoClient.connect(databaseUrl, (error, connection) => { - if (!error) { - const db = connection.db(databaseName); - collections.forEach((oneCollection, index) => { - console.log('drop collection', oneCollection); - db.collection(oneCollection).drop(() => { - //CHECKME Ist die Abfrage notwendig? - if(index == collections.length){ - console.log('close connection'); - connection.close(); - } - }); - }); - } - }); -} - -function countFiles(folderName): Promise<number> { - return new Promise((resolve, reject) => { - fs.readdir(folderName, (err, files) => { - if (err) { - return reject(err) - } - - resolve(files.length) - }) - }) -} - -function deleteFolder(folderName): void { - new Promise((resolve, reject) => { - rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => { - if (err) { - console.error(err) - return reject(err) - } - resolve(null) - }) - }) -} \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/docker-compose.yml b/goofy-client/apps/goofy-e2e/docker-compose.yml index 31c7707a16771124d617049f524badcd5f0914f4..3b0a0ea591d1afed116ba0810a7b3cd6e49ecdc6 100644 --- a/goofy-client/apps/goofy-e2e/docker-compose.yml +++ b/goofy-client/apps/goofy-e2e/docker-compose.yml @@ -39,10 +39,11 @@ services: - SPRING_DATA_MONGODB_HOST=ozg-mongodb - SPRING_PROFILES_ACTIVE=${SPRING_PROFILE:-dev,e2e} - logging_level_org_springframework_security=${LOGGING_LEVEL:-WARN} - - SPRING_ELASTICSEARCH_URIS=http://ozg-elastic:9200 - - SPRING_ELASTICSEARCH_USERNAME=elastic - - SPRING_ELASTICSEARCH_PASSWORD=password - - KOP_ELASTICSEARCH_INDEX=e2e-test-index + - OZGCLOUD_ELASTICSEARCH_ADDRESS=ozg-elastic:9200 + - OZGCLOUD_ELASTICSEARCH_USERNAME=elastic + - OZGCLOUD_ELASTICSEARCH_PASSWORD=password + - OZGCLOUD_ELASTICSEARCH_INDEX=e2e-test-index + - OZGCLOUD_ELASTICSEARCH_USESSL=false - GRPC_CLIENT_USER_MANAGER_ADDRESS=static://ozg-usermanager:9000 - GRPC_CLIENT_USER_MANAGER_NEGOTIATION_TYPE=PLAINTEXT - KOP_USERMANAGER_URL=http://localhost:9092/migration/user @@ -98,12 +99,14 @@ services: interval: 10s timeout: 10s retries: 5 + ozg-usermanager: image: docker.ozg-sh.de/user-manager:${USERMANAGER_DOCKER_IMAGE:-snapshot-latest} platform: linux/amd64 environment: - QUARKUS_HTTP_CORS_ORIGINS=http://localhost:4300,http://127.0.0.1:4300,https://e2e.dev.by.ozg-cloud.de - QUARKUS_OIDC_AUTH_SERVER_URL=https://sso.dev.by.ozg-cloud.de/realms/by-e2e-local-dev + - QUARKUS_LOG_CONSOLE_JSON=false - QUARKUS_OIDC_CLIENT_ID=alfa - KOP_KEYCLOAK_API_USER=usermanagerapiuser - KOP_KEYCLOAK_API_PASSWORD= diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts index 3d241ea57ab838b9e1bd955a9130238c568a932f..8ada47b1edcf7e7d636abc0cd8361557663d35ad 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/accessibility/vorgang-list.cy.ts @@ -4,9 +4,9 @@ import { VorgangSearchE2EComponent } from 'apps/goofy-e2e/src/components/vorgang import { VorgangViewsE2EComponent } from 'apps/goofy-e2e/src/components/vorgang/vorgang-views.e2e.component'; import { HeaderE2EComponent } from 'apps/goofy-e2e/src/page-objects/header.po'; import { MainPage, waitForSpinnerToDisappear } from 'apps/goofy-e2e/src/page-objects/main.po'; -import { getFormField, isKeyboardFocused, isMatFocused } from 'apps/goofy-e2e/src/support/angular.util'; +import { isKeyboardFocused } from 'apps/goofy-e2e/src/support/angular.util'; import { dropCollections, pressTab } from 'apps/goofy-e2e/src/support/cypress-helper'; -import { exist } from 'apps/goofy-e2e/src/support/cypress.util'; +import { exist, haveFocus } from 'apps/goofy-e2e/src/support/cypress.util'; import { getUserManagerUserSabine, initUsermanagerUsers, loginAsSabine } from 'apps/goofy-e2e/src/support/user-util'; describe('VorgangList Page', () => { @@ -54,7 +54,7 @@ describe('VorgangList Page', () => { it('should focus search field', () => { pressTab(); - isMatFocused(getFormField(vorgangSearch.getForm())); + haveFocus(vorgangSearch.getInput()); }) it('should focus help menu icon', () => { diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/interceptor/interceptor-server-error.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/interceptor/interceptor-server-error.cy.ts index bbac0d37ed2ce91377bcd6da2b872ca4e0de9f6a..e57681f1ff5a596a9f12b28123f2e9ffe69f8531 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/interceptor/interceptor-server-error.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/interceptor/interceptor-server-error.cy.ts @@ -65,7 +65,6 @@ describe('Interceptor Server Error', () => { it('should show page', () => { vorgangList.getListItem(vorgang.name).getRoot().click(); - waitForSpinnerToDisappear(); waitOfInterceptor(interceptor).then(interception => { assert(interception.response.statusCode, statusCode.toString()); diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/loesch-anforderung/endgueltig-loeschen.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/loesch-anforderung/endgueltig-loeschen.cy.ts index f3588c671c47d3c7029f50e2f54434dce166ae86..18eba64a91139e1f089a1ce399f8729e5f95ba9b 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/loesch-anforderung/endgueltig-loeschen.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/loesch-anforderung/endgueltig-loeschen.cy.ts @@ -105,7 +105,8 @@ describe('Vorgang endgültig löschen', () => { }) }) - describe('Endgültig Löschen (ludwig löscht den Vorgang für den die sabine die Löschanforderung gestellt hat)', () => { + // TODO Warum exakt derselbe Test nochmal, nur mit objectIds[2] anstelle objectIds[1]? + describe.skip('Endgültig Löschen (ludwig löscht den Vorgang für den die sabine die Löschanforderung gestellt hat)', () => { before(() => { initVorgang(vorgangZuLoeschen); diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts index 1e7f6eb1e7c108eeb80bba03550c68078ed89a36..977ce30f2e623f7661b02f9ea3ad8ed672bfbfaf 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts @@ -54,15 +54,19 @@ describe('VorgangList Suche', () => { const vorgangToStay: VorgangE2E = { ...createVorgang(), name: 'VorgangToStay', eingangs: [eingang], assignedTo: getUserSabineInternalId() }; const vorgangToDisappear: VorgangE2E = { ...buildVorgang(objectIds[0], 'VorgangToDisappear'), status: VorgangStatusE2E.NEU }; + const vorgangHyphen: VorgangE2E = { ...buildVorgang(objectIds[1], 'Vorgang-mit-Bindestrich'), status: VorgangStatusE2E.NEU }; + const vorgangWOHyphen: VorgangE2E = { ...buildVorgang(objectIds[2], 'Vorgang mit Leerzeichen'), status: VorgangStatusE2E.NEU }; const vorgangStayInList: VorgangListItemE2EComponent = vorgangList.getListItem(vorgangToStay.name); const vorgangDisappearInList: VorgangListItemE2EComponent = vorgangList.getListItem(vorgangToDisappear.name); + const vorgangHyphenInList: VorgangListItemE2EComponent = vorgangList.getListItem(vorgangHyphen.name); + const vorgangWOHyphenInList: VorgangListItemE2EComponent = vorgangList.getListItem(vorgangWOHyphen.name); const userSabine: UserE2E = getUserSabine(); before(() => { - initVorgaenge([vorgangToStay, vorgangToDisappear]); - initSearchIndex([vorgangToStay, vorgangToDisappear]); + initVorgaenge([vorgangToStay, vorgangToDisappear, vorgangHyphen, vorgangWOHyphen]); + initSearchIndex([vorgangToStay, vorgangToDisappear, vorgangHyphen, vorgangWOHyphen]); initUsermanagerUsers([getUserManagerUserSabine()]); loginAsSabine(); @@ -84,6 +88,64 @@ describe('VorgangList Suche', () => { notExist(vorgangDisappearInList.getRoot()); }) + describe('Search for partial strings...', () => { + it ('should find partial string at the beginning', () => { + doSearchWith('VorgangToS'); + waitForSpinnerToDisappear(); + + exist(vorgangStayInList.getRoot()); + }) + + it ('should find partial string in the middle', () => { + doSearchWith('gangToSt'); + waitForSpinnerToDisappear(); + + exist(vorgangStayInList.getRoot()); + }) + + it ('should find partial string at the end, case insensitive', () => { + doSearchWith('toStay'); + waitForSpinnerToDisappear(); + + exist(vorgangStayInList.getRoot()); + }) + + }) + + describe ('Search with hyphen', () => { + it ('should find entry with hyphen', () => { + doSearchWith('Vorgang-mit'); + waitForSpinnerToDisappear(); + + exist(vorgangHyphenInList.getRoot()); + notExist(vorgangWOHyphenInList.getRoot()); + }) + + it ('should find entry without hyphen', () => { + doSearchWith('Vorgang mit'); + waitForSpinnerToDisappear(); + + notExist(vorgangHyphenInList.getRoot()); + exist(vorgangWOHyphenInList.getRoot()); + }) + }) + + describe ('Search using AND operator', () => { + it ('should find entry with all matches', () => { + doSearchWith('Vorgang mit Leerzeichen'); + waitForSpinnerToDisappear(); + + exist(vorgangWOHyphenInList.getRoot()); + }) + + it ('should not find entry when using additional words', () => { + doSearchWith('Vorgang mit als Leerzeichen'); + waitForSpinnerToDisappear(); + + notExist(vorgangWOHyphenInList.getRoot()); + }) + }) + describe('navigate with filtered list to vorgang detail', () => { beforeEach(() => { @@ -442,6 +504,7 @@ describe('VorgangList Suche', () => { }) + function doSearchWith(searchBy: string): void { enterWith(vorgangSearch.getInput(), searchBy); } diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage-attachment/wiedervorlage-attachment.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage-attachment/wiedervorlage-attachment.cy.ts index f3f81417223f0b351ef9711bd9644e8a644960e1..8d25b11950bd3be02aa1dd0048245a2a7b732985 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage-attachment/wiedervorlage-attachment.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage-attachment/wiedervorlage-attachment.cy.ts @@ -25,14 +25,14 @@ import { AttachmentContainerE2EComponent, AttachmentListE2EComponent } from 'app import { WiedervorlageSubnavigationE2EComponent } from 'apps/goofy-e2e/src/components/wiedervorlage/wiedervorlage-subnavigation'; import { BinaryFileSnackbarMessageE2E } from 'apps/goofy-e2e/src/model/binary-file'; import { WiedervorlageE2E } from 'apps/goofy-e2e/src/model/wiedervorlage'; -import { dropCollections, readFileFromDownloads } from 'apps/goofy-e2e/src/support/cypress-helper'; +import { dropCollections, readFileFromDownloads, wait } from 'apps/goofy-e2e/src/support/cypress-helper'; import { initVorgangAttachedItem } from 'apps/goofy-e2e/src/support/vorgang-attached-item-util'; import { SnackBarE2EComponent } from '../../../components/ui/snackbar.e2e.component'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { WiedervorlageInVorgangE2EComponent } from '../../../components/wiedervorlage/wiedervorlage-in-vorgang.e2e.component'; import { WiedervorlageE2EComponent } from '../../../components/wiedervorlage/wiedervorlage-page.e2e.component'; import { VorgangE2E } from '../../../model/vorgang'; -import { MainPage, waitForSpinnerToDisappear, waitforSpinnerToAppear } from '../../../page-objects/main.po'; +import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { WiedervorlagePage } from '../../../page-objects/wiedervorlage.po'; import { containClass, contains, exist, haveLength, notContainClass, notExist } from '../../../support/cypress.util'; @@ -233,9 +233,9 @@ describe('Wiedervorlage attachments', () => { const wiedervorlageComp: WiedervorlageInVorgangE2EComponent = wiedervorlageContainerInVorgang.getWiedervorlage(WIEDERVORLAGE_WITH_ATTACHMENTS_BETREFF); it('should open wiedervorlage page', () => { + wait(500); wiedervorlageComp.getLink().click(); - waitforSpinnerToAppear(); waitForSpinnerToDisappear(); exist(attachmentList.getItem(TEST_FILE_WITH_CONTENT).getRoot()); @@ -244,7 +244,6 @@ describe('Wiedervorlage attachments', () => { it('should mark as erledigt', () => { subnavigation.erledigen(); - waitforSpinnerToAppear(); waitForSpinnerToDisappear(); containClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); @@ -260,7 +259,6 @@ describe('Wiedervorlage attachments', () => { it('should mark as open', () => { subnavigation.wiedereroeffnen(); - waitforSpinnerToAppear(); waitForSpinnerToDisappear(); notContainClass(wiedervorlageContainer.getStatusDot(), 'erledigt'); diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.cy.ts index bf77d79dacff2e909c12074011363e1d445e68b4..45c8b354131803e75927e39787f1909a53eb8eb0 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.cy.ts @@ -33,7 +33,7 @@ import { VorgangE2E } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { WiedervorlagePage } from '../../../page-objects/wiedervorlage.po'; -import { dropCollections } from '../../../support/cypress-helper'; +import { dropCollections, wait } from '../../../support/cypress-helper'; import { exist, haveText, haveValue, notExist } from '../../../support/cypress.util'; import { MessagesE2E } from '../../../support/messages'; import { formatDateLocal } from '../../../support/tech.util'; @@ -165,6 +165,7 @@ describe('Wiedervorlage', () => { it('should open Wiedervorlage-Page by click on wiedervorlage ', () => { const wiedervorlageInVorgang: WiedervorlageInVorgangE2EComponent = vorgangPage.getWiedervorlagenContainer().getWiedervorlage(wiedervorlageBetreff); + wait(500); wiedervorlageInVorgang.getLink().click(); waitForSpinnerToDisappear(); diff --git a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts index 000e8fb12d3357f5360bc3cc3f00678a289a0c65..f9a7d8593f4e6a546c4c9e12a312b9290be4df5b 100644 --- a/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts +++ b/goofy-client/apps/goofy-e2e/src/e2e/main-tests/wiedervorlage/wiedervorlage.erledigen.cy.ts @@ -98,6 +98,7 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { it('should open wiedervorlage on click', () => { exist(wiedervorlage.getRoot()); + wait(500); wiedervorlage.getLink().click(); waitForSpinnerToDisappear(); @@ -135,7 +136,7 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { const wiedervorlage: WiedervorlageInVorgangE2EComponent = wiedervorlageContainerInVorgang.getWiedervorlage(wiedervorlageZumWiedereroeffnen.betreff); it('should open wiedervorlage on click', () => { - wait(1000); + wait(500); wiedervorlage.getLink().click(); waitForSpinnerToDisappear(); @@ -173,6 +174,7 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { const wiedervorlage: WiedervorlageInVorgangE2EComponent = wiedervorlageContainerInVorgang.getWiedervorlage(wiedervorlageZumWiedereroeffnen.betreff); it('should open wiedervorlage on click', () => { + wait(500); wiedervorlage.getLink().click(); waitForSpinnerToDisappear(); @@ -277,6 +279,7 @@ describe('Wiedervorlage erledigen/wiedereroeffnen', () => { }) it('should mark as erledigt', () => { + wait(500); wiedervorlage.getLink().click(); waitForSpinnerToDisappear(); subnavigation.erledigen(); diff --git a/goofy-client/apps/goofy-e2e/src/support/commands.ts b/goofy-client/apps/goofy-e2e/src/support/commands.ts index 69700be30367bfac9a89f1c673ea2283f4318c62..7ee49c952b9212d5f85a12852ec954b71951e011 100644 --- a/goofy-client/apps/goofy-e2e/src/support/commands.ts +++ b/goofy-client/apps/goofy-e2e/src/support/commands.ts @@ -1,3 +1,4 @@ + /* * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den * Ministerpräsidenten des Landes Schleswig-Holstein @@ -21,6 +22,53 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +enum HttpMethod { + POST = 'POST', + GET = 'GET' +} + +interface HttpHeader { + [headerKey: string]: string +} + +const DATA_TEST_ID: string = 'data-test-id'; +const DATA_TEST_CLASS: string = 'data-test-class'; + +const ACCES_TOKEN: string = 'access_token'; +const ID_TOKEN: string = 'id_token'; + +enum Header { + CONTENT_TYPE = 'Content-Type', + AUTHORIZATION = 'Authorization' +} + +const CYPRESS_CONFIG_BASE_URL: unknown = 'baseUrl'; + +enum CypressEnv { + KEYCLOAK_CLIENT = 'keycloakClient', + KEYCLOAK_REALM = 'keycloakRealm', + KEYCLOAK_URL = 'keycloakUrl', + SEARCH = 'search' +} + +const CONTENT_TYPE_HEADER_VALUE: string = 'application/x-www-form-urlencoded'; + +interface SearchIndex { + vorgangId: string, + vorgangName: string, + vorgangNummer: string, + createdAt: string, + antragstellerName: string, + antragstellerVorname: string, + status: string, + organisationseinheitenId: string, + aktenzeichen: string, + assignedTo: string, + _class: string +} + +const SEARCH_INDEX_CLASS: string = 'de.itvsh.ozg.pluto.common.search.IndexedVorgang'; + declare namespace Cypress { interface Chainable<Subject> { getTestElementWithOid(oid: string, ...args); @@ -36,12 +84,13 @@ declare namespace Cypress { } } + Cypress.Commands.add('getTestElement', (selector, ...args) => { - return cy.get(`[data-test-id~="${selector}"]`, ...args) + return cy.get(`[${DATA_TEST_ID}~="${selector}"]`, ...args) }) Cypress.Commands.add('getTestElementWithClass', (selector, ...args) => { - return cy.get(`[data-test-class="${selector}"]`, ...args) + return cy.get(`[${DATA_TEST_CLASS}="${selector}"]`, ...args) }) Cypress.Commands.add('getTestElementWithOid', (oid, ...args) => { @@ -49,63 +98,73 @@ Cypress.Commands.add('getTestElementWithOid', (oid, ...args) => { }) Cypress.Commands.add('findTestElementWithClass', { prevSubject: true }, (subject: any, selector) => { - return subject.find(`[data-test-class="${selector}"]`) + return subject.find(`[${DATA_TEST_CLASS}="${selector}"]`) }) Cypress.Commands.add('findElement', { prevSubject: true }, (subject: any, selector) => { return subject.find(selector); }) -Cypress.Commands.add('login', (user, password) => { +Cypress.Commands.add('login', (user: string, password: string) => { cy.session(user, () => { - cy.request({ - method: 'POST', - followRedirect: false, - url: `${getKeycloakBaseRealmUrl()}/token`, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: { - client_id: Cypress.env('keycloakClient'), - username: user, - password: password, - grant_type: 'password', - redirect_uri: Cypress.config('baseUrl'), - response_mode: 'fragment', - response_type: 'code', - scope: 'openid' - } - }).then(response => { - const authorization: any = `bearer ${response.body.access_token}`; - cy.visit('', authorization); - - window.sessionStorage.setItem('access_token', response.body.access_token); - window.sessionStorage.setItem('id_token', response.body.id_token); - - cy.setCookie('XSRF-TOKEN', response.body.session_state); - }); + cy.request(buildLoginRequest(user, password)).then(response => handleLoginResponse(response)); }); }) +function buildLoginRequest(user: string, password: string): any { + return { + method: HttpMethod.POST, + followRedirect: false, + url: `${getKeycloakBaseRealmUrl()}/token`, + headers: { + [Header.CONTENT_TYPE]: CONTENT_TYPE_HEADER_VALUE + }, + body: buildLoginRequestBody(user, password) + } +} + +function buildLoginRequestBody(user: string, password: string): any { + return { + client_id: Cypress.env(CypressEnv.KEYCLOAK_CLIENT), + username: user, + password: password, + grant_type: 'password', + redirect_uri: Cypress.config(CYPRESS_CONFIG_BASE_URL), + response_mode: 'fragment', + response_type: 'code', + scope: 'openid' + } +} + +function handleLoginResponse(response): void { + const authorization: any = `bearer ${response.body.access_token}`; + cy.visit('', authorization); + + window.sessionStorage.setItem(ACCES_TOKEN, response.body.access_token); + window.sessionStorage.setItem(ID_TOKEN, response.body.id_token); + + cy.setCookie('XSRF-TOKEN', response.body.session_state); +} + Cypress.Commands.add('getUserInfo', () => { return cy.request({ - method: 'GET', + method: HttpMethod.GET, url: `${getKeycloakBaseRealmUrl()}/userinfo`, headers: { - Authorization: `bearer ${window.sessionStorage.getItem('access_token')}` + [Header.AUTHORIZATION]: `bearer ${window.sessionStorage.getItem(ACCES_TOKEN)}` } }); }) Cypress.Commands.add('logout', () => { cy.request({ - method: 'GET', + method: HttpMethod.GET, url: `${getKeycloakBaseRealmUrl()}/logout`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded' + [Header.CONTENT_TYPE]: CONTENT_TYPE_HEADER_VALUE }, body: { - refresh_token: window.sessionStorage.getItem('id_token') + refresh_token: window.sessionStorage.getItem(ID_TOKEN) }, failOnStatusCode: false }).then(() => { @@ -116,62 +175,62 @@ Cypress.Commands.add('logout', () => { }) function getKeycloakBaseRealmUrl(): string { - return `${Cypress.env('keycloakUrl')}realms/${Cypress.env('keycloakRealm')}/protocol/openid-connect`; + return `${Cypress.env(CypressEnv.KEYCLOAK_URL)}realms/${Cypress.env(CypressEnv.KEYCLOAK_REALM)}/protocol/openid-connect`; } Cypress.Commands.add('addVorgangToSearchIndex', (vorgang) => { cy.request({ - method: 'POST', + method: HttpMethod.POST, url: `${buildSearchIndexPostUrl()}/_doc/${vorgang._id.$oid}`, headers: buildAuthorizationHeader(), body: buildSearchIndexBody(vorgang) }); }) -function buildSearchIndexBody(vorgang) { +function buildSearchIndexBody(vorgang: any): SearchIndex { return { - '_class': 'de.itvsh.ozg.pluto.common.search.IndexedVorgang', - 'vorgangId': vorgang._id.$oid, - 'vorgangName': vorgang.name, - 'vorgangNummer': vorgang.nummer, - 'createdAt': vorgang.createdAt.$date, - 'antragstellerName': vorgang.eingangs[0].antragsteller.nachname, - 'antragstellerVorname': vorgang.eingangs[0].antragsteller.vorname, - 'status': vorgang.status, - 'organisationseinheitenId': vorgang.eingangs[0].zustaendigeStelle.organisationseinheitenId, - 'aktenzeichen': vorgang.aktenzeichen, - 'assignedTo': vorgang.assignedTo + vorgangId: vorgang._id.$oid, + vorgangName: vorgang.name, + vorgangNummer: vorgang.nummer, + createdAt: vorgang.createdAt.$date, + antragstellerName: vorgang.eingangs[0].antragsteller.nachname, + antragstellerVorname: vorgang.eingangs[0].antragsteller.vorname, + status: vorgang.status, + organisationseinheitenId: vorgang.eingangs[0].zustaendigeStelle.organisationseinheitenId, + aktenzeichen: vorgang.aktenzeichen, + assignedTo: vorgang.assignedTo, + _class: SEARCH_INDEX_CLASS, }; } Cypress.Commands.add('removeAllDocumentsFromSearchIndex', () => { cy.request({ - method: 'POST', + method: HttpMethod.POST, url: `${buildSearchIndexPostUrl()}/_delete_by_query`, headers: buildAuthorizationHeader(), body: buildSearchIndexRemoveAllBody() }); }) -function buildSearchIndexPostUrl() { - const searchEnv = getSearchEnv(); +function buildSearchIndexPostUrl(): string { + const searchEnv: string = getSearchEnv(); return `${searchEnv['url']}/${searchEnv['index']}`; } -function buildAuthorizationHeader() { - return { 'Authorization': `Basic ${buildToken()}` }; +function buildAuthorizationHeader(): HttpHeader { + return { [Header.AUTHORIZATION]: `Basic ${buildToken()}` }; } -function buildToken() { - const searchEnv = getSearchEnv(); +function buildToken(): string { + const searchEnv: string = getSearchEnv(); return btoa(`${searchEnv['user']}:${searchEnv['password']}`); } -function getSearchEnv() { - return Cypress.env('search'); +function getSearchEnv(): string { + return Cypress.env(CypressEnv.SEARCH); } -function buildSearchIndexRemoveAllBody() { +function buildSearchIndexRemoveAllBody(): any { return { 'query': { 'match_all': {} diff --git a/goofy-client/apps/goofy-e2e/src/support/cypress-tasks.ts b/goofy-client/apps/goofy-e2e/src/support/cypress-tasks.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c6806b83b7e4cd4e2d8dbfcd48976a506dc6774 --- /dev/null +++ b/goofy-client/apps/goofy-e2e/src/support/cypress-tasks.ts @@ -0,0 +1,298 @@ +import { Long, MongoClient, ObjectId } from 'mongodb'; + +const Binary = require('mongodb').Binary; + +module.exports = (on: any, config: any) => { + on('task', { + initCommandData({collection, data}) { + console.log('initCommandData'); + insertIntoDatabase(config, collection, parseCommandData(data)); + return 0; + }, + initGridFsFileData({collection, data}) { + console.log('initGridFsFileData'); + insertIntoDatabase(config, collection, parseGridFsFileData(data)); + return 0; + }, + initGridFsChunkData({collection, data}) { + console.log('initGridFsChunkData'); + insertIntoDatabase(config, collection, parseGridFsChunkData(data)); + return 0; + }, + initVorgangData({collection, data}) { + console.log('initVorgangData'); + insertIntoDatabase(config, collection, parseVorgangData(data)); + return 0; + }, + initVorgangAttachedItemData({collection, data}) { + console.log('initVorgangAttachedItemData'); + insertIntoDatabase(config, collection, parseVorgangAttachedItemData(data)); + return 0; + }, + initUsermanagerData({collection, data}) { + console.log('initUsermanagerData'); + insertIntoUserManagerDatabase(config, collection, parseUserData(data)); + return 0; + }, + dropCollections(collections) { + console.log('dropCollections: ', collections); + dropCollectionsFromDatabase(config, collections); + return 0; + }, + dropUserManagerCollections(collections) { + console.log('dropUserManagerCollections: ', collections); + dropUserManagerCollectionsFromDatabase(config, collections); + return 0; + } + }); + + // Workaround für Angular 13 und Cypress mit Webpack 4, + // Siehe https://github.com/cypress-io/cypress/issues/19066#issuecomment-1012055705 + // Entfernen, sobald Cypress Webpack 5 nutzt - https://github.com/cypress-io/cypress/issues/19555 + // Ursache: Angular linker needed to link partial-ivy code, + // see https://angular.io/guide/creating-libraries#consuming-partial-ivy-code-outside-the-angular-cli + // Fehlerbild: + // - Anwendung läuft im Browser, aber nicht in Cypress. + // - Fehlermeldung in Cypress: The injectable 'SystemDateTimeProvider' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available. + // Lösung: + // - NPM-Paket identifizieren, dass "SystemDateTimeProvider" enthält. + // - NPM-Paket im "test" Attribut unten hinzufügen. + const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor'); + const webpackOptions = webpackPreprocessor.defaultOptions.webpackOptions; + + webpackOptions.module.rules.unshift({ + test: /[/\\](@angular|@ngxp|angular-oauth2-oidc)[/\\].+\.m?js$/, + resolve: { + fullySpecified: false, + }, + use: { + loader: 'babel-loader', + options: { + plugins: ['@angular/compiler-cli/linker/babel'], + compact: false, + cacheDirectory: true + } + } + }); + + on('file:preprocessor', webpackPreprocessor({ + webpackOptions: webpackOptions, + typescript: require.resolve('typescript') + })); + + return config; + // Ende - Workaround für Angular 13 und Cypress mit Webpack 4 +} + +function parseCommandData(commands) { + commands.forEach(command => parseCommand(command)) + return commands; +} + +function parseCommand(command) { + command.createdAt = createDate(command.createdAt); + if(command.finishedAt){ + command.finishedAt = createDate(command.finishedAt); + } + command.relationVersion = createNumberLong(command.relationVersion); +} + +function parseGridFsFileData(gridFsFiles) { + gridFsFiles.forEach(gridFsFile => parseGridFsFile(gridFsFile)) + return gridFsFiles; +} + +function parseGridFsFile(gridFsFile) { + gridFsFile._id = createObjectId(gridFsFile); + gridFsFile.length = createNumberLong(gridFsFile.length); + gridFsFile.uploadDate = createDate(gridFsFile.uploadDate); +} + +function parseGridFsChunkData(gridFsChunks) { + gridFsChunks.forEach(gridFsChunk => parseGridFsChunk(gridFsChunk)); + return gridFsChunks; +} + +function parseGridFsChunk(gridFsChunk) { + gridFsChunk._id = createObjectId(gridFsChunk); + //TODO createObjectId nutzen, sobald diese umgestellt ist + gridFsChunk.files_id = new ObjectId(gridFsChunk.files_id.$oid); + gridFsChunk.data = createBinData(gridFsChunk.data); +} + +function parseVorgangData(data) { + data.forEach(vorgang => parseVorgang(vorgang)); + return data; +} + +function parseVorgang(vorgang) { + vorgang._id = createObjectId(vorgang); + vorgang.createdAt = createDate(vorgang.createdAt); + + vorgang.eingangs.forEach(eingang => parseEingang(eingang)); + + if (vorgang.wiedervorlages) { + vorgang.wiedervorlages.forEach(wiedervorlage => parseWiedervorlage(wiedervorlage)); + } + if(vorgang.kommentars){ + vorgang.kommentars.forEach(kommentar => parseKommentar(kommentar)); + } +} + +function parseEingang(eingang) { + eingang.header.createdAt = createDate(eingang.header.createdAt); + + if (eingang.attachments) { + eingang.attachments.forEach(attachment => parseAttachment(attachment)); + } + if (eingang.representations) { + eingang.representations.forEach(representation => parseRepresentations(representation)); + } +} + +function parseAttachment(attachment) { + attachment.files[ 0 ].content = createBinData(''); +} + +function createBinData(encoded64Value){ + return Binary(Buffer.from(encoded64Value, 'base64'), 0); +} + +function parseRepresentations(representation) { + representation.content = createBinary(representation); +} + +//TODO Code entfernen und stattdessen createBinData(value) nutzen +function createBinary(field) { + return Binary(field.content.$binary.base64, 'base64'); +} + +function parseWiedervorlage(wiedervorlage) { + wiedervorlage.frist = createDate(wiedervorlage.frist); + wiedervorlage.createdAt = createDate(wiedervorlage.createdAt); +} + +function parseKommentar(kommentar) { + kommentar.createdAt = createDate(kommentar.createdAt); +} + +function createDate(field) { + return new Date(field.$date); +} + +function parseVorgangAttachedItemData(vorgangAttachedItems){ + vorgangAttachedItems.forEach(vorgangAttachedItem => parseVorgangAttachedItem(vorgangAttachedItem)); + return vorgangAttachedItems; +} + +function parseVorgangAttachedItem(parseVorgangAttachedItem) { + parseVorgangAttachedItem._id = createObjectId(parseVorgangAttachedItem); + parseVorgangAttachedItem.version = createNumberLong(parseVorgangAttachedItem.version); +} + +function parseUserData(data) { + data.forEach(user => parseUser(user)); + return data; +} + +function parseUser(user) { + user._id = createObjectId(user); + user.createdAt = createDate(user.createdAt); +} + +//TOOD Beschraenkung auf _id aufheben +function createObjectId(field) { + return new ObjectId(field._id.$oid); +} + +function createNumberLong(numberValue){ + return Long.fromNumber(numberValue); +} + +function insertIntoDatabase(config, collection, data) { + insert(getDatabaseUrl(config), getDatabase(config), collection, data); +} + +function insertIntoUserManagerDatabase(config, collection, data){ + insert(getUserManagerDatabaseUrl(config), getUserManagerDatabase(config), collection, data); +} + +function insert(databaseUrl, databaseName, collection, data){ + MongoClient.connect(databaseUrl, (error, connection) => { + console.log(`connect to ${databaseName} database with ${databaseUrl}`); + if (!error) { + console.log('success'); + var db = connection.db(databaseName); + + db.collection(collection).drop(() => { + db.createCollection(collection, (error) => handleCreateCollection(db, connection, collection, data, error)); + }); + } else { + console.log('fail', error); + } + }); +} + +function handleCreateCollection(db, connection, collection, data, error) { + if (error) { + console.log(`Fehler beim Erstellen der Collection "${collection}": `, error); + } else { + console.log(`Collection ${collection} erfolgreich erstellt`); + insertManyToDatabase(db, connection, collection, data); + } +} + +function insertManyToDatabase(db, connection, collection, data) { + db.collection(collection).insertMany(data, (error) => handleInsertMany(connection, error)); +} + +function handleInsertMany(connection, error) { + if (error) { + console.log('Fehler beim Einlesen der Daten: ', error); + } else { + console.log('Die Daten wurden erfolgreich eingelesen.'); + } + connection.close(); +} + +function dropCollectionsFromDatabase(config, collections) { + dropCollections(getDatabaseUrl(config), getDatabase(config), collections); +} + +function getDatabaseUrl(config){ + return config.env.dbUrl; +} + +function getDatabase(config){ + return config.env.database +} + +function dropUserManagerCollectionsFromDatabase(config, collections){ + dropCollections(getUserManagerDatabaseUrl(config), getUserManagerDatabase(config), collections); +} + +function getUserManagerDatabaseUrl(config){ + return config.env.userManager.dbUrl; +} + +function getUserManagerDatabase(config){ + return config.env.userManager.database; +} + +function dropCollections(databaseUrl, databaseName, collections){ + MongoClient.connect(databaseUrl, (error, connection) => { + if (!error) { + const db = connection.db(databaseName); + collections.forEach((oneCollection, index) => { + console.log('drop collection', oneCollection); + db.collection(oneCollection).drop(() => { + //CHECKME Ist die Abfrage notwendig? + if(index == collections.length){ + console.log('close connection'); + connection.close(); + } + }); + }); + } + }); +} \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/src/support/cypress.util.ts b/goofy-client/apps/goofy-e2e/src/support/cypress.util.ts index c37486aab81955a516338c2fd08ce5f240decb09..ee3a1ea7f04adbec0e2fbc08c692e6b6c7fa5130 100644 --- a/goofy-client/apps/goofy-e2e/src/support/cypress.util.ts +++ b/goofy-client/apps/goofy-e2e/src/support/cypress.util.ts @@ -48,6 +48,10 @@ export function haveValue(element: any, value: string): void { element.should('have.value', value); } +export function haveFocus(element: any): void { + element.should('have.focus'); +} + export function mouseEnter(element: any): void { element.trigger('mouseenter'); } diff --git a/goofy-client/apps/goofy-e2e/src/support/delete-old-reports.ts b/goofy-client/apps/goofy-e2e/src/support/delete-old-reports.ts index 3a7e7d5170a0d442ac2272f1082c2f440ada2ca4..7e1fe4e63291541aca5faff95ceafd503f76d672 100644 --- a/goofy-client/apps/goofy-e2e/src/support/delete-old-reports.ts +++ b/goofy-client/apps/goofy-e2e/src/support/delete-old-reports.ts @@ -21,6 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -const fs = require('fs') +const fs = require('fs'); fs.rmdirSync(`apps/goofy-e2e/reports/${process.env.REPORT_FOLDER}`, { recursive: true }); \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/src/support/pre-ea-report-merge.ts b/goofy-client/apps/goofy-e2e/src/support/pre-ea-report-merge.ts index 656abb5c66310317a87aa181d77a0c6ed3838a8a..08d5f533e0192fe38759f724e2eb41984fea6fed 100644 --- a/goofy-client/apps/goofy-e2e/src/support/pre-ea-report-merge.ts +++ b/goofy-client/apps/goofy-e2e/src/support/pre-ea-report-merge.ts @@ -21,8 +21,8 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -const fs = require('fs') +const fs = require('fs'); -if (fs.existsSync('apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/.jsons')) { - fs.rename('apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/.jsons', 'apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/jsons', () => { }) +if (fs.existsSync(`apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/.jsons`)) { + fs.rename(`apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/.jsons`, `apps/goofy-e2e/reports_einheitlicher-ansprechpartner/mochawesome-report/jsons`, () => { }) } \ No newline at end of file diff --git a/goofy-client/apps/goofy-e2e/src/support/pre-merge.ts b/goofy-client/apps/goofy-e2e/src/support/pre-merge.ts index 710c4513d9884794cf3050d31326c01490336a34..6ea856303c5a3675377b4df88b5e7fbdf19f9c38 100644 --- a/goofy-client/apps/goofy-e2e/src/support/pre-merge.ts +++ b/goofy-client/apps/goofy-e2e/src/support/pre-merge.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -const fs = require('fs') +const fs = require('fs'); if (fs.existsSync(`apps/goofy-e2e/reports/${process.env.REPORT_FOLDER}/mochawesome-report/.jsons`)) { fs.rename(`apps/goofy-e2e/reports/${process.env.REPORT_FOLDER}/mochawesome-report/.jsons`, `apps/goofy-e2e/reports/${process.env.REPORT_FOLDER}/mochawesome-report/jsons`, () => { }) diff --git a/goofy-client/apps/goofy-e2e/src/support/tech.util.ts b/goofy-client/apps/goofy-e2e/src/support/tech.util.ts index fb41b54279a6218940dd985f5600cce9566dd0bb..f8fcc0bfe39b31c84546cc045d4ccde00e60de3c 100644 --- a/goofy-client/apps/goofy-e2e/src/support/tech.util.ts +++ b/goofy-client/apps/goofy-e2e/src/support/tech.util.ts @@ -77,7 +77,7 @@ export function replaceAllWhitespaces(value: string, replaceWith: string): strin } export function simpleTransliteration(value: string) { - return value.normalize('NFKD').replace(/[^-_\w]/g, ''); + return value.normalize('NFKD').replace(/[^-A-Za-z0-9_]/g, ''); } export function createDateToday(): string { diff --git a/goofy-client/apps/goofy-e2e/tsconfig.json b/goofy-client/apps/goofy-e2e/tsconfig.json index 4c5841de058b41112f526f8e9e7fbac5250450bd..2cdee4db8b30c135503582f5274a1387ec8cd70c 100644 --- a/goofy-client/apps/goofy-e2e/tsconfig.json +++ b/goofy-client/apps/goofy-e2e/tsconfig.json @@ -1,22 +1,28 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "sourceMap": false, - "outDir": "../dist/out-tsc", - "allowJs": true, - "types": ["cypress", "node"], - "forceConsistentCasingInFileNames": true, - "strict": false, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "sourceMap": false, + "outDir": "../dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"], + "forceConsistentCasingInFileNames": true, + "strict": false, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, - "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"], + "include": [ + "src/**/*.ts", + "src/**/*.js", + "cypress.config.ts", + "cypress-ci-main-tests.config.ts", + "cypress-ci-einheitlicher-ansprechpartner.config.ts" + ], "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true } - } +} diff --git a/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts b/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts index a89cd0122c03a80556be442cc4f09dbac26161e6..df3208154642a8ac4f8c351dc665a92e5015cc78 100644 --- a/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts +++ b/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-list-in-vorgang/kommentar-list-item-in-vorgang/kommentar-list-item-in-vorgang.component.ts @@ -23,10 +23,10 @@ */ import { Component, Input, OnInit } from '@angular/core'; import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; -import { KommentarLinkRel, KommentarListLinkRel, KommentarListResource, KommentarResource, KommentarService } from '@goofy-client/kommentar-shared'; -import { createEmptyStateResource, StateResource } from "@goofy-client/tech-shared"; -import { Observable, of } from 'rxjs'; +import { KommentarLinkRel, KommentarListResource, KommentarResource, KommentarService } from '@goofy-client/kommentar-shared'; +import { StateResource, createEmptyStateResource } from "@goofy-client/tech-shared"; import { hasLink } from '@ngxp/rest'; +import { Observable, of } from 'rxjs'; @Component({ selector: 'goofy-client-kommentar-list-item-in-vorgang', diff --git a/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.actions.ts b/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.actions.ts index c3a62b795e46767355136240ed7621ae7a31cda3..3cc1ebc16bab01635a685cc7199b60473a3765d9 100644 --- a/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.actions.ts +++ b/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.actions.ts @@ -12,10 +12,10 @@ export const loadLoeschAnforderung: TypedActionCreatorWithProps<VorgangWithEinga export const loadLoeschAnforderungSuccess: TypedActionCreatorWithProps<LoeschAnforderungProps> = createAction('[LoeschAnforderung] Load LoeschAnforderung Success', props<LoeschAnforderungProps>()); export const loadLoeschAnforderungFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[LoeschAnforderung] Load LoeschAnforderung Failure', props<ApiErrorAction>()); -export interface loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps { +export interface LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps { error: HttpErrorResponse, overviewUrl: string } export const loeschAnforderungZuruecknehmenFailedAndReloadVorgangSuccess: TypedActionCreator = createAction('[LoeschAnforderung] LoeschAnforderung zurücknehmen reload vorgang success'); -export const loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure: TypedActionCreatorWithProps<loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps> = createAction('[LoeschAnforderung] LoeschAnforderung zurücknehmen reload vorgang failure', props<loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps>()); \ No newline at end of file +export const loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure: TypedActionCreatorWithProps<LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps> = createAction('[LoeschAnforderung] LoeschAnforderung zurücknehmen reload vorgang failure', props<LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps>()); \ No newline at end of file diff --git a/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.ts b/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.ts index de14570bb3c540ae8ae4ba9da583725d405a2bba..8aeedfcb045ce309dad0d847d8d6dc92f1bda51f 100644 --- a/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.ts +++ b/goofy-client/libs/loesch-anforderung-shared/src/lib/+state/loesch-anforderung.effects.ts @@ -9,7 +9,7 @@ import { hasLink } from '@ngxp/rest'; import { of } from 'rxjs'; import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; import { isVorgangLoeschenCommand } from '../loesch-anforderung.util'; -import { loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps } from './loesch-anforderung.actions'; +import { LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps } from './loesch-anforderung.actions'; import { LoeschAnforderungRepository } from './loesch-anforderung.repository'; import * as CommandActions from '../../../../command-shared/src/lib/+state/command.actions'; @@ -40,8 +40,8 @@ export class LoeschAnforderungEffects { ), { dispatch: false }); loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure$ = createEffect(() => this.actions$.pipe(ofType(LoeschAnforderungActions.loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailure), - filter((props: loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps) => isNotFound(props.error.error.status)), - tap((props: loeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps) => { + filter((props: LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps) => isNotFound(props.error.error.status)), + tap((props: LoeschAnforderungZuruecknehmenFailedAndReloadVorgangFailureProps) => { this.snackBarService.showInfo(LOESCH_ANFORDERUNG_ZURUECKNEHMEN_CONCURRENT_TO_DELETE_VORGANG_MESSAGE); this.navigationService.navigate(props.overviewUrl); }) diff --git a/goofy-client/libs/navigation/src/lib/build-info/build-info.component.scss b/goofy-client/libs/navigation/src/lib/build-info/build-info.component.scss index 317e2628d4eec056d13bb3ba295c674fc2be3a9f..687bf9dc28611fe0de820edacad151dcd45114db 100644 --- a/goofy-client/libs/navigation/src/lib/build-info/build-info.component.scss +++ b/goofy-client/libs/navigation/src/lib/build-info/build-info.component.scss @@ -44,7 +44,6 @@ p { margin: 0; line-height: 1; white-space: nowrap; - font-family: 'Roboto'; font-style: normal; font-weight: 400; font-size: 11px; diff --git a/goofy-client/libs/tech-shared/src/lib/tech.util.ts b/goofy-client/libs/tech-shared/src/lib/tech.util.ts index 49578dea0312b3342430c049d875ce6f772a0a70..1e77223d9318e59bb225991cf7a5d0bcdcfb969f 100644 --- a/goofy-client/libs/tech-shared/src/lib/tech.util.ts +++ b/goofy-client/libs/tech-shared/src/lib/tech.util.ts @@ -39,14 +39,6 @@ export function isEmptyObject(obj: any): boolean { return Object.keys(obj).length === 0; } -export function replaceAllWhitespaces(value: string, replaceWith: string) { - return value.replace(/ /g, replaceWith); -} - -export function simpleTransliteration(value: string) { - return value.normalize('NFKD').replace(/[^-_\w]/g, ''); -} - export function replacePlaceholders(text: string, placeholders: { [key: string]: string }): string { let replaced = text; Object.keys(placeholders).forEach(key => replaced = replacePlaceholder(replaced, key, placeholders[key])); @@ -96,6 +88,14 @@ export function convertForDataTest(value: string): string { return simpleTransliteration(value); } +export function replaceAllWhitespaces(value: string, replaceWith: string) { + return value.replace(/ /g, replaceWith); +} + +export function simpleTransliteration(value: string) { + return value.normalize('NFKD').replace(/[^-A-Za-z0-9_]/g, ''); +} + export function getStringValue(value: null | undefined | string): string { if (isNil(value)) { return EMPTY_STRING; diff --git a/goofy-client/libs/test-utils/src/lib/mocking.ts b/goofy-client/libs/test-utils/src/lib/mocking.ts index e0f99e200bc81fb4bc7c73dc7c7d7e7ad33765f8..141db74e40b6275c057e8889b718015448889e6c 100644 --- a/goofy-client/libs/test-utils/src/lib/mocking.ts +++ b/goofy-client/libs/test-utils/src/lib/mocking.ts @@ -56,5 +56,5 @@ export function mockComponent(options: Component): Component { } export function mockClass(clazz: any): Mock<any> { - return clazz as jest.Mocked<typeof clazz>; + return clazz as jest.Mocked<typeof clazz>; //NOSONAR } \ No newline at end of file diff --git a/goofy-client/libs/ui/src/lib/ui/fixed-dialog/_fixed-dialog.theme.scss b/goofy-client/libs/ui/src/lib/ui/fixed-dialog/_fixed-dialog.theme.scss index 677a82fcb5b52c9f5a4e0f62ada552b952b702bd..0b4c18dfae3bbfe602477119799a5a2b7bee7b18 100644 --- a/goofy-client/libs/ui/src/lib/ui/fixed-dialog/_fixed-dialog.theme.scss +++ b/goofy-client/libs/ui/src/lib/ui/fixed-dialog/_fixed-dialog.theme.scss @@ -73,7 +73,7 @@ min-height: initial; overflow: hidden; margin: 0; - padding: 0; + padding: 0 !important; .button-bar-bottom { height: 0; diff --git a/goofy-client/libs/user-assistance/src/lib/help-menu/documentation/dowload-documentation-button/download-documentation-button.component.ts b/goofy-client/libs/user-assistance/src/lib/help-menu/documentation/dowload-documentation-button/download-documentation-button.component.ts index cc44375e2821e8a51fff5d37f8a3e58b4b236e61..4e26419b8f4543755cb6bb677f0ca920d2291427 100644 --- a/goofy-client/libs/user-assistance/src/lib/help-menu/documentation/dowload-documentation-button/download-documentation-button.component.ts +++ b/goofy-client/libs/user-assistance/src/lib/help-menu/documentation/dowload-documentation-button/download-documentation-button.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -const FILE_NAME_REGEX: string = '^.*\\/(.*.pdf)$'; +const FILE_NAME_REGEX: RegExp = /^.*\/(.*.pdf)$/; @Component({ selector: 'goofy-client-download-documentation-button', diff --git a/goofy-client/package.json b/goofy-client/package.json index f3cc6f84ca9355a8f5492610677586f2f2412ea0..2845ef85f7653a7c6373040601dba9ed5646e45a 100644 --- a/goofy-client/package.json +++ b/goofy-client/package.json @@ -34,12 +34,12 @@ "help": "nx help", "favicon": "real-favicon generate favicon/faviconDescription.json favicon/faviconData.json src/favicon", "cypress:run": "npx cypress run --project apps/goofy-e2e", - "cypress:run-main": "npx cypress run --project apps/goofy-e2e --config video=false,integrationFolder=./src/integration/main-tests", - "cypress:run-ea": "npx cypress run --project apps/goofy-e2e --config video=false,integrationFolder=./src/integration/einheitlicher-ansprechpartner", + "cypress:run-main": "npx cypress run --project apps/goofy-e2e --spec apps/goofy-e2e/src/e2e/main-tests", + "cypress:run-ea": "npx cypress run --project apps/goofy-e2e --spec apps/goofy-e2e/src/e2e/einheitlicher-ansprechpartner", "cypress:version": "npx cypress version", "cypress:install": "npx cypress install", "cypress:open": "npx cypress open --project apps/goofy-e2e", - "cypress:ci-run": "npm run cypress:delete-old-reports --REPORT_FOLDER=${npm_config_report_folder} ; NO_COLOR=1 npm run cypress:run -- --config=${npm_config_config} ; test=$(echo \"$?\") ; npm run cypress:pre-merge --REPORT_FOLDER=${npm_config_report_folder} ; npm run cypress:generate-report --REPORT_FOLDER=${npm_config_report_folder} ; exit $test", + "cypress:ci-run": "npm run cypress:delete-old-reports --REPORT_FOLDER=${npm_config_report_folder} ; NO_COLOR=1 npm run cypress:run -- --config-file=${npm_config_config} ; test=$(echo \"$?\") ; npm run cypress:pre-merge --REPORT_FOLDER=${npm_config_report_folder} ; npm run cypress:generate-report --REPORT_FOLDER=${npm_config_report_folder} ; exit $test", "cypress:delete-old-reports": "REPORT_FOLDER=${npm_config_report_folder} node apps/goofy-e2e/src/support/delete-old-reports.ts", "cypress:pre-merge": "REPORT_FOLDER=${npm_config_report_folder} node apps/goofy-e2e/src/support/pre-merge.ts", "cypress:generate-report": "npm run cypress:merge-report --REPORT_FOLDER=${npm_config_report_folder} ; npm run cypress:generate-html --REPORT_FOLDER=${npm_config_report_folder}", diff --git a/goofy-client/pom.xml b/goofy-client/pom.xml index d63ae820e5a084c655eede5de0f913b90d464bbc..0cda8691e2dce048d1d62f5c774a959e535f3019 100644 --- a/goofy-client/pom.xml +++ b/goofy-client/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>1.15.0-SNAPSHOT</version> + <version>1.16.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> @@ -54,83 +54,81 @@ </configuration> </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <version>3.0.0</version> - <executions> - - <execution> - <id>test-application</id> - <phase>test</phase> - <configuration> - <workingDirectory>./</workingDirectory> - <executable>npm</executable> - <arguments> - <argument>run</argument> - <argument>test</argument> - </arguments> - </configuration> - <goals> - <goal>exec</goal> - </goals> - </execution> - - <execution> - <id>build-application</id> - <phase>compile</phase> - <configuration> - <workingDirectory>./</workingDirectory> - <executable>npm</executable> - <arguments> - <argument>run</argument> - <argument>ci-build</argument> - </arguments> - </configuration> - <goals> - <goal>exec</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.0.0</version> + <executions> + <execution> + <id>test-application</id> + <phase>test</phase> + <configuration> + <workingDirectory>./</workingDirectory> + <executable>npm</executable> + <arguments> + <argument>run</argument> + <argument>test</argument> + </arguments> + <skip>${skipTests}</skip> + </configuration> + <goals> + <goal>exec</goal> + </goals> + </execution> + <execution> + <id>build-application</id> + <phase>compile</phase> + <configuration> + <workingDirectory>./</workingDirectory> + <executable>npm</executable> + <arguments> + <argument>run</argument> + <argument>ci-build</argument> + </arguments> + </configuration> + <goals> + <goal>exec</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> - <profiles> - <profile> - <activation> - <property> - <name>!skipNpmInstall</name> - </property> - </activation> - <id>npmInstall</id> - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <version>3.0.0</version> - <executions> - <execution> - <id>install-dependencies</id> - <phase>generate-sources</phase> - <configuration> - <workingDirectory>./</workingDirectory> - <executable>npm</executable> - <arguments> - <argument>install</argument> - <argument>--legacy-peer-deps</argument> - </arguments> - </configuration> - <goals> - <goal>exec</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> + <profiles> + <profile> + <activation> + <property> + <name>!skipNpmInstall</name> + </property> + </activation> + <id>npmInstall</id> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>3.0.0</version> + <executions> + <execution> + <id>install-dependencies</id> + <phase>generate-sources</phase> + <configuration> + <workingDirectory>./</workingDirectory> + <executable>npm</executable> + <arguments> + <argument>install</argument> + </arguments> + </configuration> + <goals> + <goal>exec</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/goofy-server/pom.xml b/goofy-server/pom.xml index 3d10300f55b3f09e2044091cc76ea69c00caf813..28fe2d2e897585e351e34b1582aa06d4ded23936 100644 --- a/goofy-server/pom.xml +++ b/goofy-server/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>1.15.0-SNAPSHOT</version> + <version>1.16.0-SNAPSHOT</version> </parent> <artifactId>goofy-server</artifactId> @@ -15,32 +15,27 @@ <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> - <spring-boot.build-image.imageName>docker.ozg-sh.de/goofy:build-latest</spring-boot.build-image.imageName> - </properties> + <spring-boot.build-image.imageName>docker.ozg-sh.de/goofy:build-latest</spring-boot.build-image.imageName> + </properties> - <dependencies> + <dependencies> + <dependency> + <groupId>de.itvsh.ozg</groupId> + <artifactId>alfa-service</artifactId> + <version>${project.version}</version> + </dependency> - <dependency> - <groupId>de.itvsh.ozg</groupId> - <artifactId>alfa-service</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>de.itvsh.ozg</groupId> - <artifactId>alfa-xdomea</artifactId> - <version>${project.version}</version> - </dependency> + <dependency> + <groupId>de.itvsh.ozg</groupId> + <artifactId>alfa-xdomea</artifactId> + <version>${project.version}</version> + </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> - - </dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + </dependencies> <build> <finalName>${project.artifactId}</finalName> diff --git a/goofy-server/src/main/java/de/ozgcloud/alfa/AlfaServerApplication.java b/goofy-server/src/main/java/de/ozgcloud/alfa/AlfaServerApplication.java index 38814989905b94b6a5996c4d32a92290e55d0fe6..54525b45e9a5b1363132efdcbad4ae8269df23f7 100644 --- a/goofy-server/src/main/java/de/ozgcloud/alfa/AlfaServerApplication.java +++ b/goofy-server/src/main/java/de/ozgcloud/alfa/AlfaServerApplication.java @@ -49,14 +49,14 @@ public class AlfaServerApplication { } @Bean - public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() { + FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() { FilterRegistrationBean<ForwardedHeaderFilter> bean = new FilterRegistrationBean<>(); bean.setFilter(new ForwardedHeaderFilter()); return bean; } @Bean - public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("async-"); @@ -65,12 +65,12 @@ public class AlfaServerApplication { } @Bean - public CallScope callScope() { + CallScope callScope() { return new CallScope(); } @Bean - public BeanFactoryPostProcessor beanFactoryPostProcessor(CallScope callScope) { + BeanFactoryPostProcessor beanFactoryPostProcessor(CallScope callScope) { return new CallBeanFactoryPostProcessor(callScope); } } \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-dev.yml b/goofy-server/src/main/resources/application-dev.yml index be7dc4d1adb0c218ef1d2c908b35b8fbd2142632..66b1bb57668a073ceaeab1e2d50c1f47ed7632fa 100644 --- a/goofy-server/src/main/resources/application-dev.yml +++ b/goofy-server/src/main/resources/application-dev.yml @@ -2,14 +2,14 @@ goofy: production: false keycloak: - auth-server-url: https://sso.dev.by.ozg-cloud.de - realm: by-kiel-dev - resource: alfa + auth-server-url: https://sso.dev.by.ozg-cloud.de + realm: by-kiel-dev + resource: alfa server: error: include-stacktrace: always - + ozgcloud: feature: vorgang-export: true diff --git a/goofy-server/src/main/resources/application-e2e.yml b/goofy-server/src/main/resources/application-e2e.yml index 6b4c84428d26ff921ab8c233062ebd1b8837dce4..50cac7fbb8d11fe5c5ad23487bcdaed4b72a05d0 100644 --- a/goofy-server/src/main/resources/application-e2e.yml +++ b/goofy-server/src/main/resources/application-e2e.yml @@ -1,13 +1,11 @@ keycloak: realm: by-e2e-local-dev - resource: alfa - kop: forwarding: lninfo: url: classpath:files/LandesnetzInfo.html user-manager: - url: http://localhost:9091 + url: http://localhost:9092 ozgcloud: feature: @@ -15,4 +13,4 @@ ozgcloud: createBescheid: true user-assistance: documentation: - url: /assets/benutzerleitfaden/Benutzerleitfaden_2.5.pdf \ No newline at end of file + url: /assets/benutzerleitfaden/benutzerleitfaden.pdf \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-local.yml b/goofy-server/src/main/resources/application-local.yml index 4f0e0f12aab2ad4c7fd4c89d4263a8a03ccaa8e2..48b03024d8f9f74e8c4aa72a27dadeb853eb6739 100644 --- a/goofy-server/src/main/resources/application-local.yml +++ b/goofy-server/src/main/resources/application-local.yml @@ -7,11 +7,6 @@ logging: goofy: production: false -keycloak: - auth-server-url: http://localhost:8088 - realm: sh-kiel-dev #TODO adjust - resource: sh-kiel-dev-goofy #TODO adjust - server: error: include-stacktrace: always @@ -30,4 +25,12 @@ grpc: ozgcloud: feature: - vorgang-export: true \ No newline at end of file + vorgang-export: true + user-assistance: + documentation: + url: /assets/benutzerleitfaden/benutzerleitfaden.pdf + +keycloak: + auth-server-url: http://localhost:8088 + realm: sh-kiel-dev #TODO adjust + resource: sh-kiel-dev-goofy #TODO adjust \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-remotekc.yml b/goofy-server/src/main/resources/application-remotekc.yml index f27d8bf81a26dd58d2b10d44b8b01eb89add3c8f..60e21682a25f6e8445fcc8d21dea502e5de6f890 100644 --- a/goofy-server/src/main/resources/application-remotekc.yml +++ b/goofy-server/src/main/resources/application-remotekc.yml @@ -1,6 +1,4 @@ -keycloak: - realm: by-kiel-dev - resource: alfa - public-client: true - use-resource-role-mappings: true +keycloak: auth-server-url: https://sso.dev.by.ozg-cloud.de + realm: by-kiel-dev + resource: alfa \ No newline at end of file diff --git a/goofy-server/src/main/resources/application.yml b/goofy-server/src/main/resources/application.yml index a84e4da06ffa475b8240b43d5301fe68a85cbbda..711b24897872a68bff0743da9e7f7092de0aaa5d 100644 --- a/goofy-server/src/main/resources/application.yml +++ b/goofy-server/src/main/resources/application.yml @@ -4,12 +4,11 @@ logging: '[de.itvsh]': INFO '[de.ozgcloud]': INFO, '[org.springframework.security]': WARN - '[org.keycloak.adapters]': WARN spring: mvc: pathmatch: - matching-strategy: ant-path-matcher + matching-strategy: ant_path_matcher application: name: Goofy jackson: @@ -19,6 +18,12 @@ spring: multipart: max-file-size: 2GB max-request-size: 2GB + security: + oauth2: + resourceserver: + jwt: + issuer-uri: ${ozgcloud.oauth2.issuer-uri} + jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs server: http2: @@ -51,13 +56,6 @@ management: goofy: production: true - -keycloak: - auth-server-url: http://localhost:8088 - realm: sh-kiel-dev - resource: sh-kiel-dev-goofy - public-client: true - use-resource-role-mappings: true grpc: client: @@ -71,7 +69,7 @@ grpc: kop: auth: token: - secret: XPPWagXn3rDwKG6Ywoir + secret: XPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6YwoirXPPWagXn3rDwKG6Ywoir validity: 60000 upload: maxFileSize: @@ -79,4 +77,12 @@ kop: wiedervorlageAttachment: 40MB user-manager: profile-template: /api/userProfiles/%s - search-template: /api/userProfiles/?searchBy={searchBy} \ No newline at end of file + search-template: /api/userProfiles/?searchBy={searchBy} + +ozgcloud: + oauth2: + auth-server-url: ${keycloak.auth-server-url} + realm: ${keycloak.realm} + resource: ${keycloak.resource} + principle-attribute: preferred_username + issuer-uri: ${ozgcloud.oauth2.auth-server-url}/realms/${ozgcloud.oauth2.realm} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 46ae5cc7d1bce45bc36dd5ec8bf33208aa7dedea..f638626967522113a0fffbb5740fd92d5264d580 100644 --- a/pom.xml +++ b/pom.xml @@ -30,14 +30,14 @@ <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>1.15.0-SNAPSHOT</version> + <version>1.16.0-SNAPSHOT</version> <name>Goofy Parent</name> <packaging>pom</packaging> <parent> <groupId>de.itvsh.kop.common</groupId> <artifactId>kop-common-parent</artifactId> - <version>1.7.0</version> + <version>2.3.1</version> </parent> <modules> @@ -45,20 +45,20 @@ <module>goofy-server</module> <module>alfa-xdomea</module> <module>alfa-service</module> - </modules> + </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - + <pluto.version>1.13.0</pluto.version> - <kop-common-pdf.version>1.6.4</kop-common-pdf.version> + <kop-common-pdf.version>2.3.1</kop-common-pdf.version> <user-manager.version>1.6.0</user-manager.version> </properties> - + <build> <pluginManagement> - <plugins> + <plugins> <plugin> <groupId>com.mycila</groupId> <artifactId>license-maven-plugin</artifactId> diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index aff0dfc2523947fd1f62d054291478403089a1c2..696cc298187a3424fbfceed525c6f778c5af26c4 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -139,6 +139,8 @@ spec: mountPath: "/bindings/ca-certificates/user-manager-tls-ca.pem" subPath: ca.crt readOnly: true + - name: temp-dir + mountPath: "/tmp" volumes: - name: bindings configMap: @@ -146,6 +148,8 @@ spec: - name: user-manager-tls-certificate secret: secretName: user-manager-tls-cert + - name: temp-dir + emptyDir: {} dnsConfig: {} dnsPolicy: ClusterFirst imagePullSecrets: diff --git a/src/test/helm/deployment_bindings_test.yaml b/src/test/helm/deployment_bindings_test.yaml index a835a85e2d1a52bbe280128990f521bd1506af65..b10cfb4fffe37fe5749a6cb2e38a6f946d6f304a 100644 --- a/src/test/helm/deployment_bindings_test.yaml +++ b/src/test/helm/deployment_bindings_test.yaml @@ -47,6 +47,11 @@ tests: mountPath: "/bindings/ca-certificates/user-manager-tls-ca.pem" subPath: ca.crt readOnly: true + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: temp-dir + mountPath: "/tmp" - it: should have volume mounts set: usermanagerName: user-manager @@ -62,4 +67,9 @@ tests: content: name: user-manager-tls-certificate secret: - secretName: user-manager-tls-cert \ No newline at end of file + secretName: user-manager-tls-cert + - contains: + path: spec.template.spec.volumes + content: + name: temp-dir + emptyDir: {} \ No newline at end of file diff --git a/src/test/helm/deployment_defaults_env_test.yaml b/src/test/helm/deployment_defaults_env_test.yaml index f131dc469ec94a9c1d305de7d4e073e1a2fa7c60..6c64212eb3409ab825aac12fc8841dd68645ca74 100644 --- a/src/test/helm/deployment_defaults_env_test.yaml +++ b/src/test/helm/deployment_defaults_env_test.yaml @@ -50,21 +50,6 @@ tests: content: name: spring_profiles_active value: oc, test - - contains: - path: spec.template.spec.containers[0].env - content: - name: keycloak_realm - value: sh-helm-test - - contains: - path: spec.template.spec.containers[0].env - content: - name: keycloak_resource - value: alfa - - contains: - path: spec.template.spec.containers[0].env - content: - name: keycloak_auth-server-url - value: https://sso.sh.ozg-cloud.de - it: should have service binding root set: usermanagerName: user-manager