Add local side REST API and fixes security config 39/1139/1
authorSerban Jora <sj2381@att.com>
Wed, 28 Feb 2018 21:18:59 +0000 (16:18 -0500)
committerSerban Jora <sj2381@att.com>
Wed, 28 Feb 2018 21:19:12 +0000 (16:19 -0500)
Also new work on unit tests

Change-Id: Idaf563d58dff963689c9d3e5689c4c47d43e923b
Signed-off-by: Serban Jora <sj2381@att.com>
Issue-ID: ACUMOS-276

22 files changed:
gateway/src/main/java/org/acumos/federation/gateway/adapter/PeerGateway.java
gateway/src/main/java/org/acumos/federation/gateway/common/API.java
gateway/src/main/java/org/acumos/federation/gateway/common/FederationClient.java
gateway/src/main/java/org/acumos/federation/gateway/config/FederationConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/config/GatewayConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/config/RegistrationCondition.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/controller/CatalogController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerPeersController.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerRegistrationController.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/controller/RegistrationController.java
gateway/src/main/java/org/acumos/federation/gateway/security/AuthenticationConfiguration.java
gateway/src/main/java/org/acumos/federation/gateway/security/Peer.java
gateway/src/main/java/org/acumos/federation/gateway/security/Priviledge.java
gateway/src/main/java/org/acumos/federation/gateway/security/Role.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerServiceLocalImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerSubscriptionServiceImpl.java
gateway/src/main/resources/logback.xml
gateway/src/test/java/org/acumos/federation/gateway/test/AuthorizationTest.java [new file with mode: 0644]
gateway/src/test/java/org/acumos/federation/gateway/test/ControllerTest.java
gateway/src/test/resources/acumosc.pkcs12 [new file with mode: 0644]
gateway/src/test/resources/docker-compose.yml

index c0f93b6..44f123c 100644 (file)
@@ -154,10 +154,11 @@ public class PeerGateway {
                                MLPSolution localSolution = null;
                                try {
                                        localSolution = cdsClient.getSolution(peerSolution.getSolutionId());
-                               } catch (HttpStatusCodeException x) {
+                               } 
+                               catch (HttpStatusCodeException x) {
                                        if (!Errors.isCDSNotFound(x)) {
-                                               log.warn(EELFLoggerDelegate.errorLogger, "Failed to check if solution with id "
-                                                               + peerSolution.getSolutionId() + " exists locally, skipping for now", x);
+                                               log.error(EELFLoggerDelegate.errorLogger, "Failed to check if solution with id "
+                                                               + peerSolution.getSolutionId() + " exists locally, skipping for now. Response says " + x.getResponseBodyAsString(), x);
                                                continue;
                                        }
                                }
@@ -167,14 +168,15 @@ public class PeerGateway {
                                                log.info(EELFLoggerDelegate.debugLogger, "Solution Id : " + peerSolution.getSolutionId()
                                                                + " does not exists locally, adding it to local catalog ");
                                                localSolution = createMLPSolution(peerSolution, cdsClient);
-                                       } else {
+                                       }
+                                       else {
                                                localSolution = updateMLPSolution(peerSolution, localSolution, cdsClient);
                                        }
 
                                        mapSolution(localSolution, cdsClient);
-                               } catch (Exception x) {
-                                       x.printStackTrace();
-                                       log.warn(EELFLoggerDelegate.debugLogger,
+                               }
+                               catch (Exception x) {
+                                       log.error(EELFLoggerDelegate.errorLogger,
                                                        "Mapping of acumos solution failed for: " + peerSolution, x);
                                }
                        }
index b064202..b76b186 100644 (file)
@@ -40,7 +40,8 @@ public enum API {
        ARTIFACT_DOWNLOAD(Paths.ARTIFACT_DOWNLOAD),
        PEERS(Paths.PEERS),
        SUBSCRIPTION(Paths.SUBSCRIPTION),
-       PING(Paths.PING);
+       PING(Paths.PING),
+       PEER_REGISTER(Paths.PEER_REGISTER);
 
        private String path;
        private String[] query;
index 68ba51c..40d5b00 100644 (file)
@@ -61,6 +61,9 @@ public class FederationClient extends AbstractClient {
        }
 
        /**
+        * @return Ping information from/for Remote Acumos
+        * @throws HttpStatusCodeException
+        *             Throws HttpStatusCodeException if remote acumos interaction has failed.
         */
        public JsonResponse<MLPPeer> ping()
                        throws HttpStatusCodeException {
@@ -85,6 +88,32 @@ public class FederationClient extends AbstractClient {
                return response == null ? null : response.getBody();
        }       
 
+       /**
+        */
+       public JsonResponse<List<MLPPeer>> getPeers()
+                       throws HttpStatusCodeException {
+               URI uri = API.PEERS.buildUri(this.baseUrl);
+               log.info(EELFLoggerDelegate.debugLogger, "Query for " + uri);
+               ResponseEntity<JsonResponse<List<MLPPeer>>> response = null;
+               try {
+                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
+                                       new ParameterizedTypeReference<JsonResponse<List<MLPPeer>>>() {
+                                       });
+               }
+               catch (HttpStatusCodeException x) {
+                       log.error(EELFLoggerDelegate.errorLogger, uri + " failed" + ((response == null) ? "" : (" " + response)), x);
+                       throw x;
+               }
+               catch (Throwable t) {
+                       log.error(EELFLoggerDelegate.errorLogger, uri + " unexpected failure.", t);
+               }
+               finally {
+                       log.info(EELFLoggerDelegate.debugLogger, uri + " response " + response);
+               }
+               return response == null ? null : response.getBody();
+       }       
+
+
        /**
         * 
         * @param theSelection
@@ -130,6 +159,9 @@ public class FederationClient extends AbstractClient {
        }
 
        /**
+        * @return Peer information from Remote Acumos
+        * @throws HttpStatusCodeException
+        *             Throws HttpStatusCodeException if remote acumos interaction has failed.
         */
        public JsonResponse<MLPSolution> getSolution(String theSolutionId)
                        throws HttpStatusCodeException {
@@ -253,4 +285,31 @@ public class FederationClient extends AbstractClient {
                }
        }
 
+       /**
+        * @return Register self with the peer this client points to.
+        * @throws HttpStatusCodeException
+        *             Throws HttpStatusCodeException if remote acumos interaction has failed.
+        */
+       public JsonResponse<MLPPeer> register(MLPPeer theSelf)
+                       throws HttpStatusCodeException {
+               URI uri = API.PEER_REGISTER.buildUri(this.baseUrl);
+               log.info(EELFLoggerDelegate.debugLogger, "Query for " + uri);
+               ResponseEntity<JsonResponse<MLPPeer>> response = null;
+               try {
+                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
+                                       new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {
+                                       });
+               }
+               catch (HttpStatusCodeException x) {
+                       log.error(EELFLoggerDelegate.errorLogger, uri + " failed" + ((response == null) ? "" : (" " + response)), x);
+                       throw x;
+               }
+               catch (Throwable t) {
+                       log.error(EELFLoggerDelegate.errorLogger, uri + " unexpected failure.", t);
+               }
+               finally {
+                       log.info(EELFLoggerDelegate.debugLogger, uri + " response " + response);
+               }
+               return response == null ? null : response.getBody();
+       }       
 }
index d7c4a31..f09f3ac 100644 (file)
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Scope;
 import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 
@@ -34,6 +35,7 @@ import org.acumos.federation.gateway.config.EELFLoggerDelegate;
 import org.acumos.federation.gateway.controller.CatalogController;
 import org.acumos.federation.gateway.controller.PeersController;
 import org.acumos.federation.gateway.controller.PingController;
+import org.acumos.federation.gateway.controller.RegistrationController;
 import org.acumos.federation.gateway.security.AuthenticationConfiguration;
 
 import org.apache.http.client.HttpClient;
@@ -74,6 +76,12 @@ public class FederationConfiguration {
                return new PingController();
        }
 
+       @Bean
+       @Conditional({RegistrationCondition.class})
+       public RegistrationController registrationServer() {
+               return new RegistrationController();
+       }
+
        /**
    * Build a client for interacting with peers through the defined
         * federation interface.
index d19c06a..24b5940 100644 (file)
@@ -53,7 +53,6 @@ import org.acumos.federation.gateway.task.TaskConfiguration;
 @Import(TaskConfiguration.class)
 @EnableConfigurationProperties({FederationInterfaceConfiguration.class,
                                                                                                                                LocalInterfaceConfiguration.class})
-//@Profile({"gateway"})
 @Conditional({GatewayCondition.class})
 @EnableScheduling
 public class GatewayConfiguration {
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/config/RegistrationCondition.java b/gateway/src/main/java/org/acumos/federation/gateway/config/RegistrationCondition.java
new file mode 100644 (file)
index 0000000..0c630a6
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * ===============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.config;
+
+import org.springframework.context.annotation.Condition;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.env.Environment;
+
+/**
+ * Expresses the configuration based condition determining wether we expose/enable the registration
+ * API or not. 
+ */
+public class RegistrationCondition implements Condition {
+
+       @Override
+       public boolean matches(ConditionContext theContext, AnnotatedTypeMetadata theMetadata) {
+
+               Environment env = theContext.getEnvironment();
+               return null != env && Boolean.parseBoolean(env.getProperty("federation.registration.enabled", "false"));
+       }
+}
index 1d2d6ea..37e6b5a 100644 (file)
@@ -107,7 +107,7 @@ public class CatalogController extends AbstractController {
                                                                                                                 .withError(x)
                                                                                                                 .build();
                        theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error(EELFLoggerDelegate.errorLogger, "Exception occurred fetching Solutions for Market Place Catalog", x);
+                       log.error(EELFLoggerDelegate.errorLogger, "Exception occurred while fetching solutions", x);
                }
                return response;
        }
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/controller/PeerPeersController.java b/gateway/src/main/java/org/acumos/federation/gateway/controller/PeerPeersController.java
new file mode 100644 (file)
index 0000000..63e4592
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ===============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.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.acumos.cds.domain.MLPPeer;
+import org.acumos.federation.gateway.common.API;
+import org.acumos.federation.gateway.common.Clients;
+import org.acumos.federation.gateway.common.JSONTags;
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.config.EELFLoggerDelegate;
+import org.acumos.federation.gateway.security.Peer;
+import org.acumos.federation.gateway.service.PeerService;
+import org.acumos.federation.gateway.service.ServiceContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import io.swagger.annotations.ApiOperation;
+
+@Controller
+@RequestMapping(API.Roots.LOCAL)
+public class PeerPeersController extends AbstractController {
+
+       @Autowired
+       private Clients clients;
+       @Autowired
+       private PeerService peerService;
+       
+
+       /**
+        * Allows local components to ping a peer.
+        * @param theHttpResponse
+        *            HttpServletResponse
+        * @return The remote peer information
+        */
+       @CrossOrigin
+       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).PEER_ACCESS)")
+       @ApiOperation(value = "Invoked by local Acumos to get peers information from remote Acumos peer.", response = MLPPeer.class, responseContainer = "List")
+       @RequestMapping(value = { API.Paths.PEERS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
+       @ResponseBody
+       public JsonResponse<List<MLPPeer>> getPeers(
+                       /* HttpServletRequest theHttpRequest, */
+                       HttpServletResponse theHttpResponse,
+                       @PathVariable("peerId") String thePeerId) {
+
+               JsonResponse<List<MLPPeer>> response = new JsonResponse<List<MLPPeer>>();
+               log.debug(EELFLoggerDelegate.debugLogger, API.Roots.LOCAL + "" + API.Paths.PEERS);
+               try {
+                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
+                       response = this.clients.getFederationClient(peer.getApiUrl()).getPeers();
+
+                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
+               } 
+               catch (Exception x) {
+                       response = JsonResponse.<List<MLPPeer>> buildErrorResponse()
+                                                                                                                .withError(x)
+                                                                                                                .build();
+                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                       log.error(EELFLoggerDelegate.errorLogger, "Exception occurred during peer " + thePeerId + " getPeers", x);
+               }
+               return response;
+       }
+
+}
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/controller/PeerRegistrationController.java b/gateway/src/main/java/org/acumos/federation/gateway/controller/PeerRegistrationController.java
new file mode 100644 (file)
index 0000000..a46c934
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ===============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.controller;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.acumos.cds.domain.MLPPeer;
+import org.acumos.federation.gateway.common.API;
+import org.acumos.federation.gateway.common.Clients;
+import org.acumos.federation.gateway.common.JSONTags;
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.config.EELFLoggerDelegate;
+import org.acumos.federation.gateway.security.Peer;
+import org.acumos.federation.gateway.service.PeerService;
+import org.acumos.federation.gateway.service.ServiceContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import io.swagger.annotations.ApiOperation;
+
+@Controller
+@RequestMapping(API.Roots.LOCAL)
+public class PeerRegistrationController extends AbstractController {
+
+       @Autowired
+       private Clients clients;
+       @Autowired
+       private PeerService peerService;
+       
+
+       /**
+        * Allows local components to require this Acumos instance to register with another Acumos instance.
+        * @param theHttpResponse
+        *            HttpServletResponse
+        * @return The remote peer information
+        */
+       @CrossOrigin
+       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).PEER_ACCESS)")
+       @ApiOperation(value = "Invoked by local Acumos to register with a remote Acumos peer.", response = MLPPeer.class)
+       @RequestMapping(value = { API.Paths.PEER_REGISTER }, method = RequestMethod.POST, produces = APPLICATION_JSON)
+       @ResponseBody
+       public JsonResponse<MLPPeer> register(
+                       /* HttpServletRequest theHttpRequest, */
+                       HttpServletResponse theHttpResponse,
+                       @PathVariable("peerId") String thePeerId) {
+
+               JsonResponse<MLPPeer> response = new JsonResponse<MLPPeer>();
+               log.debug(EELFLoggerDelegate.debugLogger, API.Roots.LOCAL + "" + API.Paths.PEER_REGISTER);
+               try {
+                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
+                       response = this.clients.getFederationClient(peer.getApiUrl()).register(peerService.getSelf());
+
+                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
+               } 
+               catch (Exception x) {
+                       response = JsonResponse.<MLPPeer> buildErrorResponse()
+                                                                                                                .withError(x)
+                                                                                                                .build();
+                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                       log.error(EELFLoggerDelegate.errorLogger, "Exception occurred during peer " + thePeerId + " getPeers", x);
+               }
+               return response;
+       }
+
+}
index 4391c84..3173113 100644 (file)
@@ -60,8 +60,8 @@ public class RegistrationController extends AbstractController {
         * @return Request status information
         */
        @CrossOrigin
-       //@PreAuthorize("hasAuthority('')") //if enabled, this must be opened to anonymous so no such check
-       @ApiOperation(value = "Invoked by another Acumos Instance to request federation.", response = String.class)
+       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).REGISTRATION_ACCESS)")
+       @ApiOperation(value = "Invoked by another Acumos Instance to request federation.", response = MLPPeer.class)
        @RequestMapping(value = { API.Paths.PEER_REGISTER }, method = RequestMethod.POST, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<MLPPeer> registerPeer(
@@ -103,8 +103,8 @@ public class RegistrationController extends AbstractController {
        /**
         */
        @CrossOrigin
-       //@PreAuthorize("hasAuthority('')") 
-       @ApiOperation(value = "Invoked by another Acumos Instance to request federation teermination.", response = String.class)
+       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).REGISTRATION_ACCESS)")
+       @ApiOperation(value = "Invoked by another Acumos Instance to request federation termination.", response = MLPPeer.class)
        @RequestMapping(value = { API.Paths.PEER_UNREGISTER }, method = RequestMethod.POST, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<MLPPeer> unregisterPeer(
index 02e48d7..d121d66 100644 (file)
@@ -26,6 +26,8 @@ import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 import javax.naming.InvalidNameException;
 
+import javax.servlet.http.HttpServletResponse;
+
 import org.acumos.cds.domain.MLPPeer;
 
 import org.acumos.federation.gateway.config.EELFLoggerDelegate;
@@ -42,6 +44,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
@@ -52,6 +56,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
  * X.509 certificate authentication : verifying the identity of a communication
  * peer when using the HTTPS (HTTP over SSL) protocol.
  *
+ * EnableWebSecurity would probably be sufficient but needs some more work as PreAuthorized would
+ * not be accessible.
  */
 
 @Configuration
@@ -82,10 +88,22 @@ public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
 
-               http.authorizeRequests()
+               http
+                               .csrf()
+                                       .disable();
+               //when the user is a valid user but does not have the right priviledges the accessDeniedHandler 
+               //is called
+               http
+                               .exceptionHandling()
+                                       .accessDeniedHandler(accessDeniedHandler());
+               http
+                               .authorizeRequests()
                                        .anyRequest()
                                                .authenticated()
-                                               .and()
+                                       .and()
+                                               .sessionManagement()
+                                                       .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                                       .and()
                                                .x509()
                                                        //.x509AuthenticationFilter(new X509AuthenticationFilter() {
                                                        //              {
@@ -101,6 +119,17 @@ public class AuthenticationConfiguration extends WebSecurityConfigurerAdapter {
                                                        .userDetailsService(userDetailsService());
        }
 
+       /** */
+       @Bean
+       public AccessDeniedHandler accessDeniedHandler() {
+               return ((request, response, exception) -> {
+                       log.info(EELFLoggerDelegate.debugLogger, "accessDeniedHandler : " + exception);
+                       response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+               });
+       }
+
+       /** */
+       @Bean
        public UserDetailsService userDetailsService() {
                return (subject -> {
                        log.info(EELFLoggerDelegate.debugLogger, " X509 subject : " + subject);
index cf93838..72aaedb 100644 (file)
@@ -27,10 +27,12 @@ import org.springframework.security.core.userdetails.User;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import org.acumos.federation.gateway.service.PeerService;
+import org.acumos.federation.gateway.cds.PeerStatus;
 
 import org.acumos.cds.domain.MLPPeer;
 
 /**
+ * Peers constitute the users of the federation gateway.
  */
 public class Peer extends User {
 
@@ -49,6 +51,15 @@ public class Peer extends User {
                return this.peerInfo;
        }
 
+//     @Override
+//     public boolean isEnabled() {
+//             if (this.peerInfo == null)
+//                     return false;
+
+//             PeerStatus peerStatus =  PeerStatus.forCode(this.peerInfo.getStatusCode());
+//             return peerStatus == PeerStatus.Active;
+//     }
+
        private static PeerService peerService = null;
 
        @Autowired
index 161317b..fdeaff6 100644 (file)
@@ -22,26 +22,36 @@ package org.acumos.federation.gateway.security;
 import org.springframework.security.core.GrantedAuthority;
 
 /**
- * An enumeratoin of federated access fine grained proviledges
+ * An enumeratoin of federatiom access priviledges. Organized around the
+ * functional sets of the federation and local API.
  */
 public enum Priviledge implements GrantedAuthority {
 
        /**
         * Granted to a peer to access catalog items (solutions) and related information; coarse at this point, all
-        * (list/read/download) or nothing
+        * (list/read/download) or nothing. The peer must be active.
         */
        CATALOG_ACCESS,
+
        /**
-        * Granted to a peer to request information about the peers registered in the local Acumos system.
+        * Granted to a peer to request information about the peers registered in the local Acumos system. The peer
+   * must be active.
         */
        PEERS_ACCESS,
+
+       /**
+        * The right to submit a subscription request. This is granted to ANY (only if so
+        * enabled system wide).
+        */
+       REGISTRATION_ACCESS,
+
        /**
-        * The right to submit a subscription request. This is granted to ANY if so
-        * enabled system wide.
+        * The right to submit a ping request. Granted to active and inactive peers.
         */
-       SUBSCRIPTION,
+       PING_ACCESS,
+
        /**
-        * granted to local Acumos components to request information from peers.
+        * granted to local Acumos components to request information from peers (local interface access).
         */
        PEER_ACCESS;
 
index 9923084..7776c87 100644 (file)
@@ -27,22 +27,26 @@ import org.springframework.security.core.GrantedAuthority;
 
 /**
  * Each Role states a predefined set of available federation priviledges.
+ * TODO: looks into unregisterPeer 
  */
 public enum Role {
 
        /**
-        * Un-authenticated client. Will at most be granted access to subscribe
+        * Un-authenticated client. Will at most be granted access to registration
         * functionality
         */
-       ANY(Collections.EMPTY_LIST),
+       ANY(Collections.unmodifiableList(Arrays.asList(Priviledge.REGISTRATION_ACCESS))),
        /**
-        * Common peer, grants generic solution catalog access
+        * Common active peer, grants generic solution catalog access
         */
-       PEER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS))),
+       PEER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS,
+                                                                                                                                                                                                       Priviledge.PING_ACCESS))),
        /**
         * Enhanced peer, gains (some lovel of) read access to the local peer list
         */
-       PARTNER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS, Priviledge.PEERS_ACCESS))),
+       PARTNER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS,
+                                                                                                                                                                                                                Priviledge.PEERS_ACCESS,
+                                                                                                                                                                                                                Priviledge.PING_ACCESS))),
        /**
         * The Acumos instance this gateway is serving, including local calls and calls
         * received through the gateways' private interface from other components,
index 9a8c1d3..97bdd67 100644 (file)
@@ -96,7 +96,7 @@ public class PeerServiceImpl extends AbstractServiceImpl implements PeerService
                RestPageResponse<MLPPeer> response = 
                        getClient().searchPeers(new MapBuilder().put("subjectName", theSubjectName).build(), false, null);
                if (response.getSize() != 1) {
-                       log.warn(EELFLoggerDelegate.errorLogger, "getPeerBySubjectName returned more then one peer:{}", response.getSize());
+                       log.warn(EELFLoggerDelegate.errorLogger, "getPeerBySubjectName returned more then one peer: {}", response.getSize());
                }
                return response.getContent();
        }
index df16992..b206717 100644 (file)
@@ -147,7 +147,8 @@ public class PeerServiceLocalImpl extends AbstractServiceLocalImpl implements Pe
        /** */
        @Override
        public void registerPeer(MLPPeer mlpPeer) {
-               throw new UnsupportedOperationException();
+               log.info(EELFLoggerDelegate.debugLogger, "Registered peer {}", mlpPeer);
+               //this.peers.put(new FLPPeer(mlpPeer));
        }
 
        /** */
index 01668e4..aecb62b 100644 (file)
@@ -58,9 +58,9 @@ public class PeerSubscriptionServiceImpl extends AbstractServiceImpl implements
        @Override
        public List<MLPPeerSubscription> getPeerSubscriptions(String peerId) {
                log.debug(EELFLoggerDelegate.debugLogger, "getPeerSubscriptions:{}", peerId);
-               List<MLPPeerSubscription> mlpPeerSubscriptions = null;
+               List<MLPPeerSubscription> peerSubscriptions = null;
                // Temporary Fix as COmmon Data Service does not handle proper Serialization
-               mlpPeerSubscriptions = getClient().getPeerSubscriptions(peerId);
+               peerSubscriptions = getClient().getPeerSubscriptions(peerId);
                /*
                 * if(!Utils.isEmptyList(mlpPeerSubscriptions)) { //mlpPeerSubscriptions =
                 * mlpPeerSubscriptionPaged.getContent(); mlpPeerSubscriptions =
@@ -68,8 +68,8 @@ public class PeerSubscriptionServiceImpl extends AbstractServiceImpl implements
                 * (mlpPeerSubscription.getPeerId().contains(peerId))).collect(Collectors.toList
                 * ()); }
                 */
-               log.debug(EELFLoggerDelegate.debugLogger, "getPeers size:{}", mlpPeerSubscriptions.size());
-               return mlpPeerSubscriptions;
+               log.debug(EELFLoggerDelegate.debugLogger, "peer {} subscriptions : {}", peerId, peerSubscriptions.size());
+               return peerSubscriptions;
        }
 
        @Override
index d63824f..aef58af 100644 (file)
 <!--
        <logger name="org.springframework.core.io.support" level="debug"/>
        <logger name="org.springframework.context.annotation" level="debug"/>
+       <logger name="org.springframework.security" level="debug"/>
 -->
 </configuration>
diff --git a/gateway/src/test/java/org/acumos/federation/gateway/test/AuthorizationTest.java b/gateway/src/test/java/org/acumos/federation/gateway/test/AuthorizationTest.java
new file mode 100644 (file)
index 0000000..72a3332
--- /dev/null
@@ -0,0 +1,202 @@
+/*-
+ * ===============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.test;
+
+import java.util.List;
+import java.util.Scanner;
+
+import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat; 
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextHierarchy;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.core.ParameterizedTypeReference;
+
+import org.apache.http.client.HttpClient;
+
+/* this is not good for unit testing .. */
+import org.acumos.federation.gateway.config.EELFLoggerDelegate;
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.config.InterfaceConfigurationBuilder;
+import static org.acumos.federation.gateway.config.InterfaceConfigurationBuilder.SSLBuilder;
+
+import org.acumos.cds.domain.MLPPeer;
+import org.acumos.cds.domain.MLPSolution;
+import org.acumos.cds.domain.MLPSolutionRevision;
+import org.acumos.cds.domain.MLPArtifact;
+
+
+
+/**
+ */
+
+//@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
+@ContextHierarchy({
+       @ContextConfiguration(classes = org.acumos.federation.gateway.test.TestAdapterConfiguration.class),
+       @ContextConfiguration(classes = org.acumos.federation.gateway.config.FederationConfiguration.class)
+})
+@SpringBootTest(classes = org.acumos.federation.gateway.Application.class,
+                                                               webEnvironment = WebEnvironment.RANDOM_PORT,
+                                                               properties = {
+                                                                       "federation.instance=adapter",
+                                                                       "federation.instance.name=test",
+                                                                       "federation.operator=admin",
+                                                                       "federation.registration.enabled=true",
+                                                                       "peersLocal.source=classpath:test-peers.json",
+                                                                       "catalogLocal.source=classpath:test-catalog.json",
+                                                                       "federation.ssl.key-store=classpath:acumosa.pkcs12",
+                                                                       "federation.ssl.key-store-password=acumosa",
+                                                                       "federation.ssl.key-store-type=PKCS12",
+                                                                       "federation.ssl.key-password = acumosa",
+                                                                       "federation.ssl.trust-store=classpath:acumosTrustStore.jks",
+                                                                       "federation.ssl.trust-store-password=acumos",
+                                                                       "federation.ssl.client-auth=need"
+                                                               })
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AuthorizationTest {
+
+       private final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(getClass().getName());
+       @Autowired
+       private TestRestTemplate restTemplate;
+
+       @Test
+       public void testUnknownPeerSolutionsAccess() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareUnknownHttpClient());
+               
+               ResponseEntity<JsonResponse<List<MLPSolution>>> response =
+                       this.restTemplate.exchange("/solutions", HttpMethod.GET, prepareRequest(), new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {});
+               
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "test unknown peer access: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "test unknown peer access: {}", response);
+               }
+               
+               assertTrue(response != null);
+               assertTrue(response.getStatusCodeValue() == 401);
+       }
+
+       @Test
+       public void testKnownPeerSolutionsAccess() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareKnownHttpClient());
+
+               ResponseEntity<JsonResponse<List<MLPSolution>>> response =
+                       this.restTemplate.exchange("/solutions", HttpMethod.GET, prepareRequest(), new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {});
+               
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "test known peer access: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "test known peer access: {}", response);
+               }
+               
+               assertTrue(response != null);
+               assertTrue(response.getStatusCodeValue() == 200);
+               assertTrue(response.getBody().getContent().size() == 1);
+       
+       }
+
+       @Test
+       public void testUnknownRegisterAccess() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareUnknownHttpClient());
+               
+               ResponseEntity<JsonResponse<MLPPeer>> response =
+                       this.restTemplate.exchange("/peer/register", HttpMethod.POST, prepareRequest(), new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {});
+               
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "test unknown peer access to register: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "test unknown peer access to register: {}", response);
+               }
+               
+               assertTrue(response != null);
+               assertTrue(response.getStatusCodeValue() == 202); //401 ??
+       }
+
+
+       private HttpEntity prepareRequest(String theResourceName) {
+               String content = new Scanner(
+                                                                          Thread.currentThread().getContextClassLoader().getResourceAsStream(theResourceName), "UTF-8")
+                                                                                       .useDelimiter("\\Z").next();
+
+               HttpHeaders headers = new HttpHeaders();
+               headers.setContentType(MediaType.APPLICATION_JSON);
+               return new HttpEntity<String>(content, headers);
+       }
+       
+       private HttpEntity prepareRequest() {
+               HttpHeaders headers = new HttpHeaders();
+               headers.setContentType(MediaType.APPLICATION_JSON);
+               return new HttpEntity<String>(headers);
+       }
+
+       private HttpClient prepareKnownHttpClient() {
+               return new InterfaceConfigurationBuilder()
+                                                               .withSSL(new SSLBuilder()
+                                                                                                                       .withKeyStore("classpath:/acumosb.pkcs12")
+                                                                                                                       .withKeyStorePassword("acumosb")
+                                                                                                                       //.withKeyPassword("acumosb")
+                                                                                                                       .withTrustStore("classpath:/acumosTrustStore.jks")
+                                                                                                                       .withTrustStoreType("JKS")
+                                                                                                                       .withTrustStorePassword("acumos")
+                                                                                                                       .build())
+                                                               .buildConfig()
+                                                               .buildClient();
+       }
+
+       private HttpClient prepareUnknownHttpClient() {
+               return new InterfaceConfigurationBuilder()
+                                                               .withSSL(new SSLBuilder()
+                                                                                                                       .withKeyStore("classpath:/acumosc.pkcs12")
+                                                                                                                       .withKeyStorePassword("acumosc")
+                                                                                                                       //.withKeyPassword("acumosb")
+                                                                                                                       .withTrustStore("classpath:/acumosTrustStore.jks")
+                                                                                                                       .withTrustStoreType("JKS")
+                                                                                                                       .withTrustStorePassword("acumos")
+                                                                                                                       .build())
+                                                               .buildConfig()
+                                                               .buildClient();
+       }
+}
+
index 2ae32d3..0113992 100644 (file)
@@ -212,7 +212,7 @@ public class ControllerTest {
                        System.out.println("testPeers: " + response);
                }
 
-               assertTrue(response.getStatusCodeValue() == 403);
+               assertTrue(response.getStatusCodeValue() == 401);
        }
 
        private HttpEntity prepareRequest(String theResourceName) {
diff --git a/gateway/src/test/resources/acumosc.pkcs12 b/gateway/src/test/resources/acumosc.pkcs12
new file mode 100644 (file)
index 0000000..4280196
Binary files /dev/null and b/gateway/src/test/resources/acumosc.pkcs12 differ
index cc911ed..faa8ce1 100644 (file)
@@ -2,37 +2,37 @@ version: '2.0'
 services:
   federation:
     container_name: federation-gateway
-    image: federation-gateway:latest
+    image: federation-gateway:1.1.0-SNAPSHOT
     environment:
       SPRING_APPLICATION_JSON: '{
         "federation" : {
           "instance" : "gateway",
           "instance.name" : "ATT",
           "operator" : "d468656f-57d0-46e3-9f94-7ffa4f66dc03",
-          "address" : "0.0.0.0"
+          "address" : "0.0.0.0",
           "server" : {
-            "port" : 9084
+            "port" : 9002
           },
           "ssl" : {
-            "key-store" : "ist-acumosa.pkcs12",
+            "key-store" : "/app/acumosa.pkcs12",
             "key-store-password" : "acumosa",
             "key-store-type" : "PKCS12",
                "key-password" : "acumosa",
-             "trust-store" : "acumosTrustStore.jks",
+             "trust-store" : "/app/acumosTrustStore.jks",
             "trust-store-password" : "acumos"
                }
         },
         "local" : {
-          "address": "0.0.0.0"
+          "address": "0.0.0.0",
           "server": {
-            "port" : 9085
+            "port" : 9022
           },
           "ssl" : {
-            "key-store" : "ist-acumosa.pkcs12",
+            "key-store" : "/app/acumosa.pkcs12",
             "key-store-password" : "acumosa",
             "key-store-type" : "PKCS12",
             "key-password" : "acumosa",
-           "trust-store" : "acumosTrustStore.jks",
+           "trust-store" : "/app/acumosTrustStore.jks",
                  "trust-store-password" : "acumos"
           }
         },
@@ -43,11 +43,6 @@ services:
           "username": "cognita_model_rw",
           "groupid": "com.artifact"
         },
-        "spring": {
-          "profiles": {
-            "active": "gateway"
-          }
-        },
         "peer": {
           "jobchecker": {
             "interval": 300
@@ -62,9 +57,13 @@ services:
         }
       }'
     expose:
-      - "9084"
+      - "9002"
+      - "9022"
     ports:
-      - "9084:9084"
+      - "9002:9002"
+      - "9022:9022"
+    volumes:
+      - ./:/app
     logging:
       driver: json-file