Work on fixing the docker image push 96/2996/2
authorSerban Jora <sj2381@att.com>
Tue, 2 Oct 2018 17:17:35 +0000 (13:17 -0400)
committerChris Lott <clott@research.att.com>
Tue, 2 Oct 2018 20:29:48 +0000 (20:29 +0000)
Change-Id: I77d62cdd471cc37246628a75249e4e18b50a0dbe
Issue-ID: ACUMOS-1781
Signed-off-by: Serban Jora <sj2381@att.com>
15 files changed:
docs/release-notes.rst
gateway/pom.xml
gateway/src/main/java/org/acumos/federation/gateway/adapter/PeerGateway.java
gateway/src/main/java/org/acumos/federation/gateway/config/AdapterConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/config/GatewayConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/config/InterfaceConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/controller/CatalogController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PingController.java
gateway/src/main/java/org/acumos/federation/gateway/security/AuthenticationConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityConfiguration.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionHandler.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionRoot.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/security/Peer.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/ContentServiceImpl.java
gateway/src/main/resources/logback.xml

index ecbdbe2..c3eb8ca 100644 (file)
@@ -22,6 +22,16 @@ Federated Gateway Release Notes
 
 The Federated Gateway server is available as a Docker image in a Docker registry.
 
+--------------------------
+Version 1.18.5, 2018-10-02
+--------------------------
+
+* Fix for loss of file name prefix/suffix (`ACUMOS-1780 <https://jira.acumos.org/browse/ACUMOS-1780>`_)
+* Fix for processing of docker artifacts, push to the local registry (`ACUMOS-1781 <https://jira.acumos.org/browse/ACUMOS-1781>`_)
+* Add peer 'isActive' as controller calls pre-authorization check
+* Fix the artifact content processing condition in the gateway
+
+--------------------------
 Version 1.18.4, 2018-09-21
 --------------------------
 
index c9f2da0..be83485 100644 (file)
@@ -24,7 +24,7 @@ limitations under the License.
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.acumos.federation</groupId>
        <artifactId>gateway</artifactId>
-       <version>1.18.4-SNAPSHOT</version>
+       <version>1.18.5-SNAPSHOT</version>
        <name>Federation Gateway</name>
        <description>Federated Acumos Interface for inter-acumos and ONAP communication</description>
 
@@ -69,7 +69,7 @@ limitations under the License.
                <dependency>
                        <groupId>org.acumos.common-dataservice</groupId>
                        <artifactId>cmn-data-svc-client</artifactId>
-                       <version>1.18.1</version>
+                       <version>1.18.2</version>
                </dependency>
                <dependency>
                        <groupId>org.json</groupId>
@@ -168,6 +168,11 @@ limitations under the License.
                        <artifactId>commons-lang3</artifactId>
                        <version>3.6</version>
                </dependency>
+               <dependency>
+       <groupId>org.apache.commons</groupId>
+       <artifactId>commons-compress</artifactId>
+       <version>1.5</version>
+               </dependency>
                <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
                <dependency>
                        <groupId>commons-beanutils</groupId>
index 7f8ece5..1d3cc8b 100644 (file)
@@ -169,7 +169,8 @@ public class PeerGateway {
                //this should go away once the move to service interface based operations is complete
                //as ugly as they come
                private ICommonDataServiceRestClient getCDSClient(ServiceContext theContext) {
-                       return (ICommonDataServiceRestClient)theContext.getAttribute(AbstractServiceImpl.Attributes.cdsClient);
+                       return PeerGateway.this.clients.getCDSClient();
+                       //return (ICommonDataServiceRestClient)theContext.getAttribute(AbstractServiceImpl.Attributes.cdsClient);
                }
 
                private MLPArtifact createMLPArtifact(String theSolutionId, String theRevisionId, MLPArtifact peerArtifact,
@@ -340,6 +341,8 @@ public class PeerGateway {
                                for (Map.Entry<MLPArtifact, MLPArtifact> artifactEntry : peerToLocalArtifacts.entrySet()) {
                                        MLPArtifact peerArtifact = artifactEntry.getKey(), localArtifact = artifactEntry.getValue();
                                        boolean doUpdate = false;
+                                       boolean doContent = (peerArtifact.getUri() != null) &&
+                                                                                                                       (SubscriptionScope.Full == SubscriptionScope.forCode(this.sub.getScopeType()));
 
                                        if (localArtifact == null) {
                                                localArtifact = createMLPArtifact(localSolution.getSolutionId(), localRevision.getRevisionId(),
@@ -351,15 +354,17 @@ public class PeerGateway {
                                                        localArtifact = copyMLPArtifact(peerArtifact, localArtifact);
                                                        doUpdate = true;
                                                }
+                                               else {
+                                                       //if no changes, do not go after the content
+                                                       doContent = false;
+                                               }
                                        }
 
-                                       boolean doContent = (peerArtifact.getUri() != null) &&
-                                                                                                                       (SubscriptionScope.Full == SubscriptionScope.forCode(this.sub.getScopeType()));
                                        if (doContent) {
                                                log.info(EELFLoggerDelegate.debugLogger, "Processing content for artifact {}", peerArtifact); 
                                                // TODO: we are trying to access the artifact by its identifier which
                                                // is fine in the common case but the uri specified in the artifact
-                                               // data is a more flexible approach.
+                                               // data is the right approach (as it does not rely on the E5 definition).
                                                Resource artifactContent = null;
                                                try {
                                                        artifactContent = fedClient.getArtifactContent(
@@ -414,6 +419,8 @@ public class PeerGateway {
                                        MLPDocument peerDocument = documentEntry.getKey(),
                                                                                        localDocument = documentEntry.getValue();
                                        boolean doUpdate = false;
+                                       boolean doContent = (peerDocument.getUri() != null) &&
+                                                                                                                       (SubscriptionScope.Full == SubscriptionScope.forCode(this.sub.getScopeType()));
 
                                        if (localDocument == null) {
                                                localDocument = createMLPDocument(localSolution.getSolutionId(), localRevision.getRevisionId(),
@@ -427,10 +434,12 @@ public class PeerGateway {
                                                        localDocument = copyMLPDocument(peerDocument, localDocument);
                                                        doUpdate = true;
                                                }
+                                               else {
+                                                       //if no changes, do not go after the content
+                                                       doContent = false;
+                                               }
                                        }
 
-                                       boolean doContent = (peerDocument.getUri() != null) &&
-                                                                                                                       (SubscriptionScope.Full == SubscriptionScope.forCode(this.sub.getScopeType()));
                                        if (doContent) {
                                                log.info(EELFLoggerDelegate.debugLogger, "Processing content for document {}", peerDocument); 
                                                // TODO: we are trying to access the document by its identifier which
index 1d7c912..614b2d2 100644 (file)
@@ -22,6 +22,7 @@ package org.acumos.federation.gateway.config;
 
 import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.security.AuthenticationConfiguration;
+import org.acumos.federation.gateway.security.FederationMethodSecurityConfiguration;
 import org.acumos.federation.gateway.service.CatalogService;
 import org.acumos.federation.gateway.service.ContentService;
 import org.acumos.federation.gateway.service.LocalWatchService;
@@ -45,7 +46,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
  */
 @Configuration
 @Import({TaskConfiguration.class,
-                                AuthenticationConfiguration.class})
+                                AuthenticationConfiguration.class,
+                                FederationMethodSecurityConfiguration.class})
 @EnableConfigurationProperties({FederationInterfaceConfiguration.class,
                                                                                                                                LocalInterfaceConfiguration.class,
                                                                                                                                CDMSClientConfiguration.class,
index d30c385..edb5edc 100644 (file)
@@ -23,6 +23,7 @@ package org.acumos.federation.gateway.config;
 import org.acumos.federation.gateway.adapter.PeerGateway;
 import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.security.AuthenticationConfiguration;
+import org.acumos.federation.gateway.security.FederationMethodSecurityConfiguration;
 import org.acumos.federation.gateway.service.CatalogService;
 import org.acumos.federation.gateway.service.CatalogServiceConfiguration;
 import org.acumos.federation.gateway.service.ContentService;
@@ -50,7 +51,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
  */
 @Configuration
 @Import({TaskConfiguration.class,
-                                AuthenticationConfiguration.class})
+                                AuthenticationConfiguration.class,
+                                FederationMethodSecurityConfiguration.class})
 @EnableConfigurationProperties({FederationInterfaceConfiguration.class,
                                                                                                                                LocalInterfaceConfiguration.class,
                                                                                                                                CDMSClientConfiguration.class,
index 16b4b5e..108654c 100644 (file)
@@ -70,9 +70,6 @@ import org.springframework.stereotype.Component;
 @Component
 public class InterfaceConfiguration {
 
-       @Autowired
-       private ResourceLoader resourceLoader;
-
        private final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(getClass().getName());
 
        private String                  address;
@@ -82,7 +79,7 @@ public class InterfaceConfiguration {
        private Server  server; 
 
        public InterfaceConfiguration() {
-               log.info(EELFLoggerDelegate.debugLogger, this + "::new");
+               log.trace(EELFLoggerDelegate.debugLogger, this + "::new");
        }
 
 /*
@@ -96,7 +93,7 @@ public class InterfaceConfiguration {
 */
        @PostConstruct
        public void initInterface() {
-               log.info(EELFLoggerDelegate.debugLogger, this + "::init");
+               log.trace(EELFLoggerDelegate.debugLogger, this + "::init");
        }       
 
        public String getAddress() {
@@ -157,7 +154,7 @@ public class InterfaceConfiguration {
                if (!hasSSL())
                        return null;
        
-               KeyStore keyStore = loadKeyStore();
+               KeyStore keyStore = this.ssl.loadKeyStore();
                X509Certificate certEntry = null;
                
                String alias = null;
@@ -200,60 +197,7 @@ public class InterfaceConfiguration {
                        .toString();
        }
 
-       /**
-        * Loads the specified key store
-        * @return the key store
-        */
-       public KeyStore loadKeyStore() {
-               return loadStore(this.ssl.keyStore, this.ssl.keyStoreType, this.ssl.keyStorePasswd);
-       }
 
-       /**
-        * Loads the specified trust store
-        * @return the key store
-        */
-       public KeyStore loadTrustStore() {
-               return loadStore(this.ssl.trustStore, this.ssl.trustStoreType, this.ssl.trustStorePasswd);
-       }
-
-       /** */
-       private KeyStore loadStore(String theLocation, String theType, String thePasswd) {
-               KeyStore store = null;
-               InputStream storeSource = null;
-               try {
-                       storeSource = this.resourceLoader.getResource(theLocation).getURL().openStream();
-               }
-               catch (FileNotFoundException rnfx) {
-                       try {
-                               storeSource = new FileInputStream(theLocation);
-                       }
-                       catch (FileNotFoundException fnfx) {
-                               throw new IllegalStateException("Failed to find key store " + theLocation);
-                       }
-               }
-               catch (IOException iox) {
-                       throw new IllegalStateException("Error loading key material: " + iox, iox);
-               }
-
-               try {
-                       store = KeyStore.getInstance(theType);
-                       store.load(storeSource, thePasswd.toCharArray());
-                       log.info(EELFLoggerDelegate.debugLogger, "Loaded key store: " + theLocation);
-               }
-               catch (Exception x) {
-                       throw new IllegalStateException("Error loading key material: " + x, x);
-               }
-               finally {
-                       try {
-                               storeSource.close();
-                       }
-                       catch (IOException iox) {
-                               log.debug(EELFLoggerDelegate.debugLogger, "Error closing key store source", iox);
-                       }
-               }
-               return store;
-       }
-       
        /**
         */
        public static class Client {
@@ -328,21 +272,30 @@ public class InterfaceConfiguration {
         */
        public static class SSL {
 
-               private String keyStore;
+               private final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(getClass().getName());
+
+               @Autowired
+               private ResourceLoader resourceLoader;
+
+               private KeyStore        keyStore,
+                                                                                       trustStore;
+
+               private String keyStoreLocation;
                private String keyStoreType = "JKS";
                private String keyStorePasswd;
                private String keyAlias;
-               private String trustStore;
+               private String trustStoreLocation;
                private String trustStoreType = "JKS";
                private String trustStorePasswd;
                private String clientAuth = "need";
 
                public String getKeyStore() {
-                       return this.keyStore;
+                       return this.keyStoreLocation;
                }
 
-               public void setKeyStore(String theKeyStore) {
-                       this.keyStore = theKeyStore;
+               public void setKeyStore(String theKeyStoreLocation) {
+                       this.keyStoreLocation = theKeyStoreLocation;
+                       this.keyStore = null;
                }
 
                public String getKeyStoreType() {
@@ -351,6 +304,7 @@ public class InterfaceConfiguration {
 
                public void setKeyStoreType(String theKeyStoreType) {
                        this.keyStoreType = theKeyStoreType;
+                       this.keyStore = null;
                }
 
                public String getKeyStorePassword() {
@@ -359,6 +313,7 @@ public class InterfaceConfiguration {
 
                public void setKeyStorePassword(String theKeyStorePassword) {
                        this.keyStorePasswd = theKeyStorePassword;
+                       this.keyStore = null;
                }
 
                public String getKeyAlias() {
@@ -370,11 +325,12 @@ public class InterfaceConfiguration {
                }
 
                public String getTrustStore() {
-                       return this.trustStore;
+                       return this.trustStoreLocation;
                }
 
-               public void setTrustStore(String theTrustStore) {
-                       this.trustStore = theTrustStore;
+               public void setTrustStore(String theTrustStoreLocation) {
+                       this.trustStoreLocation = theTrustStoreLocation;
+                       this.trustStore = null;
                }
 
                public String getTrustStoreType() {
@@ -383,6 +339,7 @@ public class InterfaceConfiguration {
 
                public void setTrustStoreType(String theTrustStoreType) {
                        this.trustStoreType = theTrustStoreType;
+                       this.trustStore = null;
                }
 
                public String getTrustStorePassword() {
@@ -391,14 +348,15 @@ public class InterfaceConfiguration {
 
                public void setTrustStorePassword(String theTrustStorePassword) {
                        this.trustStorePasswd = theTrustStorePassword;
+                       this.trustStore = null;
                }
 
                protected boolean hasKeyStoreInfo() {
-                       return this.keyStore != null && this.keyStoreType != null && this.keyStorePasswd != null;
+                       return this.keyStoreLocation != null && this.keyStoreType != null && this.keyStorePasswd != null;
                }
 
                protected boolean hasTrustStoreInfo() {
-                       return this.trustStore != null && this.trustStoreType != null /*
+                       return this.trustStoreLocation != null && this.trustStoreType != null /*
                                                                                                                                                         * && this.trustStorePasswd != null
                                                                                                                                                         */;
                }
@@ -412,10 +370,73 @@ public class InterfaceConfiguration {
                }
 
                public String toString() {
-                       return new StringBuilder("").append("SSL(").append(this.keyStore).append(",").append(this.keyStoreType)
-                                       .append(",").append(this.keyAlias).append("/").append(this.trustStore).append(",")
+                       return new StringBuilder("").append("SSL(").append(this.keyStoreLocation).append(",").append(this.keyStoreType)
+                                       .append(",").append(this.keyAlias).append("/").append(this.trustStoreLocation).append(",")
                                        .append(this.trustStoreType).append(")").toString();
                }
+
+               /**
+                * Loads the specified key store
+                * @return the key store
+                */
+               public KeyStore loadKeyStore() {
+                       return this.keyStore == null ?
+                                                       this.keyStore = loadStore(this.keyStoreLocation, this.keyStoreType, this.keyStorePasswd) :
+                                                       this.keyStore;
+               }
+
+               /**
+                * Loads the specified trust store
+                * @return the key store
+                */
+               public KeyStore loadTrustStore() {
+                       return this.trustStore == null ? 
+                                                       this.trustStore = loadStore(this.trustStoreLocation, this.trustStoreType, this.trustStorePasswd) :
+                                                       this.trustStore;
+               }
+
+               /** */
+               private KeyStore loadStore(String theLocation, String theType, String thePasswd) {
+                       KeyStore store = null;
+                       InputStream storeSource = null;
+
+                       if (this.resourceLoader == null)
+                               this.resourceLoader = new DefaultResourceLoader();
+
+                       try {
+                               storeSource = this.resourceLoader.getResource(theLocation).getURL().openStream();
+                       }
+                       catch (FileNotFoundException rnfx) {
+                               try {
+                                       storeSource = new FileInputStream(theLocation);
+                               }
+                               catch (FileNotFoundException fnfx) {
+                                       throw new IllegalStateException("Failed to find key store " + theLocation);
+                               }
+                       }
+                       catch (IOException iox) {
+                               throw new IllegalStateException("Error loading key material: " + iox, iox);
+                       }
+
+                       try {
+                               store = KeyStore.getInstance(theType);
+                               store.load(storeSource, thePasswd.toCharArray());
+                               log.trace(EELFLoggerDelegate.debugLogger, "Loaded key store: " + theLocation);
+                       }
+                       catch (Exception x) {
+                               throw new IllegalStateException("Error loading key material: " + x, x);
+                       }
+                       finally {
+                               try {
+                                       storeSource.close();
+                               }
+                               catch (IOException iox) {
+                                       log.warn(EELFLoggerDelegate.debugLogger, "Error closing key store source", iox);
+                               }
+                       }
+                       return store;
+               }
+       
        }
 
        /**
@@ -484,17 +505,14 @@ public class InterfaceConfiguration {
        public HttpClient buildClient() {
 
                SSLContext sslContext = null;
-               log.info(EELFLoggerDelegate.debugLogger, "Build HttpClient with " + this);
-
-               if (this.resourceLoader == null)
-                       this.resourceLoader = new DefaultResourceLoader();
+               log.trace(EELFLoggerDelegate.debugLogger, "Build HttpClient with " + this);
 
                if (this.ssl == null) {
                        log.trace(EELFLoggerDelegate.debugLogger, "No ssl config was provided");
                }
                else {
-                       KeyStore keyStore = loadKeyStore(),
-                                                        trustStore = loadTrustStore();
+                       KeyStore keyStore = this.ssl.loadKeyStore(),
+                                                        trustStore = this.ssl.loadTrustStore();
 
                        SSLContextBuilder contextBuilder = SSLContexts.custom();
                        try {
index c35bc84..997cdd6 100644 (file)
@@ -35,6 +35,7 @@ import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
 import org.acumos.federation.gateway.cds.ArtifactType;
+import org.acumos.federation.gateway.cds.SolutionRevision;
 import org.acumos.federation.gateway.common.API;
 import org.acumos.federation.gateway.common.JsonResponse;
 import org.acumos.federation.gateway.config.EELFLoggerDelegate;
@@ -82,7 +83,7 @@ public class CatalogController extends AbstractController {
         */
        @CrossOrigin
        // @PreAuthorize("hasAuthority('PEER')"
-       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).CATALOG_ACCESS)")
+       @PreAuthorize("isActive && hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).CATALOG_ACCESS)")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of Published Solutions from the Catalog of the local Acumos Instance .", response = MLPSolution.class, responseContainer = "List")
        @RequestMapping(value = { API.Paths.SOLUTIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
@@ -124,7 +125,7 @@ public class CatalogController extends AbstractController {
        }
 
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list detailed solution information from the Catalog of the local Acumos Instance .", response = MLPSolution.class)
        @RequestMapping(value = { API.Paths.SOLUTION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
@@ -170,7 +171,7 @@ public class CatalogController extends AbstractController {
         * @return List of Published ML Solutions in JSON format.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of Solution Revision from the Catalog of the local Acumos Instance .", response = MLPSolutionRevision.class, responseContainer = "List")
        @RequestMapping(value = { API.Paths.SOLUTION_REVISIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
@@ -218,20 +219,21 @@ public class CatalogController extends AbstractController {
         * @return List of Published ML Solutions in JSON format.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by peer Acumos to get solution revision details from the local Acumos Instance .", response = MLPSolutionRevision.class)
        @RequestMapping(value = {
                        API.Paths.SOLUTION_REVISION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
-       public JsonResponse<MLPSolutionRevision> getSolutionRevisionDetails(HttpServletResponse theHttpResponse,
+       public JsonResponse<MLPSolutionRevision> getSolutionRevisionDetails(
+                       HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse,
                        @PathVariable("solutionId") String theSolutionId, @PathVariable("revisionId") String theRevisionId) {
+               ControllerContext context = new ControllerContext();
                JsonResponse<MLPSolutionRevision> response = null;
-               MLPSolutionRevision solutionRevision = null;
+               SolutionRevision solutionRevision = null;
                log.debug(EELFLoggerDelegate.debugLogger,
                                API.Paths.SOLUTION_REVISION_DETAILS + "(" + theSolutionId + "," + theRevisionId + ")");
                try {
-                       solutionRevision = catalogService.getSolutionRevision(theSolutionId, theRevisionId,
-                                       new ControllerContext());
+                       solutionRevision = catalogService.getSolutionRevision(theSolutionId, theRevisionId, context);
                        if (null == solutionRevision) {
                                response = JsonResponse.<MLPSolutionRevision> buildResponse()
                                                                                                                                .withMessage("No solution revision " + theSolutionId + "/" + theRevisionId + " is available.")
@@ -239,6 +241,17 @@ public class CatalogController extends AbstractController {
                                theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
                        }
                        else {
+                               for (MLPArtifact artifact : solutionRevision.getArtifacts()) {
+                                       if (!context.getPeer().getPeerInfo().isLocal()) {
+                                               encodeArtifact(theSolutionId, theRevisionId, artifact, theHttpRequest);
+                                       }
+                               }
+                               for (MLPDocument document : solutionRevision.getDocuments()) {
+                                       if (!context.getPeer().getPeerInfo().isLocal()) {
+                                               encodeDocument(theSolutionId, theRevisionId, document, theHttpRequest);
+                                       }
+                               }
+       
                                response = JsonResponse.<MLPSolutionRevision> buildResponse()
                                                                                                                                .withMessage("solution revision details")
                                                                                                                                .withContent(solutionRevision)
@@ -268,14 +281,14 @@ public class CatalogController extends AbstractController {
         * @return List of solution revision artifacts in JSON format.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision artifacts from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
        @RequestMapping(value = {
                        API.Paths.SOLUTION_REVISION_ARTIFACTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
-       public JsonResponse<List<MLPArtifact>> getSolutionRevisionArtifacts(HttpServletRequest theHttpRequest,
-                       HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
-                       @PathVariable("revisionId") String theRevisionId) {
+       public JsonResponse<List<MLPArtifact>> getSolutionRevisionArtifacts(
+                       HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse,
+                       @PathVariable("solutionId") String theSolutionId,       @PathVariable("revisionId") String theRevisionId) {
                JsonResponse<List<MLPArtifact>> response = null;
                List<MLPArtifact> solutionRevisionArtifacts = null;
                ControllerContext context = new ControllerContext();
@@ -326,14 +339,14 @@ public class CatalogController extends AbstractController {
         * @return List of solution revision documents in JSON format.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision public documents from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
        @RequestMapping(value = {
                        API.Paths.SOLUTION_REVISION_DOCUMENTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
-       public JsonResponse<List<MLPDocument>> getSolutionRevisionDocuments(HttpServletRequest theHttpRequest,
-                       HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
-                       @PathVariable("revisionId") String theRevisionId) {
+       public JsonResponse<List<MLPDocument>> getSolutionRevisionDocuments(
+                       HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse,
+                       @PathVariable("solutionId") String theSolutionId,       @PathVariable("revisionId") String theRevisionId) {
                JsonResponse<List<MLPDocument>> response = null;
                List<MLPDocument> solutionRevisionDocuments = null;
                ControllerContext context = new ControllerContext();
@@ -387,7 +400,7 @@ public class CatalogController extends AbstractController {
         * @return Archive file of the Artifact for the Solution.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "API to download artifact content", response = Resource.class, code = 200)
        @RequestMapping(value = {
                        API.Paths.ARTIFACT_CONTENT }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@@ -431,7 +444,7 @@ public class CatalogController extends AbstractController {
         * @return Archive file of the Artifact for the Solution.
         */
        @CrossOrigin
-       @PreAuthorize("hasAuthority('CATALOG_ACCESS')")
+       @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "API to download a documents' content", response = Resource.class, code = 200)
        @RequestMapping(value = {
                        API.Paths.DOCUMENT_CONTENT }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
index de4033a..a46763d 100644 (file)
@@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.security.access.prepost.PreAuthorize;
 
 import io.swagger.annotations.ApiOperation;
 
@@ -54,7 +55,7 @@ public class PingController extends AbstractController {
         * @return List of Published ML Solutions in JSON format.
         */
        @CrossOrigin
-       //@PreAuthorize("") : nothing in particular, any peer has access to this
+       @PreAuthorize("isActive")
        @ApiOperation(value = "Invoked by Peer Acumos to get status and self information.", response = MLPPeer.class)
        @RequestMapping(value = { API.Paths.PING }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
index fad6d51..ce1e202 100644 (file)
@@ -125,13 +125,13 @@ public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
        @Bean
        public UserDetailsService userDetailsService() {
                return (subject -> {
-                       log.info(EELFLoggerDelegate.debugLogger, " X509 subject : " + subject);
+                       log.info(EELFLoggerDelegate.debugLogger, " X509 subject {}", subject);
                        LdapName x500subject = null;
                        try {
                                x500subject = new LdapName(subject);
                        }
                        catch (InvalidNameException inx) {
-                               log.warn(EELFLoggerDelegate.errorLogger, "Failed to parse subject information : " + subject);
+                               log.warn(EELFLoggerDelegate.errorLogger, "Failed to parse subject information {}", subject);
                                return new Peer(new MLPPeer(), Role.ANY);
                        }
 
@@ -160,7 +160,7 @@ public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
                        }
 
                        List<MLPPeer> mlpPeers = peerService.getPeerBySubjectName(cn);
-                       log.info(EELFLoggerDelegate.debugLogger, " Peers matching X509 subject : " + mlpPeers);
+                       log.info(EELFLoggerDelegate.debugLogger, "Peers matching X509 subject {}", mlpPeers);
                        if (!Utils.isEmptyList(mlpPeers)) {
                                MLPPeer mlpPeer = mlpPeers.get(0);
                                //!!here we create other instances of 'self'
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityConfiguration.java b/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityConfiguration.java
new file mode 100644 (file)
index 0000000..fcea389
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *  
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+
+package org.acumos.federation.gateway.security;
+
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+
+
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class FederationMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
+
+    @Override
+    protected MethodSecurityExpressionHandler createExpressionHandler() {
+        FederationMethodSecurityExpressionHandler expressionHandler = 
+          new FederationMethodSecurityExpressionHandler();
+        //expressionHandler.setPermissionEvaluator(new FederationPermissionEvaluator());
+        return expressionHandler;
+    }
+}
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionHandler.java b/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionHandler.java
new file mode 100644 (file)
index 0000000..a0d959a
--- /dev/null
@@ -0,0 +1,42 @@
+/* 
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.security;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
+import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
+import org.springframework.security.core.Authentication;
+
+
+public class FederationMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
+
+       private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
+
+       @Override
+       protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication theAuthentication,MethodInvocation theInvocation) {
+               final FederationMethodSecurityExpressionRoot root = new FederationMethodSecurityExpressionRoot(theAuthentication);
+               root.setPermissionEvaluator(getPermissionEvaluator());
+               root.setTrustResolver(this.trustResolver);
+               root.setRoleHierarchy(getRoleHierarchy());
+               return root;
+       }
+}
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionRoot.java b/gateway/src/main/java/org/acumos/federation/gateway/security/FederationMethodSecurityExpressionRoot.java
new file mode 100644 (file)
index 0000000..ce56c30
--- /dev/null
@@ -0,0 +1,71 @@
+/* 
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.security;
+
+import org.acumos.federation.gateway.config.EELFLoggerDelegate;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
+
+/**
+ */
+public class FederationMethodSecurityExpressionRoot 
+                                                                                                                               extends SecurityExpressionRoot 
+                                                                                                                                       implements MethodSecurityExpressionOperations {
+
+       private final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(getClass().getName());
+
+       private Object filterObject;
+       private Object returnObject;
+       public  boolean isActive = false;
+       public FederationMethodSecurityExpressionRoot(Authentication theAuthentication) {
+               super(theAuthentication);
+               log.info(EELFLoggerDelegate.debugLogger, "built with {}", theAuthentication);
+               this.isActive = ((Peer) this.getPrincipal()).isActive();        
+       }
+
+       @Override
+       public Object getFilterObject() {
+               return this.filterObject;
+       }
+
+       @Override
+       public Object getReturnObject() {
+               return this.returnObject;
+       }
+
+       @Override
+       public Object getThis() {
+               return this;
+       }
+
+       @Override
+       public void setFilterObject(Object obj) {
+               this.filterObject = obj;
+       }
+
+       @Override
+       public void setReturnObject(Object obj) {
+               this.returnObject = obj;
+       }
+
+}
index 81e2a37..025cb34 100644 (file)
@@ -22,6 +22,7 @@ package org.acumos.federation.gateway.security;
 import java.util.Collection;
 
 import org.acumos.cds.domain.MLPPeer;
+import org.acumos.federation.gateway.cds.PeerStatus;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.User;
 
@@ -45,4 +46,7 @@ public class Peer extends User {
                return this.peerInfo;
        }
 
+       public boolean isActive() {
+               return PeerStatus.Active.equals(PeerStatus.forCode(this.peerInfo.getStatusCode()));
+       }
 }
index c962abf..37c8884 100644 (file)
@@ -25,6 +25,9 @@ package org.acumos.federation.gateway.service.impl;
 
 import java.io.InputStream;
 
+import java.util.List;
+import java.util.stream.Stream;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 
@@ -51,12 +54,16 @@ import org.springframework.core.io.InputStreamResource;
 import org.springframework.stereotype.Service;
 
 import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.model.Image;
 import com.github.dockerjava.api.model.Identifier;
 import com.github.dockerjava.api.model.Repository;
+import com.github.dockerjava.api.model.PushResponseItem;
 import com.github.dockerjava.core.command.PullImageResultCallback;
 import com.github.dockerjava.core.command.PushImageResultCallback;
 
 import org.apache.commons.io.input.ProxyInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
 
 /**
  * Nexus based implementation of the ContentService.
@@ -115,6 +122,7 @@ public class ContentServiceImpl extends AbstractServiceImpl
                                                                                                                                                                                                                                                                                                                                                                                throws ServiceException {
 
                if (ArtifactType.DockerImage == ArtifactType.forCode(theArtifact.getArtifactTypeCode())) {
+                       TrackingPushImageResultCallback pushResult = null;
                        try {
                                TrackingInputStream imageSource = new TrackingInputStream(theResource.getInputStream());
                                //load followed by push
@@ -125,28 +133,58 @@ public class ContentServiceImpl extends AbstractServiceImpl
                                log.debug(EELFLoggerDelegate.debugLogger, "Completed docker image load for {}. Transfered {} bytes in {} seconds.",
                                        theArtifact, imageSource.size(), imageSource.duration()/1000);
 
-                               // there is an assumption here that the repo info was stripped from the artifact name by the originator
-                               Identifier imageId =
-                                       new Identifier(
-                                               new Repository(dockerConfig.getRegistryUrl().toString()),
-                                                                                                        theArtifact.getName() /*the tag*/);
-                               try (PushImageResultCallback pushResult = new PushImageResultCallback()) {
-                                       docker.pushImageCmd(imageId)
-                                                               .exec(pushResult);
-                                       pushResult.awaitCompletion();
-                                       log.debug(EELFLoggerDelegate.debugLogger, "Completed docker image push for {}", theArtifact);
-                               }       
+                               List<Image> images = docker.listImagesCmd().exec();
+                               log.debug(EELFLoggerDelegate.debugLogger, "Available docker images: {}", images);
+
+                               Image image = images.stream()
+                                                                                               .filter(i -> i.getRepoTags() != null && Stream.of(i.getRepoTags()).anyMatch(t -> t.equals(theArtifact.getUri())))
+                                                                                               .findFirst()
+                                                                                               .orElse(null);
+                               if (image == null) {
+                                       log.debug(EELFLoggerDelegate.debugLogger, "Could not find loaded docker image: {}", theArtifact.getUri());
+                                       throw new ServiceException("Could not find loaded docker image for " + theArtifact);
+                               }
+       
+                               //new image name for re-tagging
+                               String imageName = theArtifact.getName() + "_" + theSolutionId; 
+                               docker.tagImageCmd(image.getId(), imageName, theArtifact.getVersion()).exec();
+                               log.debug(EELFLoggerDelegate.debugLogger, "Re-tagged docker image: {} to {}:{}", image, imageName, theArtifact.getVersion());
+                       
+                               log.debug(EELFLoggerDelegate.debugLogger, "Attempt docker push for image {}", image);
+                               docker.pushImageCmd(image.getId())
+                                                       .withName(imageName)
+                                                       .withTag(theArtifact.getVersion())
+                                                       .exec(pushResult = new TrackingPushImageResultCallback());
+
+                               //pushResult.awaitSuccess(); //this will return with no warning even whan failed
+                               pushResult.awaitCompletion();
+                               PushResponseItem pushResponse = pushResult.getResponseItem();
+                               if (pushResponse.isErrorIndicated()) {
+                                       log.debug(EELFLoggerDelegate.debugLogger, "Failed to push artifact {} image {} to docker registry: {}, {}", theArtifact, image, pushResponse.getError(), pushResponse.getErrorDetail());
+                                       throw new ServiceException("Failed to push image to docker registry: " + pushResponse.getError() + "\n" + pushResponse.getErrorDetail());
+                               }
+                               else {
+                                       log.debug(EELFLoggerDelegate.debugLogger, "Completed docker push for artifact {} image {}", theArtifact, image);
+                               }
+                               
+                               String imageUri = dockerConfig.getRegistryUrl() + "/" + imageName + ":" + theArtifact.getVersion();
                                // update artifact with local repo reference. we also update the name and description in order to stay
                                // alligned with on-boarding's unwritten rules
-                               theArtifact.setSize((int)imageSource.size()); //?? is this correct
-                               theArtifact.setUri(imageId.toString());
-                               theArtifact.setName(imageId.toString());
-                               theArtifact.setDescription(imageId.toString());
+                               theArtifact.setSize((int)imageSource.size()); //this is the decompressed size ..
+                               theArtifact.setUri(imageUri);
+                               theArtifact.setDescription(imageUri);
+
+                               //we should now delete the image from the (local) docker host
                        }
                        catch (Exception x) {
                                log.error(EELFLoggerDelegate.errorLogger,
-                                                                       "Failed to push docker artifact content to Nexus repo", x);
-                               throw new ServiceException("Failed to push docker artifact content to Nexus repo", x);
+                                                                       "Failed to put docker artifact content", x);
+                               throw new ServiceException("Failed to put docker artifact content", x);
+                       }
+                       finally {
+                               if (pushResult != null) {
+                                       try { pushResult.close(); } catch (Exception x) {}
+                               }
                        }
                }
                else {
@@ -269,6 +307,10 @@ public class ContentServiceImpl extends AbstractServiceImpl
                                                                                                                                                new String[] {theName.substring(0,pos), theName.substring(pos+1)};
        }
 
+       /**
+        * Allows for accurate counting of the amount of data transferred. The docker image info resulting from
+        * a 'docker image ls' is slightly different ..
+        */
        private static class TrackingInputStream extends ProxyInputStream {
                
                private long size = 0;
@@ -299,4 +341,21 @@ public class ContentServiceImpl extends AbstractServiceImpl
                }
        }
 
+       /**
+        * It only exists because the base does not expose the response item ..
+        */
+       private static class TrackingPushImageResultCallback extends PushImageResultCallback {
+
+               private PushResponseItem        responseItem;
+
+               @Override
+               public void onNext(PushResponseItem theItem) {
+                       this.responseItem = theItem;
+                       super.onNext(theItem);
+    }
+
+               public PushResponseItem getResponseItem() {
+                       return this.responseItem;
+               }
+       }
 }
index aef58af..b0a3703 100644 (file)
   <property name="generalLogName" value="application" />
   <property name="errorLogName" value="error" />
   <property name="debugLogName" value="debug" />
-   
+<!-- 
   <property name="defaultPattern" value="%date{ISO8601}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
   <property name="debugLoggerPattern" value="%date{ISO8601}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
-  
+-->
+       <property name="defaultPattern"
+               value="%date{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;,UTC}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
+       <property name="debugLoggerPattern"
+               value="%date{&quot;yyyy-MM-dd'T'HH:mm:ss.SSSXXX&quot;,UTC}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
+       <!-- This basic audit pattern uses begin/end parameters instead of current time, otherwise identical to default -->
+       <property name="auditLoggerPattern"
+               value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
   <!-- Example evaluator filter applied against console appender -->
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <encoder>