From bb066846c89ab2c7128ed43748a5b32ebfba9729 Mon Sep 17 00:00:00 2001
From: Joze RIHTARSIC <joze.rihtarsic@ext.ec.europa.eu>
Date: Wed, 18 Jul 2018 13:25:31 +0200
Subject: [PATCH] Add new exception InvalidOwner  was added for update
 servicegroup with invalid owner.

---
 .../smp/exceptions/InvalidOwnerException.java | 28 ++++++
 .../smp/services/ServiceGroupService.java     | 28 +++++-
 ...actServiceGroupServiceIntegrationTest.java |  4 +-
 ...ServiceMultipleDomainsIntegrationTest.java |  4 +-
 ...oupServiceSingleDomainIntegrationTest.java | 87 ++++++++++++++++++-
 .../ServiceMetadataIntegrationTest.java       |  2 +-
 .../controllers/ServiceGroupController.java   | 11 +--
 7 files changed, 147 insertions(+), 17 deletions(-)
 create mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/InvalidOwnerException.java

diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/InvalidOwnerException.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/InvalidOwnerException.java
new file mode 100644
index 000000000..403e68df5
--- /dev/null
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/InvalidOwnerException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package eu.europa.ec.edelivery.smp.exceptions;
+
+/**
+ * This exceptions is thrown if the provided user name does not exist.
+ */
+public class InvalidOwnerException extends RuntimeException {
+
+    public InvalidOwnerException(String username) {
+        this(username, null);
+    }
+    public InvalidOwnerException(String username, String message) {
+        super("Invalid owner '" + username + "'. " + message!=null?message:"" );
+    }
+
+}
diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java
index 6979334dd..a58f84611 100644
--- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java
+++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java
@@ -19,6 +19,7 @@ import eu.europa.ec.edelivery.smp.data.dao.DomainDao;
 import eu.europa.ec.edelivery.smp.data.dao.ServiceGroupDao;
 import eu.europa.ec.edelivery.smp.data.dao.UserDao;
 import eu.europa.ec.edelivery.smp.data.model.*;
+import eu.europa.ec.edelivery.smp.exceptions.InvalidOwnerException;
 import eu.europa.ec.edelivery.smp.exceptions.NotFoundException;
 import eu.europa.ec.edelivery.smp.exceptions.UnknownUserException;
 import eu.europa.ec.edelivery.smp.exceptions.WrongInputFieldException;
@@ -28,16 +29,20 @@ import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.UnsupportedEncodingException;
 import java.util.HashSet;
 import java.util.Optional;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 import static eu.europa.ec.edelivery.smp.conversion.ServiceGroupConverter.toDbModel;
 import static eu.europa.ec.smp.api.Identifiers.asString;
 import static java.lang.String.format;
+import static java.net.URLDecoder.decode;
 import static java.util.Arrays.asList;
 import static org.apache.commons.lang3.StringUtils.isNotBlank;
 
@@ -48,6 +53,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
 public class ServiceGroupService {
 
     private static final Pattern DOMAIN_ID_PATTERN = Pattern.compile("[a-zA-Z0-9]{1,50}");
+    private static final String UTF_8 = "UTF-8";
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceGroupService.class);
 
     @Autowired
     private CaseSensitivityNormalizer caseSensitivityNormalizer;
@@ -76,9 +84,16 @@ public class ServiceGroupService {
     }
 
     @Transactional
-    public boolean saveServiceGroup(ServiceGroup serviceGroup, String domain, String newOwnerName) {
+    public boolean saveServiceGroup(ServiceGroup serviceGroup, String domain, String serviceGroupOwner, String authenticatedUser) {
         ServiceGroup normalizedServiceGroup = normalizeIdentifierCaseSensitivity(serviceGroup);
         ParticipantIdentifierType normalizedParticipantId = normalizedServiceGroup.getParticipantIdentifier();
+        String newOwnerName;
+        try {
+            newOwnerName = isNotBlank(serviceGroupOwner) ? decode(serviceGroupOwner, UTF_8) : authenticatedUser;
+        } catch (UnsupportedEncodingException | IllegalArgumentException ex) {
+            throw new InvalidOwnerException(serviceGroupOwner, "Unsupported or invalid encoding: " + ex.getMessage());
+        }
+
 
         DBUser newOwner = userDao.find(newOwnerName);
         if (newOwner == null) {
@@ -87,10 +102,21 @@ public class ServiceGroupService {
 
         DBServiceGroup dbServiceGroup = serviceGroupDao.find(toDbModel(normalizedParticipantId));
 
+
         validateDomain(dbServiceGroup, domain);
         String extensions = ServiceGroupConverter.extractExtensionsPayload(normalizedServiceGroup);
 
         if (dbServiceGroup != null) {
+            // test service owner
+            Set<DBOwnership> owSet =  dbServiceGroup.getOwnerships();
+            Optional<DBOwnership> owner =  owSet.stream().filter(dbOwnership -> dbOwnership.getUser().getUsername().equals(newOwnerName)).findFirst();
+            // test serviceGroupOwner but use newOwnerName - because it is decoded
+            if (serviceGroupOwner!=null && !owner.isPresent()){
+                String msg = "User: " +newOwnerName+ " is not owner of service group: " +dbServiceGroup.getId().getBusinessIdentifierScheme() + "::" + dbServiceGroup.getId().getBusinessIdentifier();
+                LOG.error(msg);
+                throw new InvalidOwnerException(serviceGroupOwner, msg);
+            }
+
             dbServiceGroup.setExtension(extensions);
             serviceGroupDao.persistFlushDetach(dbServiceGroup);
             return false;
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/AbstractServiceGroupServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/AbstractServiceGroupServiceIntegrationTest.java
index 8421429fc..c0f666543 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/AbstractServiceGroupServiceIntegrationTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/AbstractServiceGroupServiceIntegrationTest.java
@@ -46,6 +46,8 @@ abstract class AbstractServiceGroupServiceIntegrationTest {
     protected static final String SERVICE_GROUP_XML_PATH = "/eu/europa/ec/edelivery/smp/services/ServiceGroupPoland.xml";
     protected static final ParticipantIdentifierType SERVICE_GROUP_ID = asParticipantId("participant-scheme-qns::urn:poland:ncpb");
     public static final String ADMIN_USERNAME = "test_admin";
+    public static final String CERT_USER="CN=comon name,O=org,C=BE:0000000000000066";
+    public static final String CERT_USER_ENCODED="CN%3Dcomon%20name%2CO%3Dorg%2CC%3DBE%3A0000000000000066";
 
     @PersistenceContext
     protected EntityManager em;
@@ -61,7 +63,7 @@ abstract class AbstractServiceGroupServiceIntegrationTest {
 
     protected ServiceGroup saveServiceGroup() throws IOException {
         ServiceGroup inServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
-        serviceGroupService.saveServiceGroup(inServiceGroup, null, ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(inServiceGroup, null, ADMIN_USERNAME, ADMIN_USERNAME);
         return inServiceGroup;
     }
 }
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceMultipleDomainsIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceMultipleDomainsIntegrationTest.java
index 3a7bbf209..7f44fadcb 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceMultipleDomainsIntegrationTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceMultipleDomainsIntegrationTest.java
@@ -50,7 +50,7 @@ public class ServiceGroupServiceMultipleDomainsIntegrationTest extends AbstractS
     public void saveAndReadPositiveScenarioForMultipleDomain() throws IOException {
         // given
         ServiceGroup inServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
-        serviceGroupService.saveServiceGroup(inServiceGroup, SECOND_DOMAIN_ID, ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(inServiceGroup, SECOND_DOMAIN_ID, ADMIN_USERNAME, ADMIN_USERNAME);
 
         // when
         DBServiceGroup dbServiceGroup = serviceGroupDao.find(toDbModel(SERVICE_GROUP_ID));
@@ -71,7 +71,7 @@ public class ServiceGroupServiceMultipleDomainsIntegrationTest extends AbstractS
         ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
 
         //when-then
-        serviceGroupService.saveServiceGroup(newServiceGroup, SECOND_DOMAIN_ID, ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(newServiceGroup, SECOND_DOMAIN_ID, ADMIN_USERNAME, ADMIN_USERNAME);
     }
 
 
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceSingleDomainIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceSingleDomainIntegrationTest.java
index a265bce63..57f2c29bf 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceSingleDomainIntegrationTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceSingleDomainIntegrationTest.java
@@ -16,9 +16,14 @@ package eu.europa.ec.edelivery.smp.services;
 import eu.europa.ec.edelivery.smp.data.model.DBOwnership;
 import eu.europa.ec.edelivery.smp.data.model.DBOwnershipId;
 import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup;
+import eu.europa.ec.edelivery.smp.exceptions.InvalidOwnerException;
 import eu.europa.ec.edelivery.smp.exceptions.NotFoundException;
+import eu.europa.ec.edelivery.smp.exceptions.UnknownUserException;
 import eu.europa.ec.edelivery.smp.exceptions.WrongInputFieldException;
+import org.hamcrest.core.StringStartsWith;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.oasis_open.docs.bdxr.ns.smp._2016._05.ExtensionType;
 import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceGroup;
 import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadataReferenceType;
@@ -27,6 +32,7 @@ import org.springframework.test.context.jdbc.Sql;
 import javax.xml.bind.JAXBException;
 import java.io.IOException;
 import java.util.List;
+import java.util.regex.Matcher;
 
 import static eu.europa.ec.edelivery.smp.conversion.ServiceGroupConverter.toDbModel;
 import static eu.europa.ec.edelivery.smp.conversion.ServiceGroupConverter.unmarshal;
@@ -41,6 +47,13 @@ import static org.junit.Assert.*;
 @Sql("classpath:/service_integration_test_data.sql")
 public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServiceGroupServiceIntegrationTest {
 
+    private static String UnknownUser="UnknownUser";
+
+
+
+    @Rule
+    public ExpectedException expectedExeption = ExpectedException.none();
+
     @Test
     public void makeSureServiceGroupDoesNotExistAlready(){
         DBServiceGroup dbServiceGroup = serviceGroupDao.find(toDbModel(SERVICE_GROUP_ID));
@@ -99,7 +112,7 @@ public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServ
         newServiceGroup.getExtensions().add(newExtension);
 
         //when
-        serviceGroupService.saveServiceGroup(newServiceGroup, null, ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(newServiceGroup, null, ADMIN_USERNAME, ADMIN_USERNAME);
         ServiceGroup resultServiceGroup = serviceGroupService.getServiceGroup(SERVICE_GROUP_ID);
 
         //then
@@ -107,6 +120,72 @@ public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServ
         assertEquals(marshall(newServiceGroup), marshall(resultServiceGroup));
     }
 
+    @Test
+    public void updateUnknownUserException() throws IOException, JAXBException {
+
+        String invalidServiceUser = "WrongOwner";
+        //given
+        ServiceGroup oldServiceGroup = saveServiceGroup();
+        expectedExeption.expect(UnknownUserException.class);
+        expectedExeption.expectMessage("Unknown user '"+invalidServiceUser+"'");
+
+        ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
+        ExtensionType newExtension = new ExtensionType();
+        newExtension.setExtensionID("new extension ID the second");
+        newServiceGroup.getExtensions().add(newExtension);
+
+        //when
+        serviceGroupService.saveServiceGroup(newServiceGroup, null, invalidServiceUser, ADMIN_USERNAME);
+    }
+
+    @Test
+    public void updateInvalidUserException() throws IOException, JAXBException {
+
+        //given
+        ServiceGroup oldServiceGroup = saveServiceGroup();
+        expectedExeption.expect(InvalidOwnerException.class);
+        expectedExeption.expectMessage("User: "+CERT_USER+" is not owner of service group: participant-scheme-qns::urn:poland:ncpb");
+
+        ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
+
+        //when
+        serviceGroupService.saveServiceGroup(newServiceGroup, null, CERT_USER, ADMIN_USERNAME);
+
+    }
+
+    @Test
+    public void updateEncodedInvalidUserException() throws IOException, JAXBException {
+
+        //given
+        ServiceGroup oldServiceGroup = saveServiceGroup();
+        expectedExeption.expect(InvalidOwnerException.class);
+        expectedExeption.expectMessage("User: "+CERT_USER+" is not owner of service group: participant-scheme-qns::urn:poland:ncpb");
+
+        ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
+
+        //when
+        serviceGroupService.saveServiceGroup(newServiceGroup, null, CERT_USER_ENCODED, ADMIN_USERNAME);
+
+    }
+
+    @Test
+    public void updateInvalidUserEncodingException() throws IOException, JAXBException {
+        String  username = "test::20%atest";
+        //given
+        ServiceGroup oldServiceGroup = saveServiceGroup();
+        expectedExeption.expect(InvalidOwnerException.class);
+        expectedExeption.expectMessage(StringStartsWith.startsWith("Unsupported or invalid encoding"));
+
+        ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
+        ExtensionType newExtension = new ExtensionType();
+        newExtension.setExtensionID("new extension ID the second");
+        newServiceGroup.getExtensions().add(newExtension);
+
+        //when
+        serviceGroupService.saveServiceGroup(newServiceGroup, null, username, ADMIN_USERNAME);
+
+    }
+
     @Test
     public void urlsAreHandledByWebLayer() throws Throwable {
         //given
@@ -128,7 +207,7 @@ public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServ
         ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
 
         //when-then
-        serviceGroupService.saveServiceGroup(newServiceGroup,"NOTEXISTINGDOMAIN", ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(newServiceGroup,"NOTEXISTINGDOMAIN", ADMIN_USERNAME, ADMIN_USERNAME);
     }
 
     @Test(expected = WrongInputFieldException.class)
@@ -137,7 +216,7 @@ public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServ
         ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
 
         //when-then
-        serviceGroupService.saveServiceGroup(newServiceGroup,"notAllowedChars:-_;#$", ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(newServiceGroup,"notAllowedChars:-_;#$", ADMIN_USERNAME, ADMIN_USERNAME);
     }
 
     @Test
@@ -146,7 +225,7 @@ public class ServiceGroupServiceSingleDomainIntegrationTest extends AbstractServ
         ServiceGroup newServiceGroup = unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
 
         //when
-        serviceGroupService.saveServiceGroup(newServiceGroup,"domain1", ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(newServiceGroup,"domain1", ADMIN_USERNAME, ADMIN_USERNAME);
 
         //then
         assertNotNull(serviceGroupService.getServiceGroup(SERVICE_GROUP_ID));
diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceMetadataIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceMetadataIntegrationTest.java
index 53620286b..ffbc90fd7 100644
--- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceMetadataIntegrationTest.java
+++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceMetadataIntegrationTest.java
@@ -79,7 +79,7 @@ public class ServiceMetadataIntegrationTest {
     @Before
     public void before() throws IOException {
         ServiceGroup inServiceGroup = ServiceGroupConverter.unmarshal(loadDocumentAsString(SERVICE_GROUP_XML_PATH));
-        serviceGroupService.saveServiceGroup(inServiceGroup, null, ADMIN_USERNAME);
+        serviceGroupService.saveServiceGroup(inServiceGroup, null, ADMIN_USERNAME, ADMIN_USERNAME);
     }
 
     @Test
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java
index 0bb2c8e93..0a0e9b52a 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java
@@ -37,8 +37,6 @@ import java.io.UnsupportedEncodingException;
 import java.util.List;
 
 import static eu.europa.ec.smp.api.Identifiers.asParticipantId;
-import static java.net.URLDecoder.decode;
-import static org.apache.commons.lang3.StringUtils.isNotBlank;
 import static org.springframework.http.ResponseEntity.created;
 import static org.springframework.http.ResponseEntity.ok;
 
@@ -53,8 +51,6 @@ public class ServiceGroupController {
 
     private static final Logger log = LoggerFactory.getLogger(ServiceGroupController.class);
 
-    private static final String UTF_8 = "UTF-8";
-
     @Autowired
     private ServiceGroupValidator serviceGroupValidator;
 
@@ -86,9 +82,9 @@ public class ServiceGroupController {
             @PathVariable String serviceGroupId,
             @RequestHeader(name = "ServiceGroup-Owner", required = false) String serviceGroupOwner,
             @RequestHeader(name = "Domain", required = false) String domain,
-            @RequestBody String body) throws XmlInvalidAgainstSchemaException, UnsupportedEncodingException {
+            @RequestBody String body) throws XmlInvalidAgainstSchemaException {
 
-        log.info("PUT ServiceGroup: {}\n{}", serviceGroupId, body);
+        log.info("PUT ServiceGroup: {} domain {} owner {} \n{}", serviceGroupId,domain, serviceGroupOwner, body);
 
         // Validations
         BdxSmpOasisValidator.validateXSD(body);
@@ -96,8 +92,7 @@ public class ServiceGroupController {
         serviceGroupValidator.validate(serviceGroupId, serviceGroup);
 
         // Service action
-        String newOwnerName = isNotBlank(serviceGroupOwner) ? decode(serviceGroupOwner, UTF_8) : SecurityContextHolder.getContext().getAuthentication().getName();
-        boolean newServiceGroupCreated = serviceGroupService.saveServiceGroup(serviceGroup, domain, newOwnerName);
+         boolean newServiceGroupCreated = serviceGroupService.saveServiceGroup(serviceGroup, domain, serviceGroupOwner, SecurityContextHolder.getContext().getAuthentication().getName());
 
         log.info("Finished PUT ServiceGroup: {}", serviceGroupId);
 
-- 
GitLab