diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts index a91b1c6405bcdd5b2a63c43967dc0ad303701406..7170580c898d0bd3f1b8c994c09304f8a20778e5 100644 --- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts +++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts @@ -104,6 +104,7 @@ export class ResourceDocumentPanelComponent implements AfterViewInit, BeforeLeav this._document = value; this.documentForm.disable(); if (!!value) { + this.codemirror.setOptionIfChanged("mode",value.mimeType); this.documentForm.controls['mimeType'].setValue(value.mimeType); this.documentForm.controls['name'].setValue(value.name); this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion); diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html index d302ec3c8ca78a8fb9f56f2da4d71260da09224d..47d0638fdb776cebbf18f2fc90e0096042673889 100644 --- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html +++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html @@ -86,7 +86,7 @@ </div> <mat-toolbar class="mat-elevation-z2" style="flex-grow: 0"> <mat-toolbar-row class="smp-toolbar-row"> - <button d="cancel_id" mat-raised-button color="primary" + <button id="cancel_id" mat-raised-button color="primary" [disabled]="cancelButtonDisabled" (click)="onDocumentResetButtonClicked()"> <mat-icon>cancel</mat-icon> diff --git a/smp-examples/pom.xml b/smp-examples/pom.xml index 328551ec82a5e8e131ef9cc665c5ec501da2795d..bf4d7343746a985cf5499cee05bbb3bbc63dae4e 100644 --- a/smp-examples/pom.xml +++ b/smp-examples/pom.xml @@ -26,5 +26,6 @@ <description>The sub-project contains SMP examples of API and SPI implementations. Currently, SPI payload validation example.</description> <modules> <module>smp-spi-payload-validation-example</module> + <module>resource-spi-example</module> </modules> </project> diff --git a/smp-examples/resource-spi-example/pom.xml b/smp-examples/resource-spi-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..090fe5c425b165cf09758f9942cd071a1a2a0b49 --- /dev/null +++ b/smp-examples/resource-spi-example/pom.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2017 European Commission | CEF eDelivery + ~ + ~ Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent versions of the EUPL (the "Licence"); + ~ You may not use this work except in compliance with the Licence. + ~ + ~ You may obtain a copy of the Licence attached in file: LICENCE-EUPL-v1.2.pdf + ~ + ~ Unless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an "AS IS" basis, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the Licence for the specific language governing permissions and limitations under the Licence. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>eu.europa.ec.edelivery</groupId> + <artifactId>smp-modules</artifactId> + <version>5.0-RC2-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + <properties> + <maven.deploy.skip>false</maven.deploy.skip> + <org.glassfish.jaxb.jaxb-runtime.version>2.3.8</org.glassfish.jaxb.jaxb-runtime.version> + <jakarta.xml.bind-api.version>2.3.3</jakarta.xml.bind-api.version> + </properties> + <artifactId>resource-spi-example</artifactId> + <name>resource-spi-example</name> + <packaging>jar</packaging> + <description>Various resource examples.</description> + + <dependencies> + <dependency> + <groupId>eu.europa.ec.edelivery</groupId> + <artifactId>smp-spi</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>eu.europa.ec.edelivery</groupId> + <artifactId>edelivery-springsecurity-2-way-ssl-auth</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-inline</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <dependencies> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-surefire-provider</artifactId> + <version>1.3.2</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit-jupiter.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + </plugins> + </build> +</project> diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/DomiSMPResourceExampleExtension.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/DomiSMPResourceExampleExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..63e286c5621cd6b2dcbac403582a581434e8f7b3 --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/DomiSMPResourceExampleExtension.java @@ -0,0 +1,70 @@ +package eu.europa.ec.smp.spi.examples; + +import eu.europa.ec.smp.spi.ExtensionInfo; +import eu.europa.ec.smp.spi.PayloadValidatorSpi; +import eu.europa.ec.smp.spi.examples.def.DomiSMPJsonResourceExample; +import eu.europa.ec.smp.spi.examples.def.DomiSMPPropertyResourceExample; +import eu.europa.ec.smp.spi.resource.ResourceDefinitionSpi; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Joze Rihtarsic + * @since 5.0 + * <p> + * Extension implementation for handling the Oasis CPPA-cpp resources. + */ +@Service +public class DomiSMPResourceExampleExtension implements ExtensionInfo { + + final DomiSMPPropertyResourceExample domiSMPPropertyResourceExample; + final DomiSMPJsonResourceExample jsonResourceExample; + + public DomiSMPResourceExampleExtension(DomiSMPPropertyResourceExample domiSMPPropertyResourceExample, DomiSMPJsonResourceExample jsonResourceExample) { + this.domiSMPPropertyResourceExample = domiSMPPropertyResourceExample; + this.jsonResourceExample = jsonResourceExample; + } + + @Override + public String identifier() { + return "domismp-resource-example-extension"; + } + + @Override + public String name() { + return "Resource example extension"; + } + + @Override + public String description() { + return "The extension implements json and property examples"; + } + + @Override + public String version() { + return "1.0"; + } + + @Override + public List<ResourceDefinitionSpi> resourceTypes() { + return Arrays.asList(jsonResourceExample, domiSMPPropertyResourceExample); + } + + @Override + public List<PayloadValidatorSpi> payloadValidators() { + return Collections.emptyList(); + } + + + @Override + public String toString() { + return "DomiSMPResourceExampleExtension{" + + "identifier=" + identifier() + + "name=" + name() + + "version=" + version() + + '}'; + } +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPJsonResourceExample.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPJsonResourceExample.java new file mode 100644 index 0000000000000000000000000000000000000000..8b801b5eca5afcda07811e96e149d2117835fe47 --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPJsonResourceExample.java @@ -0,0 +1,73 @@ +package eu.europa.ec.smp.spi.examples.def; + +import eu.europa.ec.smp.spi.examples.handler.DomiSMPJSONHandlerExample; +import eu.europa.ec.smp.spi.resource.ResourceDefinitionSpi; +import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi; +import eu.europa.ec.smp.spi.resource.SubresourceDefinitionSpi; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + + +/** + * The Oasis CPPA cpp document + * + * @author Joze Rihtarsic + * @since 5.0 + */ +@Component +public class DomiSMPJsonResourceExample implements ResourceDefinitionSpi { + + + DomiSMPJSONHandlerExample documentHandler; + + public DomiSMPJsonResourceExample(DomiSMPJSONHandlerExample documentHandler) { + this.documentHandler = documentHandler; + } + + @Override + public String identifier() { + return "domismp-resource-example-json"; + } + + @Override + public String defaultUrlSegment() { + return "json"; + } + + @Override + public String name() { + return "DomiSMP JSON example"; + } + + @Override + public String description() { + return "DomiSMP JSON example"; + } + + @Override + public String mimeType() { + return "application/json"; + } + + @Override + public List<SubresourceDefinitionSpi> getSuresourceSpiList() { + return Collections.emptyList(); + } + + @Override + public ResourceHandlerSpi getResourceHandler() { + return documentHandler; + } + + @Override + public String toString() { + return "DomiSMPJsonResourceExample {" + + "identifier=" + identifier() + + "defaultUrlSegment=" + defaultUrlSegment() + + "name=" + name() + + "mimeType=" + mimeType() + + '}'; + } +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPPropertyResourceExample.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPPropertyResourceExample.java new file mode 100644 index 0000000000000000000000000000000000000000..61f0d29a179e2be4be02eaa3f1fdae2e64697905 --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/def/DomiSMPPropertyResourceExample.java @@ -0,0 +1,73 @@ +package eu.europa.ec.smp.spi.examples.def; + +import eu.europa.ec.smp.spi.examples.handler.DomiSMPPropertyHandlerExample; +import eu.europa.ec.smp.spi.resource.ResourceDefinitionSpi; +import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi; +import eu.europa.ec.smp.spi.resource.SubresourceDefinitionSpi; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + + +/** + * The Oasis CPPA cpp document + * + * @author Joze Rihtarsic + * @since 5.0 + */ +@Component +public class DomiSMPPropertyResourceExample implements ResourceDefinitionSpi { + + + DomiSMPPropertyHandlerExample documentHandler; + + public DomiSMPPropertyResourceExample(DomiSMPPropertyHandlerExample documentHandler) { + this.documentHandler = documentHandler; + } + + @Override + public String identifier() { + return "domismp-resource-example-properties"; + } + + @Override + public String defaultUrlSegment() { + return "prop"; + } + + @Override + public String name() { + return "DomiSMP property example"; + } + + @Override + public String description() { + return "DomiSMP property example"; + } + + @Override + public String mimeType() { + return "text/x-properties"; + } + + @Override + public List<SubresourceDefinitionSpi> getSuresourceSpiList() { + return Collections.emptyList(); + } + + @Override + public ResourceHandlerSpi getResourceHandler() { + return documentHandler; + } + + @Override + public String toString() { + return "DomiSMPPropertyResourceExample {" + + "identifier=" + identifier() + + "defaultUrlSegment=" + defaultUrlSegment() + + "name=" + name() + + "mimeType=" + mimeType() + + '}'; + } +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/AbstractHandler.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/AbstractHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..7501902b06aacbb4c7c0b8e5ad17e522beabe5fb --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/AbstractHandler.java @@ -0,0 +1,52 @@ +package eu.europa.ec.smp.spi.examples.handler; + +import eu.europa.ec.smp.spi.api.model.RequestData; +import eu.europa.ec.smp.spi.api.model.ResourceIdentifier; +import eu.europa.ec.smp.spi.exceptions.ResourceException; +import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * The abstract class with common methods for implementation of the ResourceHandlerSpi. + * + * @author Joze Rihtarsic + * @since 5.0 + */ +public abstract class AbstractHandler implements ResourceHandlerSpi { + + static final Logger LOG = LoggerFactory.getLogger(AbstractHandler.class); + + private static final String DISALLOW_DOCTYPE_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; + + + public byte[] readFromInputStream(InputStream inputStream) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] data = new byte[4096]; + int nRead; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + return buffer.toByteArray(); + } + + public ResourceIdentifier getResourceIdentifier(RequestData resourceData) throws ResourceException { + if (resourceData == null || resourceData.getResourceIdentifier() == null || StringUtils.isEmpty(resourceData.getResourceIdentifier().getValue())) { + throw new ResourceException(ResourceException.ErrorCode.INVALID_PARAMETERS, "Missing resource identifier for the resource CPP "); + } + return resourceData.getResourceIdentifier(); + } + + public ResourceIdentifier getSubresourceIdentifier(RequestData resourceData) throws ResourceException { + if (resourceData == null || resourceData.getSubresourceIdentifier() == null || StringUtils.isEmpty(resourceData.getSubresourceIdentifier().getValue())) { + throw new ResourceException(ResourceException.ErrorCode.INVALID_PARAMETERS, "Missing sub-resource identifier for the resource service metadata!"); + } + return resourceData.getSubresourceIdentifier(); + } +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPJSONHandlerExample.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPJSONHandlerExample.java new file mode 100644 index 0000000000000000000000000000000000000000..bcf149967de3062282eccf50be7d606d9e978a9f --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPJSONHandlerExample.java @@ -0,0 +1,210 @@ +package eu.europa.ec.smp.spi.examples.handler; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.edelivery.security.utils.CertificateKeyType; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; +import eu.europa.ec.smp.spi.api.SmpDataServiceApi; +import eu.europa.ec.smp.spi.api.SmpIdentifierServiceApi; +import eu.europa.ec.smp.spi.api.SmpXmlSignatureApi; +import eu.europa.ec.smp.spi.api.model.RequestData; +import eu.europa.ec.smp.spi.api.model.ResourceIdentifier; +import eu.europa.ec.smp.spi.api.model.ResponseData; +import eu.europa.ec.smp.spi.exceptions.ResourceException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.bouncycastle.operator.OperatorCreationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.OffsetDateTime; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; + +@Component +public class DomiSMPJSONHandlerExample extends AbstractHandler { + + + private static final Logger LOG = LoggerFactory.getLogger(DomiSMPJSONHandlerExample.class); + + + + final SmpDataServiceApi smpDataApi; + final SmpIdentifierServiceApi smpIdentifierApi; + + final SmpXmlSignatureApi signatureApi; + + + public DomiSMPJSONHandlerExample(SmpDataServiceApi smpDataApi, + SmpIdentifierServiceApi smpIdentifierApi, + SmpXmlSignatureApi signatureApi) { + this.smpDataApi = smpDataApi; + this.smpIdentifierApi = smpIdentifierApi; + this.signatureApi = signatureApi; + } + + public void generateResource(RequestData resourceData, ResponseData responseData, List<String> fields) throws ResourceException { + + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + + + try { + String identifierString = smpIdentifierApi.formatResourceIdentifier(identifier); + + ExampleEntityRo exampleEntityRo = new ExampleEntityRo(); + exampleEntityRo.setIdentifier(identifierString); + exampleEntityRo.setUrl("http://example.local/test"); + exampleEntityRo.setEmail("test.address@example.local"); + X509Certificate cert = createX509Certificate("CN="+identifierString+",O=edelivery,C=EU"); + exampleEntityRo.setCertificate(Base64.getEncoder().encodeToString(cert.getEncoded())); + + ObjectMapper mapper = new ObjectMapper(); + mapper.writerWithDefaultPrettyPrinter().writeValue(responseData.getOutputStream(), exampleEntityRo); + + } catch (IOException | CertificateEncodingException e) { + throw new ResourceException(PARSE_ERROR, "Can not marshal properties: [" + identifier + "]. Error: " + ExceptionUtils.getRootCauseMessage(e), e); + } + } + + public static X509Certificate createX509Certificate(String subject) throws ResourceException { + try { + KeyPair key = X509CertificateUtils.generateKeyPair(CertificateKeyType.RSA_2048); + return X509CertificateUtils.generateCertificate( + BigInteger.TEN, key.getPublic(), subject, OffsetDateTime.now().minusDays(1), + OffsetDateTime.now().plusYears(1), subject, + key.getPrivate(), false, -1, null, + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + } catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | IOException | + CertificateException | OperatorCreationException e) { + throw new ResourceException(INTERNAL_ERROR, "Error occurred at sample certificate generation!", e); + } + + } + + + @Override + public void readResource(RequestData resourceData, ResponseData responseData) throws ResourceException { + + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + if (resourceData.getResourceInputStream() == null) { + LOG.warn("Empty document input stream for service-group [{}]!", identifier); + return; + } + + InputStream inputStream = resourceData.getResourceInputStream(); + // reading resource multiple time make sure it can be rest + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + } + inputStream.mark(Integer.MAX_VALUE - 2); + + try { + inputStream.reset(); + } catch (IOException e) { + throw new ResourceException(PARSE_ERROR, "Can not reset input stream", e); + } + + try { + StreamUtils.copy(inputStream, responseData.getOutputStream()); + } catch (IOException e) { + throw new ResourceException(PROCESS_ERROR, "Error occurred while signing the cpp documen!: [" + + identifier + "]. Error: " + ExceptionUtils.getRootCauseMessage(e), e); + } + + } + + @Override + public void storeResource(RequestData resourceData, ResponseData responseData) throws ResourceException { + InputStream inputStream = resourceData.getResourceInputStream(); + // reading resource multiple time make sure it can be rest + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + } + inputStream.mark(Integer.MAX_VALUE - 2); + ExampleEntityRo properties = validateAndParse(resourceData); + try { + inputStream.reset(); + StreamUtils.copy(inputStream, responseData.getOutputStream()); + // need to save serviceGroup because of the update on the resource identifier values + //reader.serializeNative(cppDocument, responseData.getOutputStream(), true); + } catch (IOException e) { + throw new ResourceException(PARSE_ERROR, "Error occurred while copying the ServiceGroup", e); + } + } + + /** + * Method validates service group + * + * @param resourceData the resource data + */ + @Override + public void validateResource(RequestData resourceData) throws ResourceException { + validateAndParse(resourceData); + } + + public ExampleEntityRo validateAndParse(RequestData resourceData) throws ResourceException { + // get service group identifier + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + Properties properties = new Properties(); + // validate by schema + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + ExampleEntityRo entityRo; + try { + entityRo = mapper.readValue(resourceData.getResourceInputStream(), ExampleEntityRo.class); + } catch (IOException ex) { + throw new ResourceException(INVALID_RESOURCE, "Error occurred while reading example property document: [" + identifier + "] with error: " + ExceptionUtils.getRootCauseMessage(ex), ex); + } + + if ( StringUtils.isBlank(entityRo.getIdentifier())){ + throw new ResourceException(INVALID_RESOURCE, "Missing property [identifier]!" ); + } + + if ( StringUtils.isBlank(entityRo.getUrl())){ + throw new ResourceException(INVALID_RESOURCE, "Missing property [url]!" ); + } + + if ( StringUtils.isBlank(entityRo.getEmail())){ + throw new ResourceException(INVALID_RESOURCE, "Missing property [email]!" ); + } + + if ( StringUtils.isBlank(entityRo.getCertificate())){ + throw new ResourceException(INVALID_RESOURCE, "Missing property [certificate]" ); + } + + String identifierString = smpIdentifierApi.formatResourceIdentifier(identifier); + if (!StringUtils.equalsIgnoreCase(entityRo.getIdentifier(),identifierString )){ + throw new ResourceException(INVALID_RESOURCE, "Property: [identifier] does not match value for the resource ["+identifierString+"]" ); + } + + try { + new URL(entityRo.getUrl()); + } catch (MalformedURLException e) { + throw new ResourceException(INVALID_RESOURCE, "Bad property value: [url]!. Value ["+entityRo.getUrl()+"] is not URL" ); + } + + + return entityRo; + } + +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPPropertyHandlerExample.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPPropertyHandlerExample.java new file mode 100644 index 0000000000000000000000000000000000000000..284289707d673290749ead715f135d6a195334fa --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/DomiSMPPropertyHandlerExample.java @@ -0,0 +1,205 @@ +package eu.europa.ec.smp.spi.examples.handler; + +import eu.europa.ec.edelivery.security.utils.CertificateKeyType; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; +import eu.europa.ec.smp.spi.api.SmpDataServiceApi; +import eu.europa.ec.smp.spi.api.SmpIdentifierServiceApi; +import eu.europa.ec.smp.spi.api.SmpXmlSignatureApi; +import eu.europa.ec.smp.spi.api.model.RequestData; +import eu.europa.ec.smp.spi.api.model.ResourceIdentifier; +import eu.europa.ec.smp.spi.api.model.ResponseData; +import eu.europa.ec.smp.spi.exceptions.ResourceException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.bouncycastle.operator.OperatorCreationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.OffsetDateTime; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; + +@Component +public class DomiSMPPropertyHandlerExample extends AbstractHandler { + + + private static final Logger LOG = LoggerFactory.getLogger(DomiSMPPropertyHandlerExample.class); + + private static final String PROPERTY_IDENTIFIER = "domismp.extension.example.identifier"; + private static final String PROPERTY_URL = "domismp.extension.example.url"; + private static final String PROPERTY_EMAIL = "domismp.extension.example.email"; + private static final String PROPERTY_CERTIFICATE = "domismp.extension.example.certificate"; + + + final SmpDataServiceApi smpDataApi; + final SmpIdentifierServiceApi smpIdentifierApi; + + final SmpXmlSignatureApi signatureApi; + + + public DomiSMPPropertyHandlerExample(SmpDataServiceApi smpDataApi, + SmpIdentifierServiceApi smpIdentifierApi, + SmpXmlSignatureApi signatureApi) { + this.smpDataApi = smpDataApi; + this.smpIdentifierApi = smpIdentifierApi; + this.signatureApi = signatureApi; + } + + public void generateResource(RequestData resourceData, ResponseData responseData, List<String> fields) throws ResourceException { + + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + + + try { + String identifierString = smpIdentifierApi.formatResourceIdentifier(identifier); + Properties properties = new Properties(); + properties.setProperty(PROPERTY_IDENTIFIER, identifierString); + properties.setProperty(PROPERTY_URL, "http://example.local/test"); + + properties.setProperty(PROPERTY_EMAIL, "test.address@example.local"); + X509Certificate cert = createX509Certificate("CN="+identifierString+",O=edelivery,C=EU"); + properties.setProperty(PROPERTY_CERTIFICATE, Base64.getEncoder().encodeToString(cert.getEncoded())); + + properties.store(responseData.getOutputStream(), "DomiSMP property extension example"); + + } catch (IOException | CertificateEncodingException e) { + throw new ResourceException(PARSE_ERROR, "Can not marshal properties: [" + identifier + "]. Error: " + ExceptionUtils.getRootCauseMessage(e), e); + } + } + + public static X509Certificate createX509Certificate(String subject) throws ResourceException { + try { + KeyPair key = X509CertificateUtils.generateKeyPair(CertificateKeyType.RSA_2048); + return X509CertificateUtils.generateCertificate( + BigInteger.TEN, key.getPublic(), subject, OffsetDateTime.now().minusDays(1), + OffsetDateTime.now().plusYears(1), subject, + key.getPrivate(), false, -1, null, + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + } catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | IOException | + CertificateException | OperatorCreationException e) { + throw new ResourceException(INTERNAL_ERROR, "Error occurred at sample certificate generation!", e); + } + + } + + + @Override + public void readResource(RequestData resourceData, ResponseData responseData) throws ResourceException { + + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + if (resourceData.getResourceInputStream() == null) { + LOG.warn("Empty document input stream for service-group [{}]!", identifier); + return; + } + + InputStream inputStream = resourceData.getResourceInputStream(); + // reading resource multiple time make sure it can be rest + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + } + inputStream.mark(Integer.MAX_VALUE - 2); + + try { + inputStream.reset(); + } catch (IOException e) { + throw new ResourceException(PARSE_ERROR, "Can not reset input stream", e); + } + + try { + StreamUtils.copy(inputStream, responseData.getOutputStream()); + } catch (IOException e) { + throw new ResourceException(PROCESS_ERROR, "Error occurred while signing the cpp documen!: [" + + identifier + "]. Error: " + ExceptionUtils.getRootCauseMessage(e), e); + } + + } + + @Override + public void storeResource(RequestData resourceData, ResponseData responseData) throws ResourceException { + InputStream inputStream = resourceData.getResourceInputStream(); + // reading resource multiple time make sure it can be rest + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + } + inputStream.mark(Integer.MAX_VALUE - 2); + Properties properties = validateAndParse(resourceData); + try { + inputStream.reset(); + StreamUtils.copy(inputStream, responseData.getOutputStream()); + // need to save serviceGroup because of the update on the resource identifier values + //reader.serializeNative(cppDocument, responseData.getOutputStream(), true); + } catch (IOException e) { + throw new ResourceException(PARSE_ERROR, "Error occurred while copying the ServiceGroup", e); + } + } + + /** + * Method validates service group + * + * @param resourceData the resource data + */ + @Override + public void validateResource(RequestData resourceData) throws ResourceException { + validateAndParse(resourceData); + } + + public Properties validateAndParse(RequestData resourceData) throws ResourceException { + // get service group identifier + ResourceIdentifier identifier = getResourceIdentifier(resourceData); + Properties properties = new Properties(); + // validate by schema + + try { + properties.load(resourceData.getResourceInputStream()); + } catch (IOException ex) { + throw new ResourceException(INVALID_RESOURCE, "Error occurred while reading example property document: [" + identifier + "] with error: " + ExceptionUtils.getRootCauseMessage(ex), ex); + } + + if ( !properties.containsKey(PROPERTY_IDENTIFIER)){ + throw new ResourceException(INVALID_RESOURCE, "Missing property document: [" + PROPERTY_IDENTIFIER + "]" ); + } + if ( !properties.containsKey(PROPERTY_URL)){ + throw new ResourceException(INVALID_RESOURCE, "Missing property document: [" + PROPERTY_URL + "]" ); + } + if ( !properties.containsKey(PROPERTY_EMAIL)){ + throw new ResourceException(INVALID_RESOURCE, "Missing property document: [" + PROPERTY_EMAIL + "]" ); + } + if ( !properties.containsKey(PROPERTY_CERTIFICATE)){ + throw new ResourceException(INVALID_RESOURCE, "Missing property document: [" + PROPERTY_CERTIFICATE + "]" ); + } + String identifierString = smpIdentifierApi.formatResourceIdentifier(identifier); + if (!StringUtils.equalsIgnoreCase(properties.getProperty(PROPERTY_IDENTIFIER),identifierString )){ + throw new ResourceException(INVALID_RESOURCE, "Property: [" + PROPERTY_IDENTIFIER + "] does not match value for the resource ["+identifierString+"]" ); + } + + try { + new URL(properties.getProperty(PROPERTY_URL)); + } catch (MalformedURLException e) { + throw new ResourceException(INVALID_RESOURCE, "Bad property value: [" + PROPERTY_URL + "]!. Value ["+properties.getProperty(PROPERTY_URL)+"] is not URL" ); + } + + + return properties; + } + +} diff --git a/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/ExampleEntityRo.java b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/ExampleEntityRo.java new file mode 100644 index 0000000000000000000000000000000000000000..15e911be2f0305c535a450d3c6f3c776cf8708c2 --- /dev/null +++ b/smp-examples/resource-spi-example/src/main/java/eu/europa/ec/smp/spi/examples/handler/ExampleEntityRo.java @@ -0,0 +1,41 @@ +package eu.europa.ec.smp.spi.examples.handler; + +public class ExampleEntityRo { + + private String identifier; + private String url; + private String email; + private String certificate; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getCertificate() { + return certificate; + } + + public void setCertificate(String certificate) { + this.certificate = certificate; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java index 1f9a834d96ea8c404abd0e73a3d60b88d443e762..ac787cf2c160629f8126601da18e0e5ad2521590 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java @@ -274,13 +274,10 @@ public abstract class BaseDao<E extends BaseEntity> { searchValue, fieldName); } } - } } return lstPredicate; } - - public Path getPath(Root<E> om, String fieldName) { return getPath(om, fieldName, null); }