diff --git a/.github/workflows/code-climate-test-coverage.yaml b/.github/workflows/code-climate-test-coverage.yaml
index b447414..35be866 100644
--- a/.github/workflows/code-climate-test-coverage.yaml
+++ b/.github/workflows/code-climate-test-coverage.yaml
@@ -17,10 +17,10 @@ jobs:
uses: actions/checkout@v2
# Setup Java Environment
- - name: Set up JDK 1.8
+ - name: Set up JDK 17
uses: actions/setup-java@v1
with:
- java-version: 1.8
+ java-version: 17
# Cache maven dependencies
- name: Cache Java Dependencies
diff --git a/README.md b/README.md
index 8a5f39a..ca5d120 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# jag-document-utils
-[![Maintainability](https://api.codeclimate.com/v1/badges/a0b23562b87853f9824b/maintainability)](https://codeclimate.com/github/bcgov/jag-document-utils/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/a0b23562b87853f9824b/test_coverage)](https://codeclimate.com/github/bcgov/jag-document-utils/test_coverage)
+[![Lifecycle:Stable](https://img.shields.io/badge/Lifecycle-Stable-97ca00)](https://img.shields.io/badge/Lifecycle-Stable-97ca00) [![Maintainability](https://api.codeclimate.com/v1/badges/a0b23562b87853f9824b/maintainability)](https://codeclimate.com/github/bcgov/jag-document-utils/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/a0b23562b87853f9824b/test_coverage)](https://codeclimate.com/github/bcgov/jag-document-utils/test_coverage)
Justice Sector PDF Merge Microservice integration with Adobe Experience Manager
diff --git a/src/document-utils-api/pom.xml b/src/document-utils-api/pom.xml
index 46ebaa6..d39ca8e 100644
--- a/src/document-utils-api/pom.xml
+++ b/src/document-utils-api/pom.xml
@@ -6,26 +6,26 @@
org.springframework.boot
spring-boot-starter-parent
- 2.3.4.RELEASE
+ 3.3.4
ca.bc.gov.open
document-utils-api
- 0.1.0
+ 1.0.0
document-utils-api
A RESTful Document Merging Service
- 1.8
- 2.17.1
- 2.9.2
+ 17
+ 2.24.1
+ 3.0.0
1.4
- 2.8.0
+ 2.14.0
0.5
2.0.21
11.0.0
6.5.0
- 0.8.6
+ 0.8.12
2.7.1
0.3.2
11.0.3
@@ -66,17 +66,6 @@
-
- io.springfox
- springfox-swagger2
- ${springfox.version}
-
-
- io.springfox
- springfox-swagger-ui
- ${springfox.version}
-
-
org.springframework.boot
spring-boot-configuration-processor
@@ -105,12 +94,6 @@
${io.opentracing.contrib.version}
-
- org.apache.axis
- axis
- ${org.apache.axis.version}
-
-
org.apache.axis
axis-jaxrpc
@@ -160,16 +143,45 @@
wsdl4j
-
- org.keycloak
- keycloak-spring-boot-starter
- ${org.keycloak.version}
+ org.springframework.boot
+ spring-boot-starter-security
org.springframework.boot
- spring-boot-starter-security
+ spring-boot-starter-oauth2-resource-server
+
+
+
+ org.glassfish.corba
+ glassfish-corba-omgapi
+ 4.2.1
+
+
+
+ dev.akkinoc.spring.boot
+ logback-access-spring-boot-starter
+ 4.1.1
+
+
+
+
+ org.apache.axis
+ axis
+ ${org.apache.axis.version}
+
+
+
+ javax.activation
+ activation
+ 1.1.1
+
+
+
+ javax.xml.soap
+ javax.xml.soap-api
+ 1.4.0
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImpl.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImpl.java
index ba0afd2..169d56d 100644
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImpl.java
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImpl.java
@@ -1,18 +1,11 @@
package ca.bc.gov.open.jag.documentutils.adobe;
-import java.io.IOException;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Service;
-import org.springframework.util.Base64Utils;
-
+import ca.bc.gov.open.jag.documentutils.adobe.models.MergeDoc;
+import ca.bc.gov.open.jag.documentutils.api.MediaTypes;
+import ca.bc.gov.open.jag.documentutils.api.models.DocMergeRequest;
+import ca.bc.gov.open.jag.documentutils.api.models.DocMergeResponse;
+import ca.bc.gov.open.jag.documentutils.exception.MergeException;
+import ca.bc.gov.open.jag.documentutils.utils.PDFBoxUtilities;
import com.adobe.idp.Document;
import com.adobe.idp.dsc.clientsdk.ServiceClientFactory;
import com.adobe.livecycle.assembler.client.AssemblerOptionSpec;
@@ -23,13 +16,18 @@
import com.adobe.livecycle.docconverter.client.DocConverterServiceClient;
import com.adobe.livecycle.docconverter.client.PDFAConversionOptionSpec;
import com.adobe.livecycle.docconverter.client.PDFAConversionResult;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Service;
-import ca.bc.gov.open.jag.documentutils.adobe.models.MergeDoc;
-import ca.bc.gov.open.jag.documentutils.api.MediaTypes;
-import ca.bc.gov.open.jag.documentutils.api.models.DocMergeRequest;
-import ca.bc.gov.open.jag.documentutils.api.models.DocMergeResponse;
-import ca.bc.gov.open.jag.documentutils.exception.MergeException;
-import ca.bc.gov.open.jag.documentutils.utils.PDFBoxUtilities;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.stream.Collectors;
@Service
@@ -152,7 +150,7 @@ private LinkedList convertToLinkedList(DocMergeRequest request) {
private MergeDoc buildMergeDoc(ca.bc.gov.open.jag.documentutils.api.models.Document doc, DocMergeRequest request) {
- byte[] docBytes = Base64Utils.decode(doc.getData().getBytes());
+ byte[] docBytes = Base64.getDecoder().decode(doc.getData().getBytes());
if (request.getOptions().isForcePDFAOnLoad() && PDFBoxUtilities.isPDFXfa(docBytes)) {
logger.info("forcePDFA is on and document, order {}, is XFA. Converting to PDF/A...", doc.getIndex());
@@ -165,7 +163,7 @@ private MergeDoc buildMergeDoc(ca.bc.gov.open.jag.documentutils.api.models.Docum
private String buildOutputDocument(Document document) {
try {
- return Base64Utils.encodeToString(IOUtils.toByteArray(document.getInputStream()));
+ return Base64.getEncoder().encodeToString(IOUtils.toByteArray(document.getInputStream()));
} catch (IOException e) {
logger.error("Error creating pdf a ", e);
throw new MergeException("Error creating a pdf", e);
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/DocumentController.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/DocumentController.java
index bb1ab8c..12e9462 100644
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/DocumentController.java
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/DocumentController.java
@@ -4,8 +4,8 @@
import ca.bc.gov.open.jag.documentutils.adobe.AemService;
import ca.bc.gov.open.jag.documentutils.api.models.DocMergeRequest;
import ca.bc.gov.open.jag.documentutils.api.models.DocMergeResponse;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@@ -14,8 +14,6 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
-import javax.annotation.security.RolesAllowed;
-import javax.validation.Valid;
/**
*
@@ -28,7 +26,6 @@
@RestController
@Validated
@RequestMapping("document")
-@Api(tags = "Document Api")
@Scope("request")
public class DocumentController {
@@ -40,7 +37,6 @@ public DocumentController(AemService aemService) {
this.aemService = aemService;
}
- @ApiOperation("Merge a collection of PDF Document")
@PostMapping(value = {"/merge" },
consumes = MediaTypes.APPLICATION_JSON,
produces = MediaTypes.APPLICATION_JSON)
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/DocMergeRequest.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/DocMergeRequest.java
index b9fbcd0..d934d5f 100644
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/DocMergeRequest.java
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/DocMergeRequest.java
@@ -2,8 +2,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import jakarta.validation.Valid;
-import javax.validation.Valid;
import java.util.List;
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Document.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Document.java
index 3f5f0dc..140aa22 100644
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Document.java
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Document.java
@@ -2,8 +2,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
@JsonPropertyOrder({ "index", "data", "title" })
public class Document {
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Options.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Options.java
index 28ae8ee..ce47c15 100644
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Options.java
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/api/models/Options.java
@@ -2,8 +2,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-
-import javax.validation.constraints.NotNull;
+import jakarta.validation.constraints.NotNull;
/**
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverter.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverter.java
new file mode 100644
index 0000000..578c600
--- /dev/null
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverter.java
@@ -0,0 +1,73 @@
+package ca.bc.gov.open.jag.documentutils.security;
+
+import org.springframework.beans.factory.annotation.Value;
+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.server.resource.authentication.JwtAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Component
+public class JwtAuthConverter implements Converter {
+
+ private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter =
+ new JwtGrantedAuthoritiesConverter();
+
+ public static final String KEYCLOAK_PRINCIPLE_ATTRIBUTE = "preferred_username";
+ public static final String KEYCLOAK_RESOURCE_ATTRIBUTE = "resource_access";
+ public static final String KEYCLOAK_ROLE_ATTRIBUTE = "roles";
+
+ @Value("${jwt.auth.converter.resource-id}")
+ private String resourceId;
+
+ @Override
+ public AbstractAuthenticationToken convert(@NonNull Jwt jwt) {
+ Collection authorities = Stream.concat(
+ jwtGrantedAuthoritiesConverter.convert(jwt).stream(),
+ extractResourceRoles(jwt).stream()
+ ).collect(Collectors.toSet());
+
+ return new JwtAuthenticationToken(
+ jwt,
+ authorities,
+ getPrincipleClaimName(jwt)
+ );
+ }
+
+ private String getPrincipleClaimName(Jwt jwt) {
+ return jwt.getClaim(KEYCLOAK_PRINCIPLE_ATTRIBUTE);
+ }
+
+ private Collection extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
+ Map resourceAccess;
+ Map resource;
+ Collection resourceRoles;
+
+ Collection extends GrantedAuthority> resourceRoles1;
+ if (jwt.getClaim(KEYCLOAK_RESOURCE_ATTRIBUTE) == null) {
+ return Set.of();
+ }
+ resourceAccess = jwt.getClaim(KEYCLOAK_RESOURCE_ATTRIBUTE);
+
+ if (resourceAccess.get(resourceId) == null) {
+ return Set.of();
+ }
+ resource = (Map) resourceAccess.get(resourceId);
+
+ resourceRoles = (Collection) resource.get(KEYCLOAK_ROLE_ATTRIBUTE);
+ return resourceRoles
+ .stream()
+ .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
+ .collect(Collectors.toSet());
+ }
+}
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfig.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfig.java
deleted file mode 100644
index cd679ac..0000000
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfig.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package ca.bc.gov.open.jag.documentutils.security;
-
-import org.keycloak.adapters.KeycloakConfigResolver;
-import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
-import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
-import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
-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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
-import org.springframework.security.core.session.SessionRegistryImpl;
-import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
-import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.CorsConfigurationSource;
-import org.springframework.web.cors.CorsUtils;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-
-@Configuration
-@EnableWebSecurity
-@EnableGlobalMethodSecurity(jsr250Enabled = true)
-public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- super.configure(http);
- http.cors()
- .and()
- .authorizeRequests()
- .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
- .anyRequest()
- .permitAll();
- http.csrf().disable();
- }
-
-
- @Bean
- CorsConfigurationSource corsConfigurationSource() {
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
-
- CorsConfiguration corsConfiguration = new CorsConfiguration();
- corsConfiguration.applyPermitDefaultValues();
- corsConfiguration.addAllowedMethod(HttpMethod.DELETE);
- corsConfiguration.addAllowedMethod(HttpMethod.PUT);
-
- source.registerCorsConfiguration("/**", corsConfiguration);
-
- return source;
- }
-
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
- KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
- keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
- auth.authenticationProvider(keycloakAuthenticationProvider);
- }
-
- @Bean
- @Override
- protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
- return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
- }
-
- @Bean
- public KeycloakConfigResolver keycloakConfigResolver() {
- return new KeycloakSpringBootConfigResolver();
- }
-
-}
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfiguration.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfiguration.java
new file mode 100644
index 0000000..2208f45
--- /dev/null
+++ b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfiguration.java
@@ -0,0 +1,44 @@
+package ca.bc.gov.open.jag.documentutils.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+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.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+
+import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+class SecurityConfiguration {
+
+ private final JwtAuthConverter jwtAuthConverter;
+
+ public SecurityConfiguration(JwtAuthConverter jwtAuthConverter) {
+ this.jwtAuthConverter = jwtAuthConverter;
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+
+ http.csrf(
+ AbstractHttpConfigurer::disable).cors(
+ AbstractHttpConfigurer::disable);
+
+ http.authorizeHttpRequests(requests -> requests
+ .requestMatchers(antMatcher("/actuator/**")).permitAll()
+ .anyRequest().authenticated());
+
+ http.oauth2ResourceServer((oauth2) -> oauth2.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwtAuthConverter)));
+
+ http.sessionManagement(
+ httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(
+ SessionCreationPolicy.STATELESS));
+
+ return http.build();
+ }
+}
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerAPIConfig.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerAPIConfig.java
deleted file mode 100644
index 79ba057..0000000
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerAPIConfig.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package ca.bc.gov.open.jag.documentutils.swagger2;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.MediaType;
-import org.springframework.web.accept.ContentNegotiationManager;
-import org.springframework.web.accept.ContentNegotiationStrategy;
-
-import springfox.documentation.swagger2.web.Swagger2Controller;
-
-/**
- *
- * Do not tamper with this class.
- *
- * Forces application/json content-type due to problem with Swagger 2.9.2 when calling http(s)://host/digitalforms/v2/api-docs
- * @See https://github.com/springfox/springfox/issues/1835
- *
- */
-@Configuration
-public class SwaggerAPIConfig {
-
- private static final List SWAGGER_MEDIA_TYPES = Arrays.asList(MediaType.APPLICATION_JSON,
- MediaType.valueOf("application/hal+json"));
-
- /**
- * Avoid swagger api being mapped to XML with jackson xml on the class path
- *
- * @param manager Using the ready built content negotiation manager instead
- * of
- * {@link org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer}
- * to not mess with other configurations
- *
- * @param swaggerPath The path on which the swagger specs are served
- */
- @SuppressWarnings("el-syntax")
- @Autowired
- public void addSpringfoxContentNegotiation(ContentNegotiationManager manager,
- @Value("${springfox.documentation.swagger.v2.path:" + Swagger2Controller.DEFAULT_URL
- + "}") String swaggerPath) {
- manager.getStrategies().add(0, webRequest -> {
- HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
- if (request != null && request.getServletPath().startsWith(swaggerPath)) {
- return SWAGGER_MEDIA_TYPES;
- }
- return ContentNegotiationStrategy.MEDIA_TYPE_ALL_LIST;
- });
- }
-}
diff --git a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfig.java b/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfig.java
deleted file mode 100644
index b053dc8..0000000
--- a/src/document-utils-api/src/main/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfig.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package ca.bc.gov.open.jag.documentutils.swagger2;
-
-import com.google.common.base.Predicates;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.service.ApiInfo;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
-
-/**
- *
- * Swagger2 config
- *
- * Note the use of the ConditionOnProperty for enabling / disabling Swagger2 from properties file.
- *
- * @author shaunmillargov
- *
- */
-@Configuration
-@EnableSwagger2
-@ConditionalOnProperty(prefix = "docmerge", name = "service-swagger-enabled")
-public class SwaggerConfig implements WebMvcConfigurer {
-
-
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
-
- registry
- .addResourceHandler("swagger-ui.html")
- .addResourceLocations("classpath:/META-INF/resources/");
-
- registry
- .addResourceHandler("/webjars/**")
- .addResourceLocations("classpath:/META-INF/resources/webjars/");
- }
-
- @Bean
- public Docket apiDocket() {
-
- return new Docket(DocumentationType.SWAGGER_2)
- .apiInfo(getApiInfo())
- .select()
- .apis(RequestHandlerSelectors.basePackage("ca.bc.gov.open.jag.documentutils.api"))
- .paths(Predicates.not(PathSelectors.regex("/\\*\\*/.*/\\*\\*"))) // Ignore default path mappings
- .build();
- }
-
- private ApiInfo getApiInfo() {
-
- return new ApiInfoBuilder()
- .title("Document Merge RESTful Service")
- .description("Document Merge API")
- .build();
- }
-}
diff --git a/src/document-utils-api/src/main/resources/application.properties b/src/document-utils-api/src/main/resources/application.properties
index e5d8c91..e9fd6cb 100644
--- a/src/document-utils-api/src/main/resources/application.properties
+++ b/src/document-utils-api/src/main/resources/application.properties
@@ -22,13 +22,10 @@ management.endpoint.health.show-details=always
management.endpoint.health.show-components=always
# Keycloak configuration
-keycloak.auth-server-url=${JDU_KC_AUTH_SERVER_URL}
-keycloak.realm=${JDU_KC_REALM}
-keycloak.resource=${JDU_KC_RESOURCE:jag-document-utils-api}
-keycloak.credentials.secret=${JDU_KC_SECRET}
-keycloak.use-resource-role-mappings=${JDU_KC_USE_RESOURCE_ROLE_MAPPINGS:true}
-keycloak.bearer-only=${JDU_KC_BEARER_ONLY:true}
-keycloak.ssl-required=${JDU_KC_SSL_REQUIRED:external}
+jwt.auth.converter.resource-id=${JDU_KC_AUTH_RESOURCE_ID:document_utils}
+spring.jackson.default-property-inclusion=non-null
+spring.security.oauth2.resourceserver.jwt.issuer-uri=${JDU_KC_AUTH_SERVER_URL:http://localhost:8081/auth/realms/jag-document-utils-api}
+spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
#Actuator Info Endpoint Configuration
info.app.name=@project.name@
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/TestHelpers.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/TestHelpers.java
index 08d5b78..fd21dc0 100644
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/TestHelpers.java
+++ b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/TestHelpers.java
@@ -1,10 +1,9 @@
package ca.bc.gov.open.jag.documentutils;
-import java.io.*;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.MessageFormat;
-import java.util.Arrays;
public class TestHelpers {
private TestHelpers() {}
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImplTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImplTest.java
index 1617dad..bf62d4c 100644
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImplTest.java
+++ b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/adobe/AemServiceImplTest.java
@@ -58,7 +58,7 @@ public class AemServiceImplTest {
@BeforeEach
public void beforeEach() throws OperationException, DSCException, IOException, ConversionException {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.openMocks(this);
Mockito
.doReturn(assemblerResultMock)
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/controller/DocumentControllerTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/controller/DocumentControllerTest.java
index fccc7e8..abbab1d 100644
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/controller/DocumentControllerTest.java
+++ b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/controller/DocumentControllerTest.java
@@ -1,12 +1,13 @@
package ca.bc.gov.open.jag.documentutils.controller;
+import ca.bc.gov.open.jag.documentutils.adobe.AemService;
import ca.bc.gov.open.jag.documentutils.api.DocumentController;
import ca.bc.gov.open.jag.documentutils.api.models.DocMergeRequest;
import ca.bc.gov.open.jag.documentutils.api.models.DocMergeResponse;
import ca.bc.gov.open.jag.documentutils.api.models.Document;
import ca.bc.gov.open.jag.documentutils.api.models.Options;
-import ca.bc.gov.open.jag.documentutils.adobe.AemService;
import ca.bc.gov.open.jag.documentutils.exception.MergeException;
+import jakarta.validation.Valid;
import org.junit.jupiter.api.*;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
@@ -15,7 +16,6 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/exception/GlobalControllerAdviceTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/exception/GlobalControllerAdviceTest.java
index aab06d3..46d6c8e 100644
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/exception/GlobalControllerAdviceTest.java
+++ b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/exception/GlobalControllerAdviceTest.java
@@ -22,7 +22,6 @@
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.List;
@@ -38,13 +37,13 @@ public class GlobalControllerAdviceTest {
private BindingResult bindingResultMock;
@Mock
private MethodParameter parameterMock;
- @Mock
- private Executable executableMock;
+ //@Mock
+ //private Executable executableMock;
@BeforeEach
public void beforeEach() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.openMocks(this);
sut = new GlobalControllerAdvice();
@@ -119,7 +118,7 @@ public HttpHeaders getHeaders() {
@Test
@DisplayName("400: with MethodArgumentNotValidException should return ApiError")
- public void testhandleValidationExceptions() {
+ public void testhandleValidationExceptions() throws NoSuchMethodException {
Mockito.when(webRequestMock.getHeader(Mockito.eq(Keys.CORRELATION_ID))).thenReturn(TRANSACTION_ID);
@@ -130,19 +129,23 @@ public void testhandleValidationExceptions() {
Mockito.when(bindingResultMock.getAllErrors()).thenReturn(errors);
Mockito.when(parameterMock.getParameterIndex()).thenReturn(0);
- Mockito.when(parameterMock.getExecutable()).thenReturn(executableMock);
- Mockito.when(executableMock.toGenericString()).thenReturn("string");
+ Mockito.when(parameterMock.getExecutable()).thenReturn(
+ this.getClass().getDeclaredMethod("fake", GlobalControllerAdviceTest.class));
MethodArgumentNotValidException exception = new MethodArgumentNotValidException(parameterMock, bindingResultMock);
ResponseEntity actual = sut.handleValidationExceptions(exception, webRequestMock);
Assertions.assertEquals(HttpStatus.BAD_REQUEST, actual.getStatusCode());
Assertions.assertEquals(TRANSACTION_ID, actual.getBody().getTransactionId());
- Assertions.assertEquals("Validation failed for argument [0] in string with 2 errors: [Field error in object 'param' on field 'first': rejected value [null]; codes []; arguments []; default message [message]] ", actual.getBody().getDetail());
+ Assertions.assertEquals("Validation failed for argument [0] in private void ca.bc.gov.open.jag.documentutils.exception.GlobalControllerAdviceTest.fake(ca.bc.gov.open.jag.documentutils.exception.GlobalControllerAdviceTest) with 2 errors: [Field error in object 'param' on field 'first': rejected value [null]; codes []; arguments []; default message [message]] ", actual.getBody().getDetail());
Assertions.assertEquals("MethodArgumentNotValidException", actual.getBody().getError());
Assertions.assertEquals("Unknown exception while trying to merge documents.", actual.getBody().getMessage());
}
+ //This is to fake the executable that can no longet be mocked in java 17
+ private void fake(ca.bc.gov.open.jag.documentutils.exception.GlobalControllerAdviceTest fake) {
+
+ }
}
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverterTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverterTest.java
new file mode 100644
index 0000000..ff1c00d
--- /dev/null
+++ b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/JwtAuthConverterTest.java
@@ -0,0 +1,65 @@
+package ca.bc.gov.open.jag.documentutils.security;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+public class JwtAuthConverterTest {
+
+ @Test
+ void testNull() {
+ Jwt jwt = null;
+ JwtAuthConverter converter = new JwtAuthConverter();
+ assertThrows(NullPointerException.class, () -> converter.convert(null));
+ }
+
+ @Test
+ void testJwtMissingResourceAccess() throws Exception {
+ Jwt jwt = Mockito.mock(Jwt.class);
+
+ JwtAuthConverter converter = new JwtAuthConverter();
+ AbstractAuthenticationToken authenticationToken = converter.convert(jwt);
+ Collection authorities = authenticationToken.getAuthorities();
+ assertEquals(0, authorities.size());
+ }
+
+ @Test
+ void testJwtMissingResource() throws Exception {
+ Jwt jwt = Mockito.mock(Jwt.class);
+ Map resourceAccess = new HashMap();
+ when(jwt.getClaim(JwtAuthConverter.KEYCLOAK_RESOURCE_ATTRIBUTE)).thenReturn(resourceAccess);
+
+ JwtAuthConverter converter = new JwtAuthConverter();
+ AbstractAuthenticationToken authenticationToken = converter.convert(jwt);
+ Collection authorities = authenticationToken.getAuthorities();
+ assertEquals(0, authorities.size());
+ }
+
+ @Test
+ void testJwtMissingRoles() throws Exception {
+ Jwt jwt = Mockito.mock(Jwt.class);
+
+ Map resource = new HashMap();
+
+ Map resourceAccess = new HashMap();
+ resourceAccess.put("doc-utils", resource);
+ when(jwt.getClaim(JwtAuthConverter.KEYCLOAK_RESOURCE_ATTRIBUTE)).thenReturn(resourceAccess);
+
+ JwtAuthConverter converter = new JwtAuthConverter();
+ AbstractAuthenticationToken authenticationToken = converter.convert(jwt);
+ Collection authorities = authenticationToken.getAuthorities();
+ assertEquals(0, authorities.size());
+ }
+
+
+}
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfigTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfigTest.java
deleted file mode 100644
index ad3c01d..0000000
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/security/SecurityConfigTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package ca.bc.gov.open.jag.documentutils.security;
-
-import org.junit.jupiter.api.Test;
-import org.keycloak.adapters.KeycloakConfigResolver;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class SecurityConfigTest {
-
- ApplicationContextRunner context = new ApplicationContextRunner()
- .withUserConfiguration(SecurityConfig.class);
-
- @Test
- public void testConfigure() throws Exception {
-
- context.run(it -> {
- assertThat(it).hasSingleBean(SessionAuthenticationStrategy.class);
- assertThat(it).hasSingleBean(KeycloakConfigResolver.class);
- });
-
- }
-
-}
diff --git a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfigTest.java b/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfigTest.java
deleted file mode 100644
index 72f81eb..0000000
--- a/src/document-utils-api/src/test/java/ca/bc/gov/open/jag/documentutils/swagger2/SwaggerConfigTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package ca.bc.gov.open.jag.documentutils.swagger2;
-
-import com.adobe.idp.dsc.clientsdk.ServiceClientFactory;
-import com.adobe.livecycle.assembler.client.AssemblerServiceClient;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import springfox.documentation.spring.web.plugins.Docket;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class SwaggerConfigTest {
- ApplicationContextRunner context = new ApplicationContextRunner()
- .withPropertyValues(
- "docmerge.service-swagger-enabled=true"
- )
- .withUserConfiguration(SwaggerConfig.class);
-
- @Test
- public void testConfiguration() {
-
-
- context.run(it -> {
-
- assertThat(it).hasSingleBean(Docket.class);
-
- });
-
- }
-}
diff --git a/src/pom.xml b/src/pom.xml
index 676ba70..bc77fa7 100644
--- a/src/pom.xml
+++ b/src/pom.xml
@@ -6,7 +6,7 @@
ca.bc.gov.open
jag-document-utils
- 0.1.0
+ 1.0.0
Document Utilities
@@ -16,7 +16,7 @@
2.17.1
- 1.8
+ 17
diff --git a/testing/jag-document-utils-env.postman_environment.json b/testing/jag-document-utils-env.postman_environment.json
new file mode 100644
index 0000000..b4409e9
--- /dev/null
+++ b/testing/jag-document-utils-env.postman_environment.json
@@ -0,0 +1,33 @@
+{
+ "id": "e03086d2-2a9a-405a-a561-9e92d2b8cdc0",
+ "name": "jag-document-utils-env",
+ "values": [
+ {
+ "key": "keycloakUrl",
+ "value": "http://localhost:8081/realms/jag",
+ "type": "default",
+ "enabled": true
+ },
+ {
+ "key": "baseApiUrl",
+ "value": "http://localhost:5656",
+ "type": "default",
+ "enabled": true
+ },
+ {
+ "key": "keycloakCredentail",
+ "value": "",
+ "type": "secret",
+ "enabled": true
+ },
+ {
+ "key": "jwt",
+ "value": "",
+ "type": "default",
+ "enabled": true
+ }
+ ],
+ "_postman_variable_scope": "environment",
+ "_postman_exported_at": "2024-10-21T17:23:33.068Z",
+ "_postman_exported_using": "Postman/11.13.1"
+}
\ No newline at end of file
diff --git a/testing/jag-document-utils.postman_collection.json b/testing/jag-document-utils.postman_collection.json
new file mode 100644
index 0000000..135252d
--- /dev/null
+++ b/testing/jag-document-utils.postman_collection.json
@@ -0,0 +1,122 @@
+{
+ "info": {
+ "_postman_id": "9c6e2805-f935-4237-afed-77f442808f56",
+ "name": "jag-document-utils",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "7463243"
+ },
+ "item": [
+ {
+ "name": "auth",
+ "item": [
+ {
+ "name": "token",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Get jwt\", function () {\r",
+ " var jsonData = pm.response.json();\r",
+ " pm.environment.set(\"jwt\", jsonData.access_token);\r",
+ "});"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "urlencoded",
+ "urlencoded": [
+ {
+ "key": "grant_type",
+ "value": "client_credentials",
+ "type": "default"
+ },
+ {
+ "key": "client_secret",
+ "value": "{{keycloakCredentail}}",
+ "type": "default"
+ },
+ {
+ "key": "client_id",
+ "value": "justin",
+ "type": "default"
+ },
+ {
+ "key": "scope",
+ "value": "",
+ "type": "default",
+ "disabled": true
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{keycloakUrl}}",
+ "host": [
+ "{{keycloakUrl}}"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "merge",
+ "item": [
+ {
+ "name": "doc merge",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "{{jwt}}",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-Correlation-ID",
+ "value": "1231231",
+ "type": "default"
+ },
+ {
+ "key": "X-Client-ID",
+ "value": "123123",
+ "type": "default"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"options\":{\n \"forcePDFAOnLoad\": true,\n \"createToC\": false\n },\n \"documents\":[\n {\n \"id\":\"optional\",\n \"docType\":\"pdf\",\n \"order\":1,\n \"data\": {{fileDat}}\n }\n ]\n}"
+ },
+ "url": {
+ "raw": "{{baseApiUrl}}/document/merge",
+ "host": [
+ "{{baseApiUrl}}"
+ ],
+ "path": [
+ "document",
+ "merge"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file