From 9dfd7e28c1eb348fcb4a2de8c6faae2a01b34942 Mon Sep 17 00:00:00 2001 From: Ofir Sonsino Date: Wed, 20 Sep 2017 13:20:42 +0300 Subject: [PATCH] Global Read only role, Support VID specific Roles Issue-ID: VID-46 , VID-47 Change-Id: Ib100d20ac40a65d39e27a6e2741b19a173a2b8ea Signed-off-by: Ofir Sonsino --- .../src/main/webapp/WEB-INF/jsp/serviceModels.jsp | 3 +- .../org/ecomp/aai/model/AaiAICZones/AicZones.java | 10 + .../java/org/ecomp/aai/model/AaiAICZones/Zone.java | 13 + .../org/openecomp/aai/util/HttpsAuthClient.java | 2 +- .../main/java/org/openecomp/vid/aai/AaiClient.java | 284 ++++ .../org/openecomp/vid/aai/AaiClientInterface.java | 22 + .../java/org/openecomp/vid/aai/AaiResponse.java | 21 + .../org/openecomp/vid/aai/ServiceInstance.java | 33 + .../org/openecomp/vid/aai/ServiceInstances.java | 14 + .../org/openecomp/vid/aai/ServiceSubscription.java | 20 + .../openecomp/vid/aai/ServiceSubscriptions.java | 17 + .../main/java/org/openecomp/vid/aai/Services.java | 24 + .../openecomp/vid/aai/SubscriberAaiResponse.java | 24 + .../java/org/openecomp/vid/aai/SubscriberData.java | 7 + .../vid/aai/SubscriberFilteredResults.java | 29 + .../vid/aai/SubscriberListWithFilterData.java | 31 + .../openecomp/vid/aai/SubscriberWithFilter.java | 21 + .../exceptions/InvalidAAIResponseException.java | 7 + .../GetServicesAAIRespone.java | 11 + .../model/AaiGetServicesRequestModel/Service.java | 21 + .../model/AaiGetTenatns/GetTenantsResponse.java | 26 + .../openecomp/vid/aai/model/ServiceInstance.java | 26 + .../openecomp/vid/aai/model/ServiceInstances.java | 12 + .../vid/aai/model/ServiceSubscription.java | 17 + .../java/org/openecomp/vid/aai/model/Services.java | 24 + .../openecomp/vid/asdc/beans/SecureService.java | 9 + .../openecomp/vid/asdc/beans/SecureServices.java | 30 + .../vid/asdc/beans/tosca/ToscaMetadata.java | 30 + .../openecomp/vid/asdc/local/LocalAsdcClient.java | 2 +- .../vid/asdc/parser/ToscaParserImpl2.java | 60 +- .../openecomp/vid/asdc/rest/RestfulAsdcClient.java | 2 + .../org/openecomp/vid/client/FakeHttpSession.java | 224 +++ .../org/openecomp/vid/client/HttpsBasicClient.java | 4 +- .../openecomp/vid/controller/AaiController.java | 924 ++++++------ .../openecomp/vid/controller/MsoController.java | 1373 ++++++++--------- .../openecomp/vid/controller/VidController.java | 20 +- .../org/openecomp/vid/controller/WebConfig.java | 18 +- .../java/org/openecomp/vid/model/ModelUtil.java | 45 + .../org/openecomp/vid/model/ProxyResponse.java | 21 + .../main/java/org/openecomp/vid/model/Service.java | 23 + .../java/org/openecomp/vid/model/Subscriber.java | 26 + .../org/openecomp/vid/model/SubscriberList.java | 15 + .../java/org/openecomp/vid/roles/EcompRole.java | 5 + .../main/java/org/openecomp/vid/roles/Role.java | 48 + .../java/org/openecomp/vid/roles/RoleProvider.java | 62 + .../org/openecomp/vid/roles/RoleValidator.java | 57 + .../org/openecomp/vid/services/AaiService.java | 25 + .../org/openecomp/vid/services/AaiServiceImpl.java | 70 + .../org/openecomp/vid/services/VidServiceImpl.java | 4 +- .../src/main/resources/csar3933948645405128424.zip | Bin 0 -> 104292 bytes vid-app-common/src/main/resources/pnf.csar | Bin 0 -> 33299 bytes vid-app-common/src/main/resources/roles.json | 8 + vid-app-common/src/main/resources/sdcservices.json | 29 +- .../vid/scripts/constants/componentConstants.js | 18 +- .../app/vid/scripts/constants/fieldConstants.js | 615 ++++---- .../app/vid/scripts/constants/vidConfiguration.js | 5 +- .../scripts/controller/InstantiationController.js | 18 +- .../scripts/controller/ServiceModelController.js | 100 +- .../scripts/controller/aaiSubscriberController.js | 1561 +++++++++++--------- .../scripts/controller/creationDialogController.js | 14 +- .../scripts/controller/deletionDialogController.js | 4 + .../scripts/controller/previousVersionContoller.js | 40 + .../controller/previousVersionDialogController.js | 40 + .../scripts/controller/statusDialogController.js | 2 +- .../app/vid/scripts/controller/subscriberSearch.js | 2 +- .../scripts/directives/parameterBlockDirective.js | 654 ++++---- .../directives/parameterBlockDirective.js.orig | 507 +++++++ .../webapp/app/vid/scripts/services/aaiService.js | 674 +++++---- .../app/vid/scripts/services/componentService.js | 4 +- .../app/vid/scripts/services/creationService.js | 154 +- .../webapp/app/vid/scripts/services/dataService.js | 15 + .../app/vid/scripts/services/deletionService.js | 22 +- .../webapp/app/vid/scripts/services/vnfService.js | 39 +- .../app/vid/scripts/view-models/aaiGetSubs.htm | 133 +- .../scripts/view-models/aaiGetSubscriberList.htm | 6 +- .../vid/scripts/view-models/aaiServiceTypes.htm | 8 +- .../app/vid/scripts/view-models/aaiSubDetails.htm | 7 +- .../app/vid/scripts/view-models/aaiSubViewEdit.htm | 250 ++-- .../view-models/createInstanceServiceModels.htm | 20 +- .../app/vid/scripts/view-models/creationDialog.htm | 2 +- .../app/vid/scripts/view-models/instantiate.htm | 386 ++--- .../app/vid/scripts/view-models/msoCommit.htm | 2 +- .../vid/scripts/view-models/previousVersion.htm | 44 + .../scripts/view-models/previousVersionDialog.htm | 48 + .../app/vid/scripts/view-models/serviceModels.htm | 10 +- .../src/main/webapp/app/vid/test/testMso.js | 2 +- .../src/main/webapp/app/vid/test/testViewEdit.js | 2 +- .../vid/controller/ToscaParserMockHelper.java | 42 + .../opencomp/vid/controller/VidControllerTest.java | 216 +-- .../openecomp/fusionapp/controller/NetMapTest.java | 3 +- .../fusionapp/service/ProfileServiceTest.java | 16 +- .../main/java/org/vid/dao/FnAppDoaImplTest.java | 4 +- .../test/resources/WEB-INF/conf/asdc.properties | 17 + .../test/resources/WEB-INF/conf/quartz.properties | 36 + .../test/resources/WEB-INF/conf/raptor.properties | 168 +++ .../WEB-INF/conf/raptor_app_fusion.properties | 20 + .../WEB-INF/conf/raptor_db_fusion.properties | 0 .../resources/WEB-INF/conf/raptor_pdf.properties | 30 + .../src/test/resources/WEB-INF/conf/sql.properties | 303 ++++ .../test/resources/WEB-INF/conf/system.properties | 167 +++ .../resources/WEB-INF/conf/system.properties.cml | 97 ++ .../WEB-INF/fusion/conf/fusion.properties | 69 + .../resources/WEB-INF/fusion/defs/definitions.xml | 242 +++ .../resources/WEB-INF/fusion/jsp/ds2/footer.jsp | 34 + .../resources/WEB-INF/fusion/jsp/ds2/header.jsp | 236 +++ .../resources/WEB-INF/fusion/jsp/ds2/left-menu.jsp | 804 ++++++++++ .../WEB-INF/fusion/jsp/ebz/ebz_header.jsp | 833 +++++++++++ .../fusion/orm/RNoteBookIntegration.hbm.xml | 44 + .../src/test/resources/WEB-INF/index.jsp | 24 + .../WEB-INF/jsp/createnewserviceinstance.jsp | 146 ++ .../src/test/resources/WEB-INF/jsp/login.jsp | 125 ++ .../test/resources/WEB-INF/jsp/login_external.jsp | 126 ++ .../resources/WEB-INF/jsp/searchexistingsi.jsp | 81 + .../test/resources/WEB-INF/jsp/serviceModels.jsp | 82 + .../resources/WEB-INF/jsp/subscriberdetails.jsp | 184 +++ .../src/test/resources/WEB-INF/jsp/testMso.jsp | 174 +++ .../test/resources/WEB-INF/jsp/testViewEdit.jsp | 172 +++ .../src/test/resources/WEB-INF/jsp/viewlog.jsp | 33 + .../src/test/resources/WEB-INF/jsp/welcome.jsp | 49 + vid-app-common/src/test/resources/WEB-INF/web.xml | 11 + vid-app-common/src/test/resources/pnf.csar | Bin 0 -> 33299 bytes vid-app-common/src/test/resources/roles.json | 8 + vid-app-common/src/test/resources/vf-csar.JSON | 5 +- vid-app-common/src/test/resources/vl-csar.JSON | 8 +- 124 files changed, 10490 insertions(+), 3380 deletions(-) create mode 100644 vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/AicZones.java create mode 100644 vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/Zone.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClient.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClientInterface.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/AaiResponse.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstance.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstances.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscription.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscriptions.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/Services.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberAaiResponse.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberData.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberFilteredResults.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberListWithFilterData.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberWithFilter.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/exceptions/InvalidAAIResponseException.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/GetServicesAAIRespone.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/Service.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetTenatns/GetTenantsResponse.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstance.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstances.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceSubscription.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/aai/model/Services.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureService.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureServices.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/client/FakeHttpSession.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/ProxyResponse.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/Subscriber.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/model/SubscriberList.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/roles/EcompRole.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/roles/Role.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/roles/RoleProvider.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/roles/RoleValidator.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/services/AaiService.java create mode 100644 vid-app-common/src/main/java/org/openecomp/vid/services/AaiServiceImpl.java create mode 100644 vid-app-common/src/main/resources/csar3933948645405128424.zip create mode 100644 vid-app-common/src/main/resources/pnf.csar create mode 100644 vid-app-common/src/main/resources/roles.json create mode 100644 vid-app-common/src/main/webapp/app/vid/scripts/controller/previousVersionContoller.js create mode 100644 vid-app-common/src/main/webapp/app/vid/scripts/controller/previousVersionDialogController.js create mode 100644 vid-app-common/src/main/webapp/app/vid/scripts/directives/parameterBlockDirective.js.orig create mode 100644 vid-app-common/src/main/webapp/app/vid/scripts/view-models/previousVersion.htm create mode 100644 vid-app-common/src/main/webapp/app/vid/scripts/view-models/previousVersionDialog.htm create mode 100644 vid-app-common/src/test/java/org/opencomp/vid/controller/ToscaParserMockHelper.java create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/asdc.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/quartz.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/raptor.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/raptor_app_fusion.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/raptor_db_fusion.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/raptor_pdf.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/sql.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/system.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/conf/system.properties.cml create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/conf/fusion.properties create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/defs/definitions.xml create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/jsp/ds2/footer.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/jsp/ds2/header.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/jsp/ds2/left-menu.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/jsp/ebz/ebz_header.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/fusion/orm/RNoteBookIntegration.hbm.xml create mode 100644 vid-app-common/src/test/resources/WEB-INF/index.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/createnewserviceinstance.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/login.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/login_external.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/searchexistingsi.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/serviceModels.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/subscriberdetails.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/testMso.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/testViewEdit.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/viewlog.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/jsp/welcome.jsp create mode 100644 vid-app-common/src/test/resources/WEB-INF/web.xml create mode 100644 vid-app-common/src/test/resources/pnf.csar create mode 100644 vid-app-common/src/test/resources/roles.json diff --git a/epsdk-app-onap/src/main/webapp/WEB-INF/jsp/serviceModels.jsp b/epsdk-app-onap/src/main/webapp/WEB-INF/jsp/serviceModels.jsp index 1dbb3c0d..6d230510 100755 --- a/epsdk-app-onap/src/main/webapp/WEB-INF/jsp/serviceModels.jsp +++ b/epsdk-app-onap/src/main/webapp/WEB-INF/jsp/serviceModels.jsp @@ -33,7 +33,8 @@ - + + diff --git a/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/AicZones.java b/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/AicZones.java new file mode 100644 index 00000000..ee8bcf0c --- /dev/null +++ b/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/AicZones.java @@ -0,0 +1,10 @@ +package org.ecomp.aai.model.AaiAICZones; + +import java.util.List; + +import org.codehaus.jackson.annotate.JsonProperty; + +public class AicZones { + @JsonProperty("zone") + public List zones; +} diff --git a/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/Zone.java b/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/Zone.java new file mode 100644 index 00000000..00d387c2 --- /dev/null +++ b/vid-app-common/src/main/java/org/ecomp/aai/model/AaiAICZones/Zone.java @@ -0,0 +1,13 @@ +package org.ecomp.aai.model.AaiAICZones; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Zone { + @JsonProperty("zone-id") + public String zoneId; + + @JsonProperty("zone-name") + public String zoneName; +} diff --git a/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java b/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java index 6a3665ed..3323b3fb 100755 --- a/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java +++ b/vid-app-common/src/main/java/org/openecomp/aai/util/HttpsAuthClient.java @@ -80,7 +80,7 @@ public class HttpsAuthClient{ useClientCert = true; } - System.setProperty("javax.net.ssl.trustStore", truststore_path); + System.setProperty("javax.net.ssl.trustStore", truststore_path); System.setProperty("javax.net.ssl.trustStorePassword", decrypted_truststore_password); HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){ public boolean verify(String string,SSLSession ssls) { diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClient.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClient.java new file mode 100644 index 00000000..af1f4042 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClient.java @@ -0,0 +1,284 @@ +package org.openecomp.vid.aai; + +import org.apache.http.HttpStatus; +import org.codehaus.jackson.map.ObjectMapper; +import org.ecomp.aai.model.AaiAICZones.AicZones; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.openecomp.aai.util.AAIRestInterface; +import org.apache.tiles.request.ApplicationContext; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.web.support.UserUtils; +import org.openecomp.vid.aai.model.AaiGetServicesRequestModel.GetServicesAAIRespone; +import org.openecomp.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.openecomp.vid.model.SubscriberList; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.ServletContext; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.UUID; + + +/** + * Created by Oren on 7/4/17. + */ +public class AaiClient implements AaiClientInterface { + + /** + * The Constant dateFormat. + */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + protected String fromAppId = "VidAaiController"; + @Autowired + ServletContext servletContext; + /** + * The logger + */ + + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AaiClient.class); + + + public AaiClient() { + // certiPath = getCertificatesFile().getAbsolutePath(); + // depth = "0"; + } + + public AaiClient(ServletContext context) { + servletContext = context; + } + + + + private static String checkForNull(String local) { + if (local != null) + return local; + else + return ""; + + } + + @Override + public AaiResponse getAllSubscribers() { + String certiPath = getCertificatesFile().getAbsolutePath(); + String depth = "0"; + Response resp = doAaiGet(certiPath, "business/customers?subscriber-type=INFRA&depth=" + depth, false); + UserUtils userUtils = new UserUtils(); + return proccessAaiResponse(resp, SubscriberList.class,null); + } + + + @Override + public AaiResponse getAllAicZones() { + String certiPath = getCertificatesFile().getAbsolutePath(); + Response resp = doAaiGet(certiPath, "network/zones" , false); + AaiResponse aaiAicZones = proccessAaiResponse(resp, AicZones.class,null); + return aaiAicZones; + } + + @Override + public AaiResponse getSubscriberData(String subscriberId) { + + File certiPath = getCertificatesFile(); + String depth = "2"; + AaiResponse subscriberDataResponse; + Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + subscriberId + "?depth=" + depth, false); + subscriberDataResponse = proccessAaiResponse(resp, Services.class,null); + return subscriberDataResponse; + } + + @Override + public AaiResponse getServices() { + File certiPath = getCertificatesFile(); + Response resp = doAaiGet(certiPath.getAbsolutePath(), "service-design-and-creation/services", false); + AaiResponse getServicesResponse = proccessAaiResponse(resp, GetServicesAAIRespone.class,null); + + return getServicesResponse; + } + + @Override + public AaiResponse getTenants(String globalCustomerId, String serviceType) { + File certiPath = getCertificatesFile(); + String url = "business/customers/customer/" + globalCustomerId + "/service-subscriptions/service-subscription/" + serviceType; + + Response resp = doAaiGet(certiPath.getAbsolutePath(), url, false); + String responseAsString = parseForTenantsByServiceSubscription(resp.readEntity(String.class)); + + AaiResponse getTenantsResponse = proccessAaiResponse(resp, GetTenantsResponse[].class,responseAsString); + return getTenantsResponse; + } + + private AaiResponse proccessAaiResponse(Response resp, Class classType,String responseBody) { + AaiResponse subscriberDataResponse; + if (resp == null) { + subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "Invalid response from AAI"); + } else { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "getSubscribers() resp=" + resp.getStatusInfo().toString()); + if (resp.getStatus() != HttpStatus.SC_OK) { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "Invalid response from AAI"); + subscriberDataResponse = new AaiResponse<>(null, resp.readEntity(String.class), resp.getStatus()); + } else { + String finalResponse; + try { + if (responseBody != null){ + finalResponse = responseBody; + } + else{ + finalResponse = resp.readEntity(String.class);; + } + + subscriberDataResponse = new AaiResponse<>((new ObjectMapper().readValue(finalResponse, classType)), null, HttpStatus.SC_OK); + + } catch (IOException e) { + subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + + } + } + return subscriberDataResponse; + } + + private File getCertificatesFile() { + if (servletContext != null) + return new File(servletContext.getRealPath("/WEB-INF/cert/")); + return null; + } + + @SuppressWarnings("all") + protected Response doAaiGet(String certiPath, String uri, boolean xml) { + String methodName = "getSubscriberList"; + String transId = UUID.randomUUID().toString(); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + Response resp = null; + try { + + AAIRestInterface restContrller = new AAIRestInterface(certiPath); + resp = restContrller.RestGet(fromAppId, transId, uri, xml); + + } catch (WebApplicationException e) { + final String message = ((BadRequestException) e).getResponse().readEntity(String.class); + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + } + + return resp; + } + + private String parseForTenantsByServiceSubscription(String resp) { + String tenantList = ""; + + try { + JSONParser jsonParser = new JSONParser(); + + JSONObject jsonObject = (JSONObject) jsonParser.parse(resp); + + return parseServiceSubscriptionObjectForTenants(jsonObject); + } catch (Exception ex) { + + } + + return tenantList; + } + + + + public static String parseServiceSubscriptionObjectForTenants(JSONObject jsonObject) { + + JSONArray tenantArray = new JSONArray(); + boolean bconvert = false; + + try { + JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list"); + if (relationShipListsObj != null) { + JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship"); + if (rShipArray != null) { + Iterator i1 = rShipArray.iterator(); + + while (i1.hasNext()) { + + JSONObject inner1Obj = (JSONObject) i1.next(); + + if (inner1Obj == null) + continue; + + String relatedTo = checkForNull((String) inner1Obj.get("related-to")); + if (relatedTo.equalsIgnoreCase("tenant")) { + JSONObject tenantNewObj = new JSONObject(); + + String relatedLink = checkForNull((String) inner1Obj.get("related-link")); + tenantNewObj.put("link", relatedLink); + + JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data"); + if (rDataArray != null) { + Iterator i2 = rDataArray.iterator(); + + while (i2.hasNext()) { + JSONObject inner2Obj = (JSONObject) i2.next(); + + if (inner2Obj == null) + continue; + + String rShipKey = checkForNull((String) inner2Obj.get("relationship-key")); + String rShipVal = checkForNull((String) inner2Obj.get("relationship-value")); + if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) { + tenantNewObj.put("cloudOwner", rShipVal); + } else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) { + tenantNewObj.put("cloudRegionID", rShipVal); + } + + if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) { + tenantNewObj.put("tenantID", rShipVal); + } + } + } + + JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property"); + if (relatedTPropArray != null) { + Iterator i3 = relatedTPropArray.iterator(); + + while (i3.hasNext()) { + JSONObject inner3Obj = (JSONObject) i3.next(); + + if (inner3Obj == null) + continue; + + String propKey = checkForNull((String) inner3Obj.get("property-key")); + String propVal = checkForNull((String) inner3Obj.get("property-value")); + if (propKey.equalsIgnoreCase("tenant.tenant-name")) { + tenantNewObj.put("tenantName", propVal); + } + } + } + bconvert = true; + tenantArray.add(tenantNewObj); + } + } + + } + } + } catch (NullPointerException ex) { + + + } + + if (bconvert) + return tenantArray.toJSONString(); + else + return ""; + + } + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClientInterface.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClientInterface.java new file mode 100644 index 00000000..fdaf602f --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiClientInterface.java @@ -0,0 +1,22 @@ +package org.openecomp.vid.aai; + +import org.openecomp.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.openecomp.vid.model.SubscriberList; + +import java.util.List; + +/** + * Created by Oren on 7/4/17. + */ +public interface AaiClientInterface { + + AaiResponse getAllSubscribers(); + + AaiResponse getSubscriberData(String subscriberId); + + AaiResponse getServices(); + + AaiResponse getTenants(String globalCustomerId, String serviceType); + + AaiResponse getAllAicZones(); +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiResponse.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiResponse.java new file mode 100644 index 00000000..337d1371 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/AaiResponse.java @@ -0,0 +1,21 @@ +package org.openecomp.vid.aai; + +import org.openecomp.vid.model.ProxyResponse; + +/** + * Created by Oren on 7/10/17. + */ +public class AaiResponse extends ProxyResponse{ + + T t; + + public AaiResponse(T t, String errorMessage, int aaiHttpCode) { + this.t = t; + this.errorMessage = errorMessage; + this.httpCode = aaiHttpCode; + } + + public T getT() { + return t; + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstance.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstance.java new file mode 100644 index 00000000..45f7947e --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstance.java @@ -0,0 +1,33 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceInstance { + + @JsonProperty("service-instance-id") + public String serviceInstanceId; + + @JsonProperty("service-instance-name") + public String serviceInstanceName; + + @JsonProperty("persona-model-id") + public String personaModelId; + + @JsonProperty("persona-model-version") + public String personaModelVersion; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("orchestration-status") + public String orchestrationStatus; + + @JsonProperty("model-invariant-id") + public String modelInvariantId; + + @JsonProperty("model-version-id") + public String modelVersionId; + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstances.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstances.java new file mode 100644 index 00000000..063317e4 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceInstances.java @@ -0,0 +1,14 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceInstances { + + @JsonProperty("service-instance") + public List serviceInstance; + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscription.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscription.java new file mode 100644 index 00000000..6a178d97 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscription.java @@ -0,0 +1,20 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class ServiceSubscription { + + @JsonProperty("service-type") + public String serviceType; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("service-instances") + public ServiceInstances serviceInstances; + + @JsonProperty("is-permitted") + public boolean isPermitted =false; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscriptions.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscriptions.java new file mode 100644 index 00000000..eaadba2c --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/ServiceSubscriptions.java @@ -0,0 +1,17 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.util.List; + +/** + * Created by Oren on 7/9/17. + */ +@JsonIgnoreProperties(ignoreUnknown = true) + +public class ServiceSubscriptions { + + @JsonProperty("service-subscription") + public List serviceSubscription; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/Services.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/Services.java new file mode 100644 index 00000000..e8148a92 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/Services.java @@ -0,0 +1,24 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Services { + @JsonProperty("global-customer-id") + public String globalCustomerId; + + @JsonProperty("subscriber-name") + public String subscriberName; + + @JsonProperty("subscriber-type") + public String subscriberType; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("service-subscriptions") + public ServiceSubscriptions serviceSubscriptions; + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberAaiResponse.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberAaiResponse.java new file mode 100644 index 00000000..53304e93 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberAaiResponse.java @@ -0,0 +1,24 @@ +package org.openecomp.vid.aai; + +import org.openecomp.vid.model.ProxyResponse; +import org.openecomp.vid.model.SubscriberList; + +/** + * Created by Oren on 7/5/17. + */ +public class SubscriberAaiResponse extends ProxyResponse { + + + private SubscriberList subscriberList; + + public SubscriberAaiResponse(SubscriberList subscriberList, String errorMessage, int aaiHttpCode) { + this.subscriberList = subscriberList; + this.errorMessage = errorMessage; + this.httpCode = aaiHttpCode; + } + + + public SubscriberList getSubscriberList() { + return subscriberList; + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberData.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberData.java new file mode 100644 index 00000000..efaa4788 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberData.java @@ -0,0 +1,7 @@ +package org.openecomp.vid.aai; + +/** + * Created by Oren on 7/10/17. + */ +public class SubscriberData { +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberFilteredResults.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberFilteredResults.java new file mode 100644 index 00000000..c2afee33 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberFilteredResults.java @@ -0,0 +1,29 @@ +package org.openecomp.vid.aai; + +import org.openecomp.vid.model.ProxyResponse; +import org.openecomp.vid.model.SubscriberList; +import org.openecomp.vid.roles.RoleValidator; + +/** + * Created by Oren on 7/5/17. + */ + +public class SubscriberFilteredResults extends ProxyResponse { + + private SubscriberListWithFilterData subscriberList; + + public SubscriberFilteredResults(RoleValidator roleValidator,SubscriberList subscribers, String errorMessage, int aaiHttpCode) { + this.subscriberList = new SubscriberListWithFilterData(subscribers,roleValidator); + this.errorMessage = errorMessage; + this.httpCode = aaiHttpCode; + } + + + public SubscriberListWithFilterData getSubscriberList() { + return subscriberList; + } + + public void setSubscriberList(SubscriberListWithFilterData subscriberList) { + this.subscriberList = subscriberList; + } +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberListWithFilterData.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberListWithFilterData.java new file mode 100644 index 00000000..75f505f2 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberListWithFilterData.java @@ -0,0 +1,31 @@ +package org.openecomp.vid.aai; + +import org.openecomp.vid.model.Subscriber; +import org.openecomp.vid.model.SubscriberList; +import org.openecomp.vid.roles.RoleValidator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Oren on 7/5/17. + */ +public class SubscriberListWithFilterData { + + public SubscriberListWithFilterData(SubscriberList subscriberList, RoleValidator roleValidator){ + List subscribers = subscriberList.customer; + List subscribersWithFilter = new ArrayList<>(); + for (Subscriber subscriber :subscribers){ + SubscriberWithFilter subscriberWithFilter = new SubscriberWithFilter(); + subscriberWithFilter.setIsPermitted(roleValidator.isSubscriberPermitted(subscriber.globalCustomerId)); + subscriberWithFilter.subscriberType = subscriber.subscriberType; + subscriberWithFilter.resourceVersion = subscriber.resourceVersion; + subscriberWithFilter.subscriberName = subscriber.subscriberName; + subscriberWithFilter.globalCustomerId = subscriber.globalCustomerId; + subscribersWithFilter.add(subscriberWithFilter); + } + this.customer = subscribersWithFilter; + } + + public List customer; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberWithFilter.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberWithFilter.java new file mode 100644 index 00000000..abd9c0f3 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/SubscriberWithFilter.java @@ -0,0 +1,21 @@ +package org.openecomp.vid.aai; + +import org.codehaus.jackson.annotate.JsonProperty; +import org.openecomp.vid.model.Subscriber; + +/** + * Created by Oren on 7/5/17. + */ +public class SubscriberWithFilter extends Subscriber{ + + @JsonProperty("is-permitted") + private boolean isPermitted; + + public boolean getIsPermitted() { + return isPermitted; + } + + public void setIsPermitted(boolean isPermitted) { + this.isPermitted = isPermitted; + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/exceptions/InvalidAAIResponseException.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/exceptions/InvalidAAIResponseException.java new file mode 100644 index 00000000..f84c09af --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/exceptions/InvalidAAIResponseException.java @@ -0,0 +1,7 @@ +package org.openecomp.vid.aai.exceptions; + +/** + * Created by Oren on 7/4/17. + */ +public class InvalidAAIResponseException extends Exception { +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/GetServicesAAIRespone.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/GetServicesAAIRespone.java new file mode 100644 index 00000000..27c38ce8 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/GetServicesAAIRespone.java @@ -0,0 +1,11 @@ +package org.openecomp.vid.aai.model.AaiGetServicesRequestModel; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.util.List; +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetServicesAAIRespone { + + public List service; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/Service.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/Service.java new file mode 100644 index 00000000..977e57e6 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetServicesRequestModel/Service.java @@ -0,0 +1,21 @@ +package org.openecomp.vid.aai.model.AaiGetServicesRequestModel; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Created by Oren on 7/17/17. + */ + +@JsonIgnoreProperties(ignoreUnknown = true) +public class Service { + @JsonProperty("service-id") + public String serviceId; + @JsonProperty("service-description") + public String serviceDescription; + @JsonProperty("resource-version") + public String resourceVersion; + @JsonProperty("is-permitted") + public boolean isPermitted; + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetTenatns/GetTenantsResponse.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetTenatns/GetTenantsResponse.java new file mode 100644 index 00000000..5e88bf37 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/AaiGetTenatns/GetTenantsResponse.java @@ -0,0 +1,26 @@ +package org.openecomp.vid.aai.model.AaiGetTenatns; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Created by Oren on 7/18/17. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetTenantsResponse { + + @JsonProperty("cloudRegionID") + public String cloudRegionId; + + @JsonProperty("tenantName") + public String tenantName; + + @JsonProperty("tenantID") + public String tenantID; + + @JsonProperty("is-permitted") + public boolean isPermitted; + + + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstance.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstance.java new file mode 100644 index 00000000..9fe4c06b --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstance.java @@ -0,0 +1,26 @@ +package org.openecomp.vid.aai.model; + +import org.codehaus.jackson.annotate.JsonProperty; + +public class ServiceInstance { + + @JsonProperty("service-instance-id") + public String serviceInstanceId; + + @JsonProperty("service-instance-name") + public String serviceInstanceName; + + @JsonProperty("persona-model-id") + public String personaModelId; + + @JsonProperty("persona-model-version") + public String personaModelVersion; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("orchestration-status") + public String orchestrationStatus; + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstances.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstances.java new file mode 100644 index 00000000..163ed045 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceInstances.java @@ -0,0 +1,12 @@ +package org.openecomp.vid.aai.model; + +import java.util.List; + +import org.codehaus.jackson.annotate.JsonProperty; + +public class ServiceInstances { + + @JsonProperty("service-instance") + public List serviceInstance; + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceSubscription.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceSubscription.java new file mode 100644 index 00000000..02ddfd17 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/ServiceSubscription.java @@ -0,0 +1,17 @@ +package org.openecomp.vid.aai.model; + +import org.codehaus.jackson.annotate.JsonProperty; + +public class ServiceSubscription { + + @JsonProperty("service-type") + public String serviceType; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("service-instances") + public ServiceInstances serviceInstances; + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/aai/model/Services.java b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/Services.java new file mode 100644 index 00000000..6e7b8907 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/aai/model/Services.java @@ -0,0 +1,24 @@ +package org.openecomp.vid.aai.model; + +import java.util.List; + +import org.codehaus.jackson.annotate.JsonProperty; + +public class Services { + @JsonProperty("global-customer-id") + public String globalCustomerId; + + @JsonProperty("subscriber-name") + public String subscriberName; + + @JsonProperty("subscriber-type") + public String subscriberType; + + @JsonProperty("resource-version") + public String resourceVersion; + + @JsonProperty("service-subscriptions") + public List serviceSubscriptions; + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureService.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureService.java new file mode 100644 index 00000000..1372472f --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureService.java @@ -0,0 +1,9 @@ +package org.openecomp.vid.asdc.beans; + +/** + * Created by Oren on 6/27/17. + */ +public class SecureService extends Service{ + + public boolean isPermmited = true; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureServices.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureServices.java new file mode 100644 index 00000000..0d2c2fba --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/SecureServices.java @@ -0,0 +1,30 @@ +package org.openecomp.vid.asdc.beans; + +import java.util.Collection; +import java.util.List; + +/** + * Created by Oren on 6/27/17. + */ +public class SecureServices { + + private Collection services; + private boolean isReadOnly = true; + + public void setServices(Collection services) { + this.services = services; + } + + public Collection getServices() { + + return services; + } + public boolean isReadOnly() { + return isReadOnly; + } + + public void setReadOnly(boolean readOnly) { + isReadOnly = readOnly; + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java index d42c1f15..41c7ca5b 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/beans/tosca/ToscaMetadata.java @@ -61,6 +61,9 @@ public class ToscaMetadata { /** The resource vendor release. */ private String resourceVendorRelease; + /** the resourceVendorModelNumber */ + private String resourceVendorModelNumber; + /** The service ecomp naming. */ private String serviceEcompNaming; @@ -88,6 +91,11 @@ public class ToscaMetadata { /** The vf module model version. */ private String vfModuleModelVersion; + + /** serviceType */ + private String serviceType; + /** serviceRole */ + private String serviceRole; /** * Instantiates a new tosca metadata. @@ -458,4 +466,26 @@ public class ToscaMetadata { return vfModuleModelCustomizationUUID; } + + /** serviceType */ + public String getServiceType() { + return serviceType; + } + public void setServiceType(String serviceType) { + this.serviceType= serviceType; + } + /** serviceRole */ + public String getServiceRole() { + return serviceRole; + } + public void setServiceRole(String serviceRole) { + this.serviceRole= serviceRole; + } + /** resourceVendorModelNumber */ + public String getResourceVendorModelNumber() { + return resourceVendorModelNumber; + } + public void setResourceVendorModelNumber(String resourceVendorModelNumber) { + this.resourceVendorModelNumber= resourceVendorModelNumber; + } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java index 8538f6ba..d73c75b3 100644 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/local/LocalAsdcClient.java @@ -264,7 +264,7 @@ public class LocalAsdcClient implements AsdcClient { * @see org.openecomp.vid.asdc.AsdcClient#getResourceArtifact(java.util.UUID, java.util.UUID) */ public Artifact getResourceArtifact(UUID resourceUuid, UUID artifactUuid) throws AsdcCatalogException { - final JSONArray artifacts = getCatalog().getJSONObject("resources") + final JSONArray artifacts = getCatalog().getJSONObject("resources") .getJSONObject(resourceUuid.toString()) .getJSONArray("artifacts"); diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java index 4819cae8..3f0dd8c3 100644 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/parser/ToscaParserImpl2.java @@ -7,7 +7,6 @@ import org.openecomp.sdc.tosca.parser.impl.SdcToscaParserFactory; import org.openecomp.sdc.toscaparser.api.Group; import org.openecomp.sdc.toscaparser.api.NodeTemplate; import org.openecomp.sdc.toscaparser.api.Property; -import org.openecomp.sdc.toscaparser.api.elements.constraints.Constraint; import org.openecomp.sdc.toscaparser.api.parameters.Input; import org.openecomp.vid.asdc.beans.Service; import org.openecomp.vid.model.*; @@ -23,8 +22,11 @@ public class ToscaParserImpl2 { public class Constants { public final static String uuid = "UUID"; public final static String description = "description"; - public final static String ecompGeneratedNaming = "ecompGeneratedNaming"; - public final static String customizationUUID = "customizationUUID"; + public final static String serviceType = "serviceType"; + public final static String serviceRole = "serviceRole"; + public final static String ecompGeneratedNaming = "ecompGeneratedNaming"; + + public final static String customizationUUID = "customizationUUID"; public final static String vfModuleModelVersion = "vfModuleModelVersion"; public final static String vfModuleModelCustomizationUUID = "vfModuleModelCustomizationUUID"; public final static String volume_group = "volume_group"; @@ -49,7 +51,7 @@ public class ToscaParserImpl2 { serviceModel.setService(extractServiceFromCsar(asdcServiceMetadata, sdcCsarHelper)); serviceModel.setVolumeGroups(extractVolumeGroups(sdcCsarHelper)); serviceModel.setVfModules(extractVfModuleFromCsar(sdcCsarHelper)); - serviceModel.setVnfs(extractVnfsFromCsar(sdcCsarHelper)); + serviceModel.setVnfs(extractVnfsFromCsar(serviceModel,sdcCsarHelper)); serviceModel.setNetworks(extractNetworksFromCsar(sdcCsarHelper)); return serviceModel; } @@ -65,22 +67,52 @@ public class ToscaParserImpl2 { service.setDescription(csarHelper.getServiceMetadata().getValue(Constants.description)); service.setInputs(inputsListToInputsMap(csarHelper.getServiceInputs())); service.setServiceEcompNaming(csarHelper.getServiceMetadata().getValue(Constants.ecompGeneratedNaming)); + service.setServiceType(csarHelper.getServiceMetadata().getValue(Constants.serviceType)); + service.setServiceRole(csarHelper.getServiceMetadata().getValue(Constants.serviceRole)); + return service; } - private Map extractVnfsFromCsar(ISdcCsarHelper csarHelper) { + private Map extractVnfsFromCsar(ServiceModel serviceModel,ISdcCsarHelper csarHelper) { List nodeTemplates = csarHelper.getServiceVfList(); Map vnfsMaps = new HashMap(); for (NodeTemplate nodeTemplate : nodeTemplates) { VNF vnf = new VNF(); populateNodeFromNodeTemplate(nodeTemplate, csarHelper, vnf); + vnf.setModelCustomizationName(nodeTemplate.getName()); + Map vfModuleHashMap = getVfModulesFromVF(csarHelper, vnf.getCustomizationUuid()); + vnf.setVfModules(vfModuleHashMap); + + Map volumeGroupMap = getVolumeGroupsFromVF(csarHelper, vnf.getCustomizationUuid()); + vnf.setVolumeGroups(volumeGroupMap); + vnfsMaps.put(nodeTemplate.getName(), vnf); } return vnfsMaps; } + private Map getVfModulesFromVF(ISdcCsarHelper csarHelper, String vfUuid) { + Map vfModuleHashMap = new HashMap(); + for (Group group : csarHelper.getVfModulesByVf(vfUuid)) { + vfModuleHashMap.put(group.getName(), populateVfModuleFromGroup(group)); + } + return vfModuleHashMap; + } + + private Map getVolumeGroupsFromVF(ISdcCsarHelper csarHelper, String vfCustomizationUuid) { + Map volumeGroupMap = new HashMap(); + List groups = csarHelper.getVfModulesByVf(vfCustomizationUuid); + for (Group group : groups) { + boolean isVolumeGroup = Boolean.valueOf(group.getPropertyValue(Constants.volume_group).toString()); + if (isVolumeGroup) { + volumeGroupMap.put(group.getName(), populateVolumeGroupFromGroup(group)); + } + } + return volumeGroupMap; + } + private Map extractNetworksFromCsar(ISdcCsarHelper csarHelper) { List nodeTemplates = csarHelper.getServiceVlList(); Map networksMap = new HashMap(); @@ -99,25 +131,19 @@ public class ToscaParserImpl2 { HashMap vfModuleHashMap = new HashMap<>(); for (NodeTemplate nodeTemplate : serviceVfList) { - List groups = csarHelper.getVfModulesByVf(nodeTemplate.getMetaData().getValue(Constants.customizationUUID)); - for (Group group : groups) { - vfModuleHashMap.put(group.getName(), populateVfModuleFromGroup(group)); - } + Map nodeTemplateVfModule = + getVfModulesFromVF(csarHelper, nodeTemplate.getMetaData().getValue(Constants.customizationUUID)); + vfModuleHashMap.putAll(nodeTemplateVfModule); } return vfModuleHashMap; } - private Map extractVolumeGroups(ISdcCsarHelper csarHelper) { HashMap volumeGroupHashMap = new HashMap<>(); for (NodeTemplate nodeTemplate : csarHelper.getServiceVfList()) { - List groups = csarHelper.getVfModulesByVf(csarHelper.getNodeTemplateCustomizationUuid(nodeTemplate)); - for (Group group : groups) { - boolean isVolumeGroup = Boolean.valueOf(group.getPropertyValue(Constants.volume_group).toString()); - if (isVolumeGroup) { - volumeGroupHashMap.put(group.getName(), populateVolumeGroupFromGroup(group)); - } - } + Map nodeTemplateVolumeGroups = + getVolumeGroupsFromVF(csarHelper, csarHelper.getNodeTemplateCustomizationUuid(nodeTemplate)); + volumeGroupHashMap.putAll(nodeTemplateVolumeGroups); } return volumeGroupHashMap; } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java b/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java index 5b783f5c..67375aa6 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/asdc/rest/RestfulAsdcClient.java @@ -57,6 +57,7 @@ import java.util.UUID; /** * The Class RestfulAsdcClient. */ +@SuppressWarnings("Duplicates") public class RestfulAsdcClient implements AsdcClient { /** @@ -183,6 +184,7 @@ public class RestfulAsdcClient implements AsdcClient { * @see org.openecomp.vid.asdc.AsdcClient#getResource(java.util.UUID) */ public Resource getResource(UUID uuid) throws AsdcCatalogException { + String path = VidProperties.getPropertyWithDefault(ModelConstants.ASDC_RESOURCE_API_PATH, ModelConstants.DEFAULT_ASDC_RESOURCE_API_PATH); try { return getClient() diff --git a/vid-app-common/src/main/java/org/openecomp/vid/client/FakeHttpSession.java b/vid-app-common/src/main/java/org/openecomp/vid/client/FakeHttpSession.java new file mode 100644 index 00000000..226e6c4d --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/client/FakeHttpSession.java @@ -0,0 +1,224 @@ +package org.openecomp.vid.client; + +import org.apache.commons.io.IOUtils; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by pickjonathan on 03/07/2017. + */ +public class FakeHttpSession implements HttpSession { + + /** + * Setup the creation time + */ + public FakeHttpSession() { + File file = new File("resources/roles.json"); + + String rolesInputStream = null; + try { + rolesInputStream = IOUtils.toString(FakeHttpSession.class.getClassLoader().getResourceAsStream("roles.json"),"UTF8"); + } catch (IOException e) { + e.printStackTrace(); + } + JSONTokener tokener = new JSONTokener(rolesInputStream); + JSONObject roles = new JSONObject(tokener); + + JSONArray rolesArray = roles.getJSONArray("roles"); + + //set permissions to the roles from file. + this.setAttribute("role", rolesArray); + + creationTime = System.currentTimeMillis(); + } + + + /** + * Setup the creation time + * @param id The new session id + */ + public FakeHttpSession(String id) + { + this.id = id; + creationTime = System.currentTimeMillis(); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getCreationTime() + */ + public long getCreationTime() + { + return creationTime; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getId() + */ + public String getId() + { + if (id == null) + { + System.out.println("Inventing data in FakeHttpSession.getId() to remain plausible."); + id = "fake"; + } + + return id; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getLastAccessedTime() + */ + public long getLastAccessedTime() + { + return creationTime; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getServletContext() + */ + public ServletContext getServletContext() + { + return null; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int) + */ + public void setMaxInactiveInterval(int maxInactiveInterval) + { + this.maxInactiveInterval = maxInactiveInterval; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() + */ + public int getMaxInactiveInterval() + { + return maxInactiveInterval; + } + + /** + * @see javax.servlet.http.HttpSession#getSessionContext() + * @deprecated + */ + @SuppressWarnings({"UnnecessaryFullyQualifiedName"}) + @Deprecated + public javax.servlet.http.HttpSessionContext getSessionContext() + { + return null; + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) + */ + public Object getAttribute(String name) + { + return attributes.get(name); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getValue(java.lang.String) + */ + @Deprecated + public Object getValue(String name) + { + return attributes.get(name); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getAttributeNames() + */ + public Enumeration getAttributeNames() + { + return Collections.enumeration(attributes.keySet()); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#getValueNames() + */ + @Deprecated + public String[] getValueNames() + { + return attributes.keySet().toArray(new String[attributes.keySet().size()]); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object) + */ + public void setAttribute(String name, Object value) + { + attributes.put(name, value); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object) + */ + @Deprecated + public void putValue(String name, Object value) + { + attributes.put(name, value); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String) + */ + public void removeAttribute(String name) + { + attributes.remove(name); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#removeValue(java.lang.String) + */ + @Deprecated + public void removeValue(String name) + { + attributes.remove(name); + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#invalidate() + */ + public void invalidate() + { + } + + /* (non-Javadoc) + * @see javax.servlet.http.HttpSession#isNew() + */ + public boolean isNew() + { + return true; + } + + /** + * The session id + */ + private String id = null; + + /** + * The list of attributes + */ + private Map attributes = new HashMap(); + + /** + * When were we created + */ + private long creationTime; + + /** + * How long before we timeout? + */ + private int maxInactiveInterval = 30 * 60 * 1000; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/client/HttpsBasicClient.java b/vid-app-common/src/main/java/org/openecomp/vid/client/HttpsBasicClient.java index dc0e3559..676a7e79 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/client/HttpsBasicClient.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/client/HttpsBasicClient.java @@ -95,7 +95,7 @@ public class HttpsBasicClient{ //May need to make the algorithm a parameter. MSO requires TLSv1.1 or TLSv1.2 ctx = SSLContext.getInstance("TLSv1.2"); - /* + /* KeyManagerFactory kmf = null; try { kmf = KeyManagerFactory.getInstance("SunX509"); @@ -113,7 +113,7 @@ public class HttpsBasicClient{ ctx.init(kmf.getKeyManagers(), null, null); */ ctx.init(null, null, null); - //config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, + //config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, // new HTTPSProperties( , ctx)); return ClientBuilder.newBuilder() diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java index 861ddf8d..c0b6a514 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/AaiController.java @@ -18,31 +18,27 @@ * ============LICENSE_END========================================================= */ -package org.openecomp.vid.controller; - -import java.io.File; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import javax.ws.rs.BadRequestException; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; +package org.openecomp.vid.controller; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.openecomp.aai.util.AAIRestInterface; +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.domain.User; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; +import org.openecomp.vid.aai.AaiResponse; +import org.openecomp.vid.aai.SubscriberData; +import org.openecomp.vid.aai.SubscriberFilteredResults; +import org.openecomp.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.openecomp.vid.roles.Role; +import org.openecomp.vid.roles.RoleProvider; +import org.openecomp.vid.roles.RoleValidator; +import org.openecomp.vid.services.AaiService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -53,35 +49,292 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; -import org.openecomp.portalsdk.core.controller.RestrictedBaseController; -import org.openecomp.portalsdk.core.domain.User; -import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.openecomp.portalsdk.core.util.SystemProperties; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.ws.rs.BadRequestException; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import java.io.File; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; /** * Controller to handle a&ai requests. */ @RestController -public class AaiController extends RestrictedBaseController{ +public class AaiController extends RestrictedBaseController { + + public AaiController() { + + } +public AaiController(ServletContext servletContext) { + this.servletContext = servletContext; + + } + + + - /** The view name. */ + /** + * The Constant dateFormat. + */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + /** + * The from app id. + */ + protected String fromAppId = "VidAaiController"; + /** + * The view name. + */ String viewName; - - /** The logger. */ + /** + * The logger. + */ EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AaiController.class); - /** The Constant dateFormat. */ - final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); - /** The from app id. */ - protected String fromAppId = "VidAaiController"; + /** + * The model. + */ + private Map model = new HashMap(); + /** + * The servlet context. + */ + @Autowired + private ServletContext servletContext; + + /** + * aai service + */ + @Autowired + private AaiService aaiService; + + + + /** + * Return tenant details. + * + * @param jsonObject the json object + * @return String The parsing results + */ + public static String parseCustomerObjectForTenants(JSONObject jsonObject) { + + JSONArray tenantArray = new JSONArray(); + boolean bconvert = false; + + try { + + JSONObject serviceSubsObj = (JSONObject) jsonObject.get("service-subscriptions"); + + if (serviceSubsObj != null) { + JSONArray srvcSubArray = (JSONArray) serviceSubsObj.get("service-subscription"); + + if (srvcSubArray != null) { + Iterator i = srvcSubArray.iterator(); + + while (i.hasNext()) { + + JSONObject innerObj = (JSONObject) i.next(); + + if (innerObj == null) + continue; + + JSONObject relationShipListsObj = (JSONObject) innerObj.get("relationship-list"); + if (relationShipListsObj != null) { + JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship"); + if (rShipArray != null) { + Iterator i1 = rShipArray.iterator(); + + while (i1.hasNext()) { + + JSONObject inner1Obj = (JSONObject) i1.next(); + + if (inner1Obj == null) + continue; + + String relatedTo = checkForNull((String) inner1Obj.get("related-to")); + if (relatedTo.equalsIgnoreCase("tenant")) { + JSONObject tenantNewObj = new JSONObject(); + + String relatedLink = checkForNull((String) inner1Obj.get("related-link")); + tenantNewObj.put("link", relatedLink); + + JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data"); + if (rDataArray != null) { + Iterator i2 = rDataArray.iterator(); + + while (i2.hasNext()) { + JSONObject inner2Obj = (JSONObject) i2.next(); + + if (inner2Obj == null) + continue; + + String rShipKey = checkForNull((String) inner2Obj.get("relationship-key")); + String rShipVal = checkForNull((String) inner2Obj.get("relationship-value")); + if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) { + tenantNewObj.put("cloudOwner", rShipVal); + } else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) { + tenantNewObj.put("cloudRegionID", rShipVal); + } + + if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) { + tenantNewObj.put("tenantID", rShipVal); + } + } + } + + JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property"); + if (relatedTPropArray != null) { + Iterator i3 = relatedTPropArray.iterator(); + + while (i3.hasNext()) { + JSONObject inner3Obj = (JSONObject) i3.next(); + + if (inner3Obj == null) + continue; + + String propKey = checkForNull((String) inner3Obj.get("property-key")); + String propVal = checkForNull((String) inner3Obj.get("property-value")); + if (propKey.equalsIgnoreCase("tenant.tenant-name")) { + tenantNewObj.put("tenantName", propVal); + } + } + } + bconvert = true; + tenantArray.add(tenantNewObj); + } + } + } + } + } + } + } + } catch (NullPointerException ex) { + + + } + + if (bconvert) + return tenantArray.toJSONString(); + else + return ""; + + } - /** The model. */ - private Map model = new HashMap(); - /** The servlet context. */ - private @Autowired ServletContext servletContext; + + /** + * Retrieve the service subscription from the jsonObject. + * + * @param jsonObject the json object + * @return String + */ + public static String parseServiceSubscriptionObjectForTenants(JSONObject jsonObject) { + + JSONArray tenantArray = new JSONArray(); + boolean bconvert = false; + + try { + JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list"); + if (relationShipListsObj != null) { + JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship"); + if (rShipArray != null) { + Iterator i1 = rShipArray.iterator(); + + while (i1.hasNext()) { + + JSONObject inner1Obj = (JSONObject) i1.next(); + + if (inner1Obj == null) + continue; + + String relatedTo = checkForNull((String) inner1Obj.get("related-to")); + if (relatedTo.equalsIgnoreCase("tenant")) { + JSONObject tenantNewObj = new JSONObject(); + + String relatedLink = checkForNull((String) inner1Obj.get("related-link")); + tenantNewObj.put("link", relatedLink); + + JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data"); + if (rDataArray != null) { + Iterator i2 = rDataArray.iterator(); + + while (i2.hasNext()) { + JSONObject inner2Obj = (JSONObject) i2.next(); + + if (inner2Obj == null) + continue; + + String rShipKey = checkForNull((String) inner2Obj.get("relationship-key")); + String rShipVal = checkForNull((String) inner2Obj.get("relationship-value")); + if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) { + tenantNewObj.put("cloudOwner", rShipVal); + } else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) { + tenantNewObj.put("cloudRegionID", rShipVal); + } + + if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) { + tenantNewObj.put("tenantID", rShipVal); + } + } + } + + JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property"); + if (relatedTPropArray != null) { + Iterator i3 = relatedTPropArray.iterator(); + + while (i3.hasNext()) { + JSONObject inner3Obj = (JSONObject) i3.next(); + + if (inner3Obj == null) + continue; + + String propKey = checkForNull((String) inner3Obj.get("property-key")); + String propVal = checkForNull((String) inner3Obj.get("property-value")); + if (propKey.equalsIgnoreCase("tenant.tenant-name")) { + tenantNewObj.put("tenantName", propVal); + } + } + } + bconvert = true; + tenantArray.add(tenantNewObj); + } + } + + } + } + } catch (NullPointerException ex) { + + + } + + if (bconvert) + return tenantArray.toJSONString(); + else + return ""; + + } + + /** + * Check for null. + * + * @param local the local + * @return the string + */ + private static String checkForNull(String local) { + if (local != null) + return local; + else + return ""; + + } /** * Welcome method. @@ -89,10 +342,18 @@ public class AaiController extends RestrictedBaseController{ * @param request the request * @return ModelAndView The view */ - @RequestMapping(value = {"/subscriberSearch" }, method = RequestMethod.GET) + @RequestMapping(value = {"/subscriberSearch"}, method = RequestMethod.GET) public ModelAndView welcome(HttpServletRequest request) { logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== AaiController welcome start"); - return new ModelAndView(getViewName()); + return new ModelAndView(getViewName()); + } + + @RequestMapping(value = {"/aai_get_aic_zones"}, method = RequestMethod.GET) + public ResponseEntity getAicZones(HttpServletRequest request) throws JsonGenerationException, JsonMappingException, IOException { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== getAicZones controller start"); + AaiResponse response = aaiService.getAaiZones(); + return aaiResponseToResponseEntity(response); + } /* (non-Javadoc) @@ -113,19 +374,17 @@ public class AaiController extends RestrictedBaseController{ * Get services from a&ai. * * @return ResponseEntity The response entity with the logged in user uuid. - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value = {"/getuserID" }, method = RequestMethod.GET) + @RequestMapping(value = {"/getuserID"}, method = RequestMethod.GET) public ResponseEntity getUserID(HttpServletRequest request) throws IOException, InterruptedException { String userId = ""; HttpSession session = request.getSession(); - if (session != null) - { + if (session != null) { User user = (User) session.getAttribute(SystemProperties.getProperty(SystemProperties.USER_ATTRIBUTE_NAME)); - if (user != null) - { + if (user != null) { //userId = user.getHrid(); userId = user.getLoginId(); if (userId == null) @@ -133,23 +392,39 @@ public class AaiController extends RestrictedBaseController{ } } - return new ResponseEntity( userId, HttpStatus.OK); + return new ResponseEntity(userId, HttpStatus.OK); } - /** * Get services from a&ai. * * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value="/aai_get_services",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity doGetServices() throws IOException, InterruptedException { - File certiPath = GetCertificatesPath(); - Response resp = doAaiGet(certiPath.getAbsolutePath(), "service-design-and-creation/services", false); + @RequestMapping(value = "/aai_get_services", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity doGetServices(HttpServletRequest request) throws IOException, InterruptedException { + + RoleValidator roleValidator = new RoleValidator(new RoleProvider().getUserRoles(request)); - return convertResponseToResponseEntity(resp); + AaiResponse subscriberList = aaiService.getServices(roleValidator); + ResponseEntity responseEntity = aaiResponseToResponseEntity( subscriberList); + + return responseEntity; + } + + + + private ResponseEntity aaiResponseToResponseEntity( AaiResponse aaiResponseData) + throws IOException, JsonGenerationException, JsonMappingException { + ResponseEntity responseEntity; + ObjectMapper objectMapper = new ObjectMapper(); + if (aaiResponseData.getHttpCode() == 200) { + responseEntity = new ResponseEntity(objectMapper.writeValueAsString(aaiResponseData.getT()), HttpStatus.OK); + } else { + responseEntity = new ResponseEntity(aaiResponseData.getErrorMessage(), HttpStatus.valueOf(aaiResponseData.getHttpCode())); + } + return responseEntity; } /** @@ -157,41 +432,40 @@ public class AaiController extends RestrictedBaseController{ * * @param serviceInstanceId the service instance Id * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value="/aai_get_service_instance/{service-instance-id}/{service-instance-type}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity doGetServiceInstance(@PathVariable("service-instance-id") String serviceInstanceId,@PathVariable("service-instance-type") String serviceInstanceType) throws IOException, InterruptedException { + @RequestMapping(value = "/aai_get_service_instance/{service-instance-id}/{service-instance-type}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity doGetServiceInstance(@PathVariable("service-instance-id") String serviceInstanceId, @PathVariable("service-instance-type") String serviceInstanceType) throws IOException, InterruptedException { File certiPath = GetCertificatesPath(); - Response resp=null; + Response resp = null; - if(serviceInstanceType.equalsIgnoreCase("Service Instance Id")){ - resp = doAaiGet( certiPath.getAbsolutePath(), - "search/nodes-query?search-node-type=service-instance&filter=service-instance-id:EQUALS:" + if (serviceInstanceType.equalsIgnoreCase("Service Instance Id")) { + resp = doAaiGet(certiPath.getAbsolutePath(), + "search/nodes-query?search-node-type=service-instance&filter=service-instance-id:EQUALS:" + serviceInstanceId, false); } else { - resp = doAaiGet( certiPath.getAbsolutePath(), - "search/nodes-query?search-node-type=service-instance&filter=service-instance-name:EQUALS:" + resp = doAaiGet(certiPath.getAbsolutePath(), + "search/nodes-query?search-node-type=service-instance&filter=service-instance-name:EQUALS:" + serviceInstanceId, false); } return convertResponseToResponseEntity(resp); } - /** * Get services from a&ai. * - * @param globalCustomerId the global customer id + * @param globalCustomerId the global customer id * @param serviceSubscriptionId the service subscription id * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value="/aai_get_service_subscription/{global-customer-id}/{service-subscription-id}",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/aai_get_service_subscription/{global-customer-id}/{service-subscription-id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity doGetServices(@PathVariable("global-customer-id") String globalCustomerId, - @PathVariable("service-subscription-id") String serviceSubscriptionId) throws IOException, InterruptedException { + @PathVariable("service-subscription-id") String serviceSubscriptionId) throws IOException, InterruptedException { File certiPath = GetCertificatesPath(); - Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + globalCustomerId + Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + globalCustomerId + "/service-subscriptions/service-subscription/" + serviceSubscriptionId + "?depth=0", false); return convertResponseToResponseEntity(resp); } @@ -201,23 +475,22 @@ public class AaiController extends RestrictedBaseController{ * * @param fullSet the full set * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value="/aai_get_subscribers",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity doGetSubscriberList(@DefaultValue("n") @QueryParam("fullSet") String fullSet) throws IOException, InterruptedException { - Response resp = getSubscribers(false); - return convertResponseToResponseEntity(resp); + @RequestMapping(value = "/aai_get_subscribers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity doGetSubscriberList(HttpServletRequest request,@DefaultValue("n") @QueryParam("fullSet") String fullSet) throws IOException, InterruptedException { + return getFullSubscriberList(request); } /** * Obtain the Target Prov Status from the System.Properties file. * * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception - */ - @RequestMapping(value="/get_system_prop_vnf_prov_status",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + */ + @RequestMapping(value = "/get_system_prop_vnf_prov_status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getTargetProvStatus() throws IOException, InterruptedException { String p = SystemProperties.getProperty("aai.vnf.provstatus"); return new ResponseEntity(p, HttpStatus.OK); @@ -225,17 +498,27 @@ public class AaiController extends RestrictedBaseController{ /** * Obtain the full subscriber list from a&ai. + *

+ * g @return ResponseEntity The response entity * - * @return ResponseEntity The response entity - * @throws IOException Signals that an I/O exception has occurred. + * @throws IOException Signals that an I/O exception has occurred. * @throws InterruptedException the interrupted exception */ - @RequestMapping(value="/aai_get_full_subscribers",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getFullSubscriberList() throws IOException, InterruptedException { - Response resp = getSubscribers(true); - return convertResponseToResponseEntity(resp); - } + @RequestMapping(value = "/aai_get_full_subscribers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getFullSubscriberList(HttpServletRequest request) throws IOException, InterruptedException { + ObjectMapper objectMapper = new ObjectMapper(); + ResponseEntity responseEntity; + RoleValidator roleValidator = new RoleValidator(new RoleProvider().getUserRoles(request)); + SubscriberFilteredResults subscriberList = aaiService.getFullSubscriberList(roleValidator); + if (subscriberList.getHttpCode() == 200) { + responseEntity = new ResponseEntity(objectMapper.writeValueAsString(subscriberList.getSubscriberList()), HttpStatus.OK); + } else { + responseEntity = new ResponseEntity(subscriberList.getErrorMessage(), HttpStatus.valueOf(subscriberList.getHttpCode())); + } + + return responseEntity; + } /** * Refresh the subscriber list from a&ai. @@ -243,7 +526,7 @@ public class AaiController extends RestrictedBaseController{ * @return ResponseEntity The response entity * @throws IOException Signals that an I/O exception has occurred. */ - @RequestMapping(value="/aai_refresh_subscribers",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/aai_refresh_subscribers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity doRefreshSubscriberList() throws IOException { Response resp = getSubscribers(false); return convertResponseToResponseEntity(resp); @@ -255,7 +538,7 @@ public class AaiController extends RestrictedBaseController{ * @return ResponseEntity The response entity * @throws IOException Signals that an I/O exception has occurred. */ - @RequestMapping(value="/aai_refresh_full_subscribers",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/aai_refresh_full_subscribers", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity doRefreshFullSubscriberList() throws IOException { Response resp = getSubscribers(false); return convertResponseToResponseEntity(resp); @@ -267,75 +550,88 @@ public class AaiController extends RestrictedBaseController{ * @param subscriberId the subscriber id * @return ResponseEntity The response entity */ - @RequestMapping(value="/aai_sub_details/{subscriberId}", method = RequestMethod.GET) - public ResponseEntity GetSubscriber(@PathVariable("subscriberId") String subscriberId) { - Response resp = getSubscriberDetails(subscriberId); - return convertResponseToResponseEntity(resp); + @RequestMapping(value = "/aai_sub_details/{subscriberId}", method = RequestMethod.GET) + public ResponseEntity GetSubscriberDetails(HttpServletRequest request,@PathVariable("subscriberId") String subscriberId) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + ResponseEntity responseEntity; + List roles = new RoleProvider().getUserRoles(request); + RoleValidator roleValidator = new RoleValidator(roles); + AaiResponse subscriberData = aaiService.getSubscriberData(subscriberId,roleValidator); + String httpMessage = subscriberData.getT() != null ? + objectMapper.writeValueAsString(subscriberData.getT()) : + subscriberData.getErrorMessage(); + + responseEntity = new ResponseEntity(httpMessage, HttpStatus.valueOf(subscriberData.getHttpCode())); + return responseEntity; } /** * Issue a named query to a&ai. * - * @param namedQueryId the named query id + * @param namedQueryId the named query id * @param globalCustomerId the global customer id - * @param serviceType the service type - * @param serviceInstance the service instance + * @param serviceType the service type + * @param serviceInstance the service instance * @return ResponseEntity The response entity */ - @RequestMapping(value="/aai_sub_viewedit/{namedQueryId}/{globalCustomerId}/{serviceType}/{serviceInstance}", method = RequestMethod.GET) + @RequestMapping(value = "/aai_sub_viewedit/{namedQueryId}/{globalCustomerId}/{serviceType}/{serviceInstance}", method = RequestMethod.GET) public ResponseEntity viewEditGetComponentList( @PathVariable("namedQueryId") String namedQueryId, @PathVariable("globalCustomerId") String globalCustomerId, @PathVariable("serviceType") String serviceType, - @PathVariable("serviceInstance") String serviceInstance) { + @PathVariable("serviceInstance") String serviceInstance) { String componentListPayload = getComponentListPutPayload(namedQueryId, globalCustomerId, serviceType, serviceInstance); File certiPath = GetCertificatesPath(); - Response resp = doAaiPost(certiPath.getAbsolutePath(), "search/named-query", componentListPayload, false); - return convertResponseToResponseEntity(resp); + Response resp = doAaiPost(certiPath.getAbsolutePath(), "search/named-query", componentListPayload, false); + return convertResponseToResponseEntity(resp); } + + // @RequestMapping(value="/aai_get_tenants/{global-customer-id}", method = RequestMethod.GET) + // public ResponseEntity viewEditGetComponentList( + // @PathVariable("global-customer-id") String globalCustomerId) { + // return new ResponseEntity(getTenants(globalCustomerId), HttpStatus.OK); + // } + /** * Issue a named query to a&ai. * - * @param namedQueryId the named query id + * @param namedQueryId the named query id * @param globalCustomerId the global customer id - * @param serviceType the service type + * @param serviceType the service type * @return ResponseEntity The response entity */ - @RequestMapping(value="/aai_get_models_by_service_type/{namedQueryId}/{globalCustomerId}/{serviceType}", method = RequestMethod.GET) + @RequestMapping(value = "/aai_get_models_by_service_type/{namedQueryId}/{globalCustomerId}/{serviceType}", method = RequestMethod.GET) public ResponseEntity viewEditGetComponentList( @PathVariable("namedQueryId") String namedQueryId, @PathVariable("globalCustomerId") String globalCustomerId, - @PathVariable("serviceType") String serviceType) { + @PathVariable("serviceType") String serviceType) { String componentListPayload = getModelsByServiceTypePayload(namedQueryId, globalCustomerId, serviceType); File certiPath = GetCertificatesPath(); - Response resp = doAaiPost(certiPath.getAbsolutePath(), "search/named-query", componentListPayload, false); - return convertResponseToResponseEntity(resp); + Response resp = doAaiPost(certiPath.getAbsolutePath(), "search/named-query", componentListPayload, false); + return convertResponseToResponseEntity(resp); } - + /** * Parses the for tenants. * * @param resp the resp * @return the string */ - private String parseForTenants(String resp) - { + private String parseForTenants(String resp) { String tenantList = ""; - try - { + try { JSONParser jsonParser = new JSONParser(); JSONObject jsonObject = (JSONObject) jsonParser.parse(resp); - return parseCustomerObjectForTenants(jsonObject); - } - catch (Exception ex) { + return parseCustomerObjectForTenants(jsonObject); + } catch (Exception ex) { } @@ -348,51 +644,59 @@ public class AaiController extends RestrictedBaseController{ * @param resp the resp * @return the string */ - private String parseForTenantsByServiceSubscription(String resp) - { + private String parseForTenantsByServiceSubscription(String resp) { String tenantList = ""; - try - { + try { JSONParser jsonParser = new JSONParser(); JSONObject jsonObject = (JSONObject) jsonParser.parse(resp); - return parseServiceSubscriptionObjectForTenants(jsonObject); - } - catch (Exception ex) { + return parseServiceSubscriptionObjectForTenants(jsonObject); + } catch (Exception ex) { } return tenantList; } - - // @RequestMapping(value="/aai_get_tenants/{global-customer-id}", method = RequestMethod.GET) - // public ResponseEntity viewEditGetComponentList( - // @PathVariable("global-customer-id") String globalCustomerId) { - // return new ResponseEntity(getTenants(globalCustomerId), HttpStatus.OK); - // } - /** * Obtain tenants for a given service type. * * @param globalCustomerId the global customer id - * @param serviceType the service type + * @param serviceType the service type * @return ResponseEntity The response entity */ - @RequestMapping(value="/aai_get_tenants/{global-customer-id}/{service-type}", method = RequestMethod.GET) - public ResponseEntity viewEditGetTenantsFromServiceType( + @RequestMapping(value = "/aai_get_tenants/{global-customer-id}/{service-type}", method = RequestMethod.GET) + public ResponseEntity viewEditGetTenantsFromServiceType(HttpServletRequest request, @PathVariable("global-customer-id") String globalCustomerId, @PathVariable("service-type") String serviceType) { - return getTenantsFromServiceType(globalCustomerId, serviceType); + + ResponseEntity responseEntity; + try { + ObjectMapper objectMapper = new ObjectMapper(); + List roles = new RoleProvider().getUserRoles(request); + RoleValidator roleValidator = new RoleValidator(roles); + AaiResponse response = aaiService.getTenants(globalCustomerId, serviceType, roleValidator); + if (response.getHttpCode() == 200) { + responseEntity = new ResponseEntity(objectMapper.writeValueAsString(response.getT()), HttpStatus.OK); + } else { + responseEntity = new ResponseEntity(response.getErrorMessage(), HttpStatus.valueOf(response.getHttpCode())); + } + } + catch (Exception e){ + responseEntity = new ResponseEntity("Unable to proccess getTenants reponse", HttpStatus.INTERNAL_SERVER_ERROR); + } + return responseEntity; } - private ResponseEntity convertResponseToResponseEntity(Response resp) { + + private ResponseEntity convertResponseToResponseEntity(Response resp) { ResponseEntity respEnt; - if (resp == null) { + ObjectMapper objectMapper = new ObjectMapper(); + if (resp == null) { respEnt = new ResponseEntity("Failed to fetch data from A&AI, check server logs for details.", HttpStatus.INTERNAL_SERVER_ERROR); - } else { - respEnt = new ResponseEntity((String)resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); + } else { + respEnt = new ResponseEntity(resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); } return respEnt; } @@ -403,18 +707,17 @@ public class AaiController extends RestrictedBaseController{ * @param globalCustomerId the global customer id * @return the tenants */ - private ResponseEntity getTenants(String globalCustomerId) - { + private ResponseEntity getTenants(String globalCustomerId) { File certiPath = GetCertificatesPath(); - Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + globalCustomerId, false); - - ResponseEntity respEnt; - if (resp.getStatus() >= 200 && resp.getStatus() <= 299) { - respEnt = new ResponseEntity(parseForTenants((String)resp.readEntity(String.class)), HttpStatus.OK); - } else { - respEnt = new ResponseEntity((String)resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); - } - return respEnt; + Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + globalCustomerId, false); + + ResponseEntity respEnt; + if (resp.getStatus() >= 200 && resp.getStatus() <= 299) { + respEnt = new ResponseEntity(parseForTenants((String) resp.readEntity(String.class)), HttpStatus.OK); + } else { + respEnt = new ResponseEntity((String) resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); + } + return respEnt; } @@ -422,23 +725,27 @@ public class AaiController extends RestrictedBaseController{ * Gets the tenants from service type. * * @param globalCustomerId the global customer id - * @param serviceType the service type + * @param serviceType the service type * @return the tenants from service type */ - private ResponseEntity getTenantsFromServiceType(String globalCustomerId, String serviceType) - { + private ResponseEntity getTenantsFromServiceType(String globalCustomerId, String serviceType) { + + + + + File certiPath = GetCertificatesPath(); - String url = "business/customers/customer/" + globalCustomerId + "/service-subscriptions/service-subscription/" + serviceType; + String url = "business/customers/customer/" + globalCustomerId + "/service-subscriptions/service-subscription/" + serviceType; - Response resp = doAaiGet(certiPath.getAbsolutePath(), url, false); - - ResponseEntity respEnt; - if (resp.getStatus() >= 200 && resp.getStatus() <= 299) { - respEnt = new ResponseEntity(parseForTenantsByServiceSubscription((String)resp.readEntity(String.class)), HttpStatus.OK); - } else { - respEnt = new ResponseEntity((String)resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); - } - return respEnt; + Response resp = doAaiGet(certiPath.getAbsolutePath(), url, false); + + ResponseEntity respEnt; + if (resp.getStatus() >= 200 && resp.getStatus() <= 299) { + respEnt = new ResponseEntity(parseForTenantsByServiceSubscription((String) resp.readEntity(String.class)), HttpStatus.OK); + } else { + respEnt = new ResponseEntity((String) resp.readEntity(String.class), HttpStatus.valueOf(resp.getStatus())); + } + return respEnt; } @@ -447,45 +754,40 @@ public class AaiController extends RestrictedBaseController{ * * @return the services */ - private Response getServices() - { + private Response getServices() { File certiPath = GetCertificatesPath(); - Response resp = doAaiGet(certiPath.getAbsolutePath(), "service-design-and-creation/services", false); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "getServices() resp=" + resp.getStatusInfo()); + Response resp = doAaiGet(certiPath.getAbsolutePath(), "service-design-and-creation/services", false); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "getServices() resp=" + resp.getStatusInfo()); //model.put("aai_get_services", resp); return resp; } - /** * Gets the subscribers. * * @param isFullSet the is full set * @return the subscribers */ - private Response getSubscribers(boolean isFullSet) - { + private Response getSubscribers(boolean isFullSet) { + File certiPath = GetCertificatesPath(); String depth = "0"; - Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers?subscriber-type=INFRA&depth=" + depth, false); - if (resp != null) { - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "getSubscribers() resp=" + resp.getStatusInfo().toString()); - } + Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers?subscriber-type=INFRA&depth=" + depth, false); + if (resp != null) { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "getSubscribers() resp=" + resp.getStatusInfo().toString()); + } return resp; } - - /** * Gets the subscriber details. * * @param subscriberId the subscriber id * @return the subscriber details */ - private Response getSubscriberDetails(String subscriberId) - { + private Response getSubscriberDetails(String subscriberId) { File certiPath = GetCertificatesPath(); Response resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + subscriberId + "?depth=2", false); //String resp = doAaiGet(certiPath.getAbsolutePath(), "business/customers/customer/" + subscriberId, false); @@ -498,10 +800,9 @@ public class AaiController extends RestrictedBaseController{ * * @return the file */ - private File GetCertificatesPath() - { + private File GetCertificatesPath() { if (servletContext != null) - return new File( servletContext.getRealPath("/WEB-INF/cert/") ); + return new File(servletContext.getRealPath("/WEB-INF/cert/")); return null; } @@ -509,12 +810,12 @@ public class AaiController extends RestrictedBaseController{ * Send a GET request to a&ai. * * @param certiPath the certi path - * @param uri the uri - * @param xml the xml + * @param uri the uri + * @param xml the xml * @return String The response */ protected Response doAaiGet(String certiPath, String uri, boolean xml) { - String methodName = "getSubscriberList"; + String methodName = "getSubscriberList"; String transId = UUID.randomUUID().toString(); logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); @@ -526,11 +827,11 @@ public class AaiController extends RestrictedBaseController{ } catch (WebApplicationException e) { final String message = ((BadRequestException) e).getResponse().readEntity(String.class); - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + message); } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); } return resp; @@ -540,13 +841,13 @@ public class AaiController extends RestrictedBaseController{ * Send a POST request to a&ai. * * @param certiPath the certi path - * @param uri the uri - * @param payload the payload - * @param xml the xml + * @param uri the uri + * @param payload the payload + * @param xml the xml * @return String The response */ protected Response doAaiPost(String certiPath, String uri, String payload, boolean xml) { - String methodName = "getSubscriberList"; + String methodName = "getSubscriberList"; String transId = UUID.randomUUID().toString(); logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); @@ -557,8 +858,8 @@ public class AaiController extends RestrictedBaseController{ resp = restContrller.RestPost(fromAppId, transId, uri, payload, xml); } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); } return resp; @@ -567,14 +868,14 @@ public class AaiController extends RestrictedBaseController{ /** * Gets the component list put payload. * - * @param namedQueryId the named query id + * @param namedQueryId the named query id * @param globalCustomerId the global customer id - * @param serviceType the service type - * @param serviceInstance the service instance + * @param serviceType the service type + * @param serviceInstance the service instance * @return the component list put payload */ private String getComponentListPutPayload(String namedQueryId, String globalCustomerId, String serviceType, String serviceInstance) { - return + return " {" + " \"instance-filters\": {" + " \"instance-filter\": [" + @@ -599,9 +900,10 @@ public class AaiController extends RestrictedBaseController{ "}"; } + private String getModelsByServiceTypePayload(String namedQueryId, String globalCustomerId, String serviceType) { // TODO Auto-generated method stub - return " {" + + return " {" + " \"instance-filters\": {" + " \"instance-filter\": [" + " {" + @@ -620,246 +922,6 @@ public class AaiController extends RestrictedBaseController{ " }" + " }" + "}"; - - } - - /** - * Return tenant details. - * - * @param jsonObject the json object - * @return String The parsing results - */ - public static String parseCustomerObjectForTenants(JSONObject jsonObject) { - - JSONArray tenantArray = new JSONArray(); - boolean bconvert = false; - - try { - - JSONObject serviceSubsObj = (JSONObject) jsonObject.get("service-subscriptions"); - - if (serviceSubsObj != null) - { - JSONArray srvcSubArray = (JSONArray) serviceSubsObj.get("service-subscription"); - - if (srvcSubArray != null) - { - Iterator i = srvcSubArray.iterator(); - - while (i.hasNext()) { - - JSONObject innerObj = (JSONObject) i.next(); - - if (innerObj == null) - continue; - - JSONObject relationShipListsObj = (JSONObject) innerObj.get("relationship-list"); - if (relationShipListsObj != null) - { - JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship"); - if (rShipArray != null) - { - Iterator i1 = rShipArray.iterator(); - - while (i1.hasNext()) { - - JSONObject inner1Obj = (JSONObject) i1.next(); - - if (inner1Obj == null) - continue; - - String relatedTo = checkForNull((String)inner1Obj.get("related-to")); - if (relatedTo.equalsIgnoreCase("tenant")) - { - JSONObject tenantNewObj = new JSONObject(); - - String relatedLink = checkForNull((String) inner1Obj.get("related-link")); - tenantNewObj.put("link", relatedLink); - - JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data"); - if (rDataArray != null) - { - Iterator i2 = rDataArray.iterator(); - - while (i2.hasNext()) { - JSONObject inner2Obj = (JSONObject) i2.next(); - - if (inner2Obj == null) - continue; - - String rShipKey = checkForNull((String)inner2Obj.get("relationship-key")); - String rShipVal = checkForNull((String)inner2Obj.get("relationship-value")); - if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) - { - tenantNewObj.put("cloudOwner", rShipVal); - } - else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) - { - tenantNewObj.put("cloudRegionID", rShipVal); - } - - if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) - { - tenantNewObj.put("tenantID", rShipVal); - } - } - } - - JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property"); - if (relatedTPropArray != null) - { - Iterator i3 = relatedTPropArray.iterator(); - - while (i3.hasNext()) { - JSONObject inner3Obj = (JSONObject) i3.next(); - - if (inner3Obj == null) - continue; - - String propKey = checkForNull((String)inner3Obj.get("property-key")); - String propVal = checkForNull((String)inner3Obj.get("property-value")); - if (propKey.equalsIgnoreCase("tenant.tenant-name")) - { - tenantNewObj.put("tenantName", propVal); - } - } - } - bconvert = true; - tenantArray.add(tenantNewObj); - } - } - } - } - } - } - } - } catch (NullPointerException ex) { - - - } - - if (bconvert) - return tenantArray.toJSONString(); - else - return ""; - - } - - - /** - * Retrieve the service subscription from the jsonObject. - * - * @param jsonObject the json object - * @return String - */ - public static String parseServiceSubscriptionObjectForTenants(JSONObject jsonObject) { - - JSONArray tenantArray = new JSONArray(); - boolean bconvert = false; - - try { - JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list"); - if (relationShipListsObj != null) - { - JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship"); - if (rShipArray != null) - { - Iterator i1 = rShipArray.iterator(); - - while (i1.hasNext()) { - - JSONObject inner1Obj = (JSONObject) i1.next(); - - if (inner1Obj == null) - continue; - - String relatedTo = checkForNull((String)inner1Obj.get("related-to")); - if (relatedTo.equalsIgnoreCase("tenant")) - { - JSONObject tenantNewObj = new JSONObject(); - - String relatedLink = checkForNull((String) inner1Obj.get("related-link")); - tenantNewObj.put("link", relatedLink); - - JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data"); - if (rDataArray != null) - { - Iterator i2 = rDataArray.iterator(); - - while (i2.hasNext()) { - JSONObject inner2Obj = (JSONObject) i2.next(); - - if (inner2Obj == null) - continue; - - String rShipKey = checkForNull((String)inner2Obj.get("relationship-key")); - String rShipVal = checkForNull((String)inner2Obj.get("relationship-value")); - if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) - { - tenantNewObj.put("cloudOwner", rShipVal); - } - else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) - { - tenantNewObj.put("cloudRegionID", rShipVal); - } - - if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) - { - tenantNewObj.put("tenantID", rShipVal); - } - } - } - - JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property"); - if (relatedTPropArray != null) - { - Iterator i3 = relatedTPropArray.iterator(); - - while (i3.hasNext()) { - JSONObject inner3Obj = (JSONObject) i3.next(); - - if (inner3Obj == null) - continue; - - String propKey = checkForNull((String)inner3Obj.get("property-key")); - String propVal = checkForNull((String)inner3Obj.get("property-value")); - if (propKey.equalsIgnoreCase("tenant.tenant-name")) - { - tenantNewObj.put("tenantName", propVal); - } - } - } - bconvert = true; - tenantArray.add(tenantNewObj); - } - } - - } - } - } catch (NullPointerException ex) { - - - } - - if (bconvert) - return tenantArray.toJSONString(); - else - return ""; - - } - - /** - * Check for null. - * - * @param local the local - * @return the string - */ - private static String checkForNull(String local) - { - if (local != null) - return local; - else - return ""; } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/MsoController.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/MsoController.java index f051ed38..56fbd4cb 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/MsoController.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/MsoController.java @@ -21,106 +21,80 @@ package org.openecomp.vid.controller; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -//import java.util.UUID; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.stream.Collectors; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.codehaus.jackson.JsonEncoding; -import org.codehaus.jackson.JsonFactory; -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; import org.glassfish.jersey.client.ClientResponse; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import org.openecomp.vid.domain.mso.CloudConfiguration; -import org.openecomp.vid.domain.mso.ModelInfo; -import org.openecomp.vid.domain.mso.ModelInfo.ModelType; -import org.openecomp.vid.domain.mso.RequestInfo; -import org.openecomp.vid.domain.mso.RequestParameters; +import org.openecomp.portalsdk.core.controller.RestrictedBaseController; +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.util.SystemProperties; import org.openecomp.vid.model.ExceptionResponse; -import org.openecomp.vid.mso.MsoProperties; -import org.openecomp.vid.mso.MsoResponseWrapper; -import org.openecomp.vid.mso.MsoRestInterfaceFactory; -import org.openecomp.vid.mso.MsoRestInterfaceIfc; -import org.openecomp.vid.mso.MsoUtil; -import org.openecomp.vid.mso.RestObject; -import org.openecomp.vid.mso.rest.RelatedModel; +import org.openecomp.vid.mso.*; import org.openecomp.vid.mso.rest.RequestDetails; +import org.openecomp.vid.roles.RoleProvider; +import org.openecomp.vid.roles.RoleValidator; import org.springframework.http.HttpStatus; -//import org.springframework.http.ResponseEntity; -//import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.bind.annotation.*; -import org.openecomp.portalsdk.core.controller.RestrictedBaseController; -import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.openecomp.portalsdk.core.util.SystemProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.DeserializationFeature; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; /** * The Class MsoController. */ @RestController @RequestMapping("mso") -public class MsoController extends RestrictedBaseController{ - - /** The view name. */ - String viewName; - - /** The logger. */ - EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MsoController.class); - - /** The Constant dateFormat. */ - final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); - - /** The Constant SVC_INSTANCE_ID. */ - public final static String SVC_INSTANCE_ID = ""; - - /** The Constant VNF_INSTANCE_ID. */ - public final static String VNF_INSTANCE_ID = ""; - - /** - * Welcome. - * - * @param request the request - * @return the model and view - - public ModelAndView welcome(HttpServletRequest request) { - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== MsoController welcome start"); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + " MSO_SERVER_URL=" + - SystemProperties.getProperty(MsoProperties.MSO_SERVER_URL) ); - return new ModelAndView(getViewName()); - } - */ +public class MsoController extends RestrictedBaseController { + + /** + * The Constant SVC_INSTANCE_ID. + */ + public final static String SVC_INSTANCE_ID = ""; + /** + * The Constant VNF_INSTANCE_ID. + */ + public final static String VNF_INSTANCE_ID = ""; + /** + * The Constant dateFormat. + */ + final static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSSS"); + /** + * The view name. + */ + String viewName; + /** + * The logger. + */ + EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MsoController.class); + + /** + * Welcome. + * + * @param request the request + * @return the model and view + + public ModelAndView welcome(HttpServletRequest request) { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== MsoController welcome start"); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + " MSO_SERVER_URL=" + + SystemProperties.getProperty(MsoProperties.MSO_SERVER_URL) ); + return new ModelAndView(getViewName()); + } + */ /* (non-Javadoc) - * @see org.openecomp.portalsdk.core.controller.RestrictedBaseController#getViewName() + * @see org.openecomp.portalsdk.core.controller.RestrictedBaseController#getViewName() public String getViewName() { return viewName; @@ -128,504 +102,538 @@ public class MsoController extends RestrictedBaseController{ */ /* (non-Javadoc) - * @see org.openecomp.portalsdk.core.controller.RestrictedBaseController#setViewName(java.lang.String) + * @see org.openecomp.portalsdk.core.controller.RestrictedBaseController#setViewName(java.lang.String) public void setViewName(String viewName) { this.viewName = viewName; } */ - /** - * Creates the svc instance. - * - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_create_svc_instance", method = RequestMethod.POST) - public ResponseEntity createSvcInstance(HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - String methodName = "createSvcInstance"; - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start" ); - + /** + * Creates the svc instance. + * + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_create_svc_instance", method = RequestMethod.POST) + public ResponseEntity createSvcInstance(HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + + String methodName = "createSvcInstance"; + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } // mso_request = retrieveRequestObject (request, mso_request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_SVC_INSTANCE); - - MsoResponseWrapper w = createInstance(mso_request, p); - // always return OK, the MSO status code is embedded in the body - - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Creates the vnf. - * - * @param serviceInstanceId the service instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value="/mso_create_vnf_instance/{serviceInstanceId}", method = RequestMethod.POST) - public ResponseEntity createVnf(@PathVariable("serviceInstanceId") String serviceInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "createVnf"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VNF_INSTANCE); - - if ( p == null || p.isEmpty()) { - throw new Exception ( "Vnf instance path is not defined"); - } - // /serviceInstances/v2//vnfs - String vnf_path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId ); - MsoResponseWrapper w = createInstance(mso_request, vnf_path); - - // always return OK, the MSO status code is embedded in the body - - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Creates the nw instance. - * - * @param serviceInstanceId the service instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_create_nw_instance/{serviceInstanceId}", method = RequestMethod.POST) - public ResponseEntity createNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "createNwInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start, serviceInstanceId = " + serviceInstanceId ); - - //RequestDetails mso_request = retrieveRequestObject (request); - - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_NETWORK_INSTANCE); - - if ( p == null || p.isEmpty()) { - throw new Exception ( "Network instance path is not defined"); - } - // /serviceInstances/v2//networks/ - - String nw_path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId ); - MsoResponseWrapper w = createInstance(mso_request, nw_path); - - // always return OK, the MSO status code is embedded in the body - - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Creates the volume group instance. - * - * @param serviceInstanceId the service instance id - * @param vnfInstanceId the vnf instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_create_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) - public ResponseEntity createVolumeGroupInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, - HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - String methodName = "createVolumeGroupInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VOLUME_GROUP_INSTANCE); - - if ( p == null || p.isEmpty()) { - throw new Exception ( "Volume group instance path is not defined"); - } - String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); - path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); - - MsoResponseWrapper w = createInstance(mso_request, path); - - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - } - - /** - * Creates the vf module instance. - * - * @param serviceInstanceId the service instance id - * @param vnfInstanceId the vnf instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_create_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) - public ResponseEntity createVfModuleInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, - @PathVariable("vnfInstanceId") String vnfInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - String methodName = "createVfModuleInstance"; - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - //RequestDetails mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE); - - if ( p == null || p.isEmpty()) { - throw new Exception ( "VF module instance path is not defined"); - } - // /serviceInstances/v2//vnfs//vfmodules - String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); - path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); - - MsoResponseWrapper w = createInstance(mso_request, path); - - // always return OK, the MSO status code is embedded in the body - - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - } - - /** - * Creates the instance. - * - * @param request the request - * @param path the path - * @return the mso response wrapper - * @throws ClientHandlerException the client handler exception - * @throws Exception the exception - */ - protected MsoResponseWrapper createInstance(RequestDetails request, String path) throws Exception { - String methodName = "createInstance"; - logger.debug(dateFormat.format(new Date()) + "<== " + methodName + " start"); - - try { - MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " calling Post, request = (" + request + ")"); - - RestObject restObjStr = new RestObject(); - String str = new String(); - restObjStr.set(str); - restController.Post(str, request, "", path, restObjStr ); - MsoResponseWrapper w = MsoUtil.wrapResponse (restObjStr); - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - return w; - } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - throw e; - } - } - - /** - * Delete svc instance. - * - * @param serviceInstanceId the service instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_delete_svc_instance/{serviceInstanceId}", method = RequestMethod.POST) - public ResponseEntity deleteSvcInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, - HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "deleteSvcInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_SVC_INSTANCE); - String path = p + "/" + serviceInstanceId; - MsoResponseWrapper w = deleteInstance ( mso_request, path ); - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - // always return OK, the MSO status code is embedded in the body - - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Delete vnf. - * - * @param serviceInstanceId the service instance id - * @param vnfInstanceId the vnf instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_delete_vnf_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) - - public ResponseEntity deleteVnf(@PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, - HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - String methodName = "deleteVnf"; - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VNF_INSTANCE); - if ( p == null || p.isEmpty()) { - throw new Exception ( "Vnf instance path is not defined"); - } - // /serviceInstances/v2//vnfs/ - String vnf_path = p.replaceFirst(SVC_INSTANCE_ID, vnfInstanceId ); - MsoResponseWrapper w = deleteInstance ( mso_request, vnf_path + "/" + vnfInstanceId ); - - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Delete vf module. - * - * @param serviceInstanceId the service instance id - * @param vnfInstanceId the vnf instance id - * @param vfModuleId the vf module id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - //mso_delete_vf_module/bc305d54-75b4-431b-adb2-eb6b9e546014/vnfs/fe9000-0009-9999/vfmodules/abeeee-abeeee-abeeee - @RequestMapping(value = "/mso_delete_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules/{vfModuleId}", method = RequestMethod.POST) - public ResponseEntity deleteVfModule ( - @PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, - @PathVariable("vfModuleId") String vfModuleId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "deleteVfModule"; - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = new RequestDetails(); - //mso_request = retrieveRequestObject (request); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE); - if ( p == null || p.isEmpty()) { - throw new Exception ( "VF Module instance path is not defined"); - } - // /serviceInstances/v2//vnfs//vfmodules - String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId ); - path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId ); - MsoResponseWrapper w = deleteInstance ( mso_request, path + "/" + vfModuleId); - - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_SVC_INSTANCE); + String userId = ""; + HttpSession session = request.getSession(); + System.out.println((new ObjectMapper().writeValueAsString(session.getAttribute("roles")))); + MsoResponseWrapper w = createInstance(mso_request, p); + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + // always return OK, the MSO status code is embedded in the body - } - /** - * Delete volume group instance. - * - * @param serviceInstanceId the service instance id - * @param vnfInstanceId the vnf instance id - * @param volumeGroupId the volume group id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_delete_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/volumeGroups/{volumeGroupId}", method = RequestMethod.POST) - public ResponseEntity deleteVolumeGroupInstance ( - @PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, @PathVariable("volumeGroupId") String volumeGroupId, - HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "deleteVolumeGroupInstance"; - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - //RequestDetails mso_request = retrieveRequestObject (request); - - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VOLUME_GROUP_INSTANCE); - if ( p == null || p.isEmpty()) { - throw new Exception ( "Volume group instance path is not defined"); - } - // /serviceInstances/v2/{serviceInstanceId}/volumeGroups - String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId ); - path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId ); - MsoResponseWrapper w = deleteInstance ( mso_request, path + "/" + volumeGroupId); - - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - } - - /** - * Delete nw instance. - * - * @param serviceInstanceId the service instance id - * @param networkInstanceId the network instance id - * @param request the request - * @return the response entity - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_delete_nw_instance/{serviceInstanceId}/networks/{networkInstanceId}", method = RequestMethod.POST) - public ResponseEntity deleteNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, - @PathVariable("networkInstanceId") String networkInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { - - String methodName = "deleteNwInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - //RequestDetails mso_request = retrieveRequestObject (request); - - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_NETWORK_INSTANCE); - if ( p == null || p.isEmpty()) { - throw new Exception ( "Network instance path is not defined"); - } - // /serviceInstances/v2//networks - String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId ); - MsoResponseWrapper w = deleteInstance ( mso_request, path + "/" + networkInstanceId); - - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - - } - - /** - * Delete instance. - * - * @param request the request - * @param path the path - * @return the mso response wrapper - * @throws Exception the exception - */ - protected MsoResponseWrapper deleteInstance(RequestDetails request, String path) throws Exception { - String methodName = "deleteInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - - try { - MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " calling Delete, path =[" + path + "]"); - - RestObject restObjStr = new RestObject(); - String str = new String(); - restObjStr.set(str); - restController.Delete(str, request, "", path, restObjStr ); - MsoResponseWrapper w = MsoUtil.wrapResponse (restObjStr); - - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - return w; - - } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - throw e; - } - - } - - /** - * Gets the orchestration request. - * - * @param requestId the request id - * @param request the request - * @return the orchestration request - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_get_orch_req/{requestId}", method = RequestMethod.GET) - public ResponseEntity getOrchestrationRequest(@PathVariable("requestId") String requestId, - HttpServletRequest request) throws Exception { - - String methodName = "getOrchestrationRequest"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - MsoResponseWrapper w = null; - try { - MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQ); - String path = p + "/" + requestId; - - RestObject restObjStr = new RestObject(); - String str = new String(); - restObjStr.set(str); - - restController.Get(str, "", path, restObjStr); - - w = MsoUtil.wrapResponse (restObjStr); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - // always return OK, the MSO status code is embedded in the body - - } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - throw e; - } - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - } - - - /** - * Gets the orchestration requests. - * - * @param filterString the filter string - * @param request the request - * @return the orchestration requests - * @throws Exception the exception - */ - @RequestMapping(value = "/mso_get_orch_reqs/{filterString}", method = RequestMethod.GET) - public ResponseEntity getOrchestrationRequests(@PathVariable("filterString") String filterString, - HttpServletRequest request) throws Exception { - - String methodName = "getOrchestrationRequests"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - MsoResponseWrapper w = null; - try { - MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQS); - String path = p + filterString; - - RestObject restObjStr = new RestObject(); - String str = new String(); - restObjStr.set(str); - - restController.Get(str, "", path, restObjStr); - - w = MsoUtil.wrapResponse (restObjStr); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - throw e; - } - // always return OK, the MSO status code is embedded in the body - return ( new ResponseEntity(w.getResponse(), HttpStatus.OK) ); - } - - /** - * Gets the orchestration requests for svc instance. - * - * @param svc_instance_id the svc instance id - * @return the orchestration requests for svc instance - * @throws Exception the exception - */ - public MsoResponseWrapper getOrchestrationRequestsForSvcInstance (String svc_instance_id) throws Exception { - - String methodName = "getOrchestrationRequestsForSvcInstance"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); - MsoResponseWrapper w = null; - - try { - MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); - String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQS); - String path = p + svc_instance_id; - - RestObject restObjStr = new RestObject(); - String str = new String(); - restObjStr.set(str); - - restController.Get(str, "", path, restObjStr); - w = MsoUtil.wrapResponse (restObjStr); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); - - } catch (Exception e) { - logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - throw e; - } - return w; - } - - /** - * Exception handler. - * - * @param e the e - * @param response the response - * @throws IOException Signals that an I/O exception has occurred. - */ - @ExceptionHandler(Exception.class) - private void exceptionHandler(Exception e, HttpServletResponse response) throws IOException { + } + + private boolean userIsPermmited(HttpServletRequest request, RequestDetails mso_request) { + + RoleValidator roleValidator = new RoleValidator(new RoleProvider().getUserRoles(request)); + boolean isPermitted = roleValidator.isMsoRequestValid(mso_request); + if (!isPermitted) { + return false; + } else { + return true; + } + } + + /** + * Creates the vnf. + * + * @param serviceInstanceId the service instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_create_vnf_instance/{serviceInstanceId}", method = RequestMethod.POST) + public ResponseEntity createVnf(@PathVariable("serviceInstanceId") String serviceInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { +// if (!userIsPermmited(request, mso_request)) { + String instanceId = (String) ((Map)((Map)((ArrayList)((Map) mso_request.getAdditionalProperties().get("requestDetails")).get("relatedInstanceList")).get(0)).get("relatedInstance")).get("instanceId"); + ResponseEntity a = new AaiController(request.getServletContext()).doGetServiceInstance(instanceId,"Service Instance id"); +// return new ResponseEntity(HttpStatus.FORBIDDEN); +// } + String methodName = "createVnf"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + //RequestDetails mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VNF_INSTANCE); + + if (p == null || p.isEmpty()) { + throw new Exception("Vnf instance path is not defined"); + } + // /serviceInstances/v2//vnfs + String vnf_path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + MsoResponseWrapper w = createInstance(mso_request, vnf_path); + + // always return OK, the MSO status code is embedded in the body + + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Creates the nw instance. + * + * @param serviceInstanceId the service instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_create_nw_instance/{serviceInstanceId}", method = RequestMethod.POST) + public ResponseEntity createNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "createNwInstance"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start, serviceInstanceId = " + serviceInstanceId); + + //RequestDetails mso_request = retrieveRequestObject (request); + + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_NETWORK_INSTANCE); + + if (p == null || p.isEmpty()) { + throw new Exception("Network instance path is not defined"); + } + // /serviceInstances/v2//networks/ + + String nw_path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + MsoResponseWrapper w = createInstance(mso_request, nw_path); + + // always return OK, the MSO status code is embedded in the body + + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Creates the volume group instance. + * + * @param serviceInstanceId the service instance id + * @param vnfInstanceId the vnf instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_create_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) + public ResponseEntity createVolumeGroupInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, + HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + String methodName = "createVolumeGroupInstance"; + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + //RequestDetails mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VOLUME_GROUP_INSTANCE); + + if (p == null || p.isEmpty()) { + throw new Exception("Volume group instance path is not defined"); + } + String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); + + MsoResponseWrapper w = createInstance(mso_request, path); + + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + } + + /** + * Creates the vf module instance. + * + * @param serviceInstanceId the service instance id + * @param vnfInstanceId the vnf instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_create_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) + public ResponseEntity createVfModuleInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, + @PathVariable("vnfInstanceId") String vnfInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + String methodName = "createVfModuleInstance"; + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + //RequestDetails mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE); + + if (p == null || p.isEmpty()) { + throw new Exception("VF module instance path is not defined"); + } + // /serviceInstances/v2//vnfs//vfmodules + String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); + + MsoResponseWrapper w = createInstance(mso_request, path); + + // always return OK, the MSO status code is embedded in the body + + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + } + + /** + * Creates the instance. + * + * @param request the request + * @param path the path + * @return the mso response wrapper + * @throws Exception the exception + */ + protected MsoResponseWrapper createInstance(RequestDetails request, String path) throws Exception { + String methodName = "createInstance"; + logger.debug(dateFormat.format(new Date()) + "<== " + methodName + " start"); + + try { + MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " calling Post, request = (" + request + ")"); + + RestObject restObjStr = new RestObject(); + String str = new String(); + restObjStr.set(str); + restController.Post(str, request, "", path, restObjStr); + MsoResponseWrapper w = MsoUtil.wrapResponse(restObjStr); + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + return w; + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + throw e; + } + } + + /** + * Delete svc instance. + * + * @param serviceInstanceId the service instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_delete_svc_instance/{serviceInstanceId}", method = RequestMethod.POST) + public ResponseEntity deleteSvcInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, + HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "deleteSvcInstance"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + //RequestDetails mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_SVC_INSTANCE); + String path = p + "/" + serviceInstanceId; + MsoResponseWrapper w = deleteInstance(mso_request, path); + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + // always return OK, the MSO status code is embedded in the body + + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Delete vnf. + * + * @param serviceInstanceId the service instance id + * @param vnfInstanceId the vnf instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_delete_vnf_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST) + + public ResponseEntity deleteVnf(@PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, + HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + String methodName = "deleteVnf"; + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + //RequestDetails mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VNF_INSTANCE); + if (p == null || p.isEmpty()) { + throw new Exception("Vnf instance path is not defined"); + } + // /serviceInstances/v2//vnfs/ + String vnf_path = p.replaceFirst(SVC_INSTANCE_ID, vnfInstanceId); + MsoResponseWrapper w = deleteInstance(mso_request, vnf_path + "/" + vnfInstanceId); + + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Delete vf module. + * + * @param serviceInstanceId the service instance id + * @param vnfInstanceId the vnf instance id + * @param vfModuleId the vf module id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + //mso_delete_vf_module/bc305d54-75b4-431b-adb2-eb6b9e546014/vnfs/fe9000-0009-9999/vfmodules/abeeee-abeeee-abeeee + @RequestMapping(value = "/mso_delete_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules/{vfModuleId}", method = RequestMethod.POST) + public ResponseEntity deleteVfModule( + @PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, + @PathVariable("vfModuleId") String vfModuleId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "deleteVfModule"; + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + //RequestDetails mso_request = new RequestDetails(); + //mso_request = retrieveRequestObject (request); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE); + if (p == null || p.isEmpty()) { + throw new Exception("VF Module instance path is not defined"); + } + // /serviceInstances/v2//vnfs//vfmodules + String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); + MsoResponseWrapper w = deleteInstance(mso_request, path + "/" + vfModuleId); + + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Delete volume group instance. + * + * @param serviceInstanceId the service instance id + * @param vnfInstanceId the vnf instance id + * @param volumeGroupId the volume group id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_delete_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/volumeGroups/{volumeGroupId}", method = RequestMethod.POST) + public ResponseEntity deleteVolumeGroupInstance( + @PathVariable("serviceInstanceId") String serviceInstanceId, @PathVariable("vnfInstanceId") String vnfInstanceId, @PathVariable("volumeGroupId") String volumeGroupId, + HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "deleteVolumeGroupInstance"; + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_VOLUME_GROUP_INSTANCE); + if (p == null || p.isEmpty()) { + throw new Exception("Volume group instance path is not defined"); + } + // /serviceInstances/v2/{serviceInstanceId}/volumeGroups + String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + path = path.replaceFirst(VNF_INSTANCE_ID, vnfInstanceId); + MsoResponseWrapper w = deleteInstance(mso_request, path + "/" + volumeGroupId); + + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + } + + /** + * Delete nw instance. + * + * @param serviceInstanceId the service instance id + * @param networkInstanceId the network instance id + * @param request the request + * @return the response entity + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_delete_nw_instance/{serviceInstanceId}/networks/{networkInstanceId}", method = RequestMethod.POST) + public ResponseEntity deleteNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId, + @PathVariable("networkInstanceId") String networkInstanceId, HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "deleteNwInstance"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + //RequestDetails mso_request = retrieveRequestObject (request); + if (!userIsPermmited(request, mso_request)) { + return new ResponseEntity(HttpStatus.FORBIDDEN); + } + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_NETWORK_INSTANCE); + if (p == null || p.isEmpty()) { + throw new Exception("Network instance path is not defined"); + } + // /serviceInstances/v2//networks + String path = p.replaceFirst(SVC_INSTANCE_ID, serviceInstanceId); + MsoResponseWrapper w = deleteInstance(mso_request, path + "/" + networkInstanceId); + + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + + } + + /** + * Delete instance. + * + * @param request the request + * @param path the path + * @return the mso response wrapper + * @throws Exception the exception + */ + protected MsoResponseWrapper deleteInstance(RequestDetails request, String path) throws Exception { + String methodName = "deleteInstance"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + try { + MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " calling Delete, path =[" + path + "]"); + + RestObject restObjStr = new RestObject(); + String str = new String(); + restObjStr.set(str); + restController.Delete(str, request, "", path, restObjStr); + MsoResponseWrapper w = MsoUtil.wrapResponse(restObjStr); + + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + return w; + + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + throw e; + } + + } + + /** + * Gets the orchestration request. + * + * @param requestId the request id + * @param request the request + * @return the orchestration request + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_get_orch_req/{requestId}", method = RequestMethod.GET) + public ResponseEntity getOrchestrationRequest(@PathVariable("requestId") String requestId, + HttpServletRequest request) throws Exception { + + String methodName = "getOrchestrationRequest"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + MsoResponseWrapper w = null; + try { + MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQ); + String path = p + "/" + requestId; + + RestObject restObjStr = new RestObject(); + String str = new String(); + restObjStr.set(str); + + restController.Get(str, "", path, restObjStr); + + w = MsoUtil.wrapResponse(restObjStr); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + // always return OK, the MSO status code is embedded in the body + + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + throw e; + } + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + } + + + /** + * Gets the orchestration requests. + * + * @param filterString the filter string + * @param request the request + * @return the orchestration requests + * @throws Exception the exception + */ + @RequestMapping(value = "/mso_get_orch_reqs/{filterString}", method = RequestMethod.GET) + public ResponseEntity getOrchestrationRequests(@PathVariable("filterString") String filterString, + HttpServletRequest request) throws Exception { + + String methodName = "getOrchestrationRequests"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + MsoResponseWrapper w = null; + try { + MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQS); + String path = p + filterString; + + RestObject restObjStr = new RestObject(); + String str = new String(); + restObjStr.set(str); + + restController.Get(str, "", path, restObjStr); + + w = MsoUtil.wrapResponse(restObjStr); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + throw e; + } + // always return OK, the MSO status code is embedded in the body + return (new ResponseEntity(w.getResponse(), HttpStatus.OK)); + } + + /** + * Gets the orchestration requests for svc instance. + * + * @param svc_instance_id the svc instance id + * @return the orchestration requests for svc instance + * @throws Exception the exception + */ + public MsoResponseWrapper getOrchestrationRequestsForSvcInstance(String svc_instance_id) throws Exception { + + String methodName = "getOrchestrationRequestsForSvcInstance"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + MsoResponseWrapper w = null; + + try { + MsoRestInterfaceIfc restController = MsoRestInterfaceFactory.getInstance(); + String p = SystemProperties.getProperty(MsoProperties.MSO_REST_API_GET_ORC_REQS); + String path = p + svc_instance_id; + + RestObject restObjStr = new RestObject(); + String str = new String(); + restObjStr.set(str); + + restController.Get(str, "", path, restObjStr); + w = MsoUtil.wrapResponse(restObjStr); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " w=" + w.getResponse()); + + } catch (Exception e) { + logger.info(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + throw e; + } + return w; + } + + /** + * Exception handler. + * + * @param e the e + * @param response the response + * @throws IOException Signals that an I/O exception has occurred. + */ + @ExceptionHandler(Exception.class) + private void exceptionHandler(Exception e, HttpServletResponse response) throws IOException { /* * The following "logger.error" lines "should" be sufficient for logging the exception. @@ -633,129 +641,126 @@ public class MsoController extends RestrictedBaseController{ * logger statements in this class. Thus the temporary "e.printStackTrace" statement * is also included. */ - - String methodName = "exceptionHandler"; - logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - logger.error(EELFLoggerDelegate.errorLogger, sw.toString()); + + String methodName = "exceptionHandler"; + logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + e.toString()); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + logger.error(EELFLoggerDelegate.errorLogger, sw.toString()); /* * Temporary - IF the above mentioned "logger.error" glitch is resolved ... * this statement could be removed since it would then likely result in duplicate * trace output. */ - e.printStackTrace(System.err); + e.printStackTrace(System.err); - response.setContentType("application/json; charset=UTF-8"); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - ExceptionResponse exceptionResponse = new ExceptionResponse(); - exceptionResponse.setException(e.getClass().toString().replaceFirst("^.*\\.", "")); - exceptionResponse.setMessage(e.getMessage()); + ExceptionResponse exceptionResponse = new ExceptionResponse(); + exceptionResponse.setException(e.getClass().toString().replaceFirst("^.*\\.", "")); + exceptionResponse.setMessage(e.getMessage()); - response.getWriter().write(new ObjectMapper().writeValueAsString(exceptionResponse)); + response.getWriter().write(new ObjectMapper().writeValueAsString(exceptionResponse)); - response.flushBuffer(); + response.flushBuffer(); - } + } - /** - * Parses the orchestration requests for svc instance. - * - * @param resp the resp - * @return the list - * @throws ParseException the parse exception - * @throws Exception the exception - */ - @SuppressWarnings("unchecked") - public List parseOrchestrationRequestsForSvcInstance ( ClientResponse resp ) throws org.json.simple.parser.ParseException, Exception { - - String methodName = "parseOrchestrationRequestsForSvcInstance"; - - ArrayList json_list = new ArrayList(); - - String rlist_str = resp.readEntity (String.class); - logger.debug (EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Response string: " + rlist_str); - - JSONParser parser = new JSONParser(); - try { - Object obj = parser.parse(rlist_str); - - JSONObject jsonObject = (JSONObject) obj; - - JSONArray requestList = (JSONArray) jsonObject.get("requestList"); - - if ( requestList != null && ! (requestList.isEmpty()) ) - for ( Object container : requestList) { - - JSONObject containerJsonObj = (JSONObject) container; - //logger.debug(dateFormat.format(new Date()) + "<== " + "." + methodName + " reqJsonObj: " + containerJsonObj.toJSONString()); - JSONObject reqJsonObj = (JSONObject) containerJsonObj.get("request"); - - //logger.debug(dateFormat.format(new Date()) + "<== " + "." + methodName + " reqJsonObj.requestId: " + - // reqJsonObj.get("requestId") ); - JSONObject result = new JSONObject(); - - result.put("requestId", reqJsonObj.get ("requestId")); - if ( reqJsonObj.get("requestType") != null ) { - result.put("requestType", (reqJsonObj.get("requestType").toString())); - } - JSONObject req_status = (JSONObject)reqJsonObj.get("requestStatus"); - if ( req_status != null ) { - result.put("timestamp", (req_status.get("timestamp"))); - result.put("requestState", (req_status.get("requestState"))); - result.put("statusMessage", (req_status.get("statusMessage"))); - result.put("percentProgress", (req_status.get("percentProgress"))); - } - json_list.add (result); - } - } catch (org.json.simple.parser.ParseException pe) { - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Parse exception: " + pe.toString()); - throw pe; - } catch (Exception e) { - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Exception: " + e.toString()); - throw e; - } - return ( json_list ); - } - - /** - * Retrieve request object. - * - * @param request the request - * @return the request details - * @throws Exception the exception - */ - public RequestDetails retrieveRequestObject ( HttpServletRequest request, @RequestBody RequestDetails mso_request ) throws Exception { - - String methodName = "retrieveRequestObject"; - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start" ); - - ObjectMapper mapper = new ObjectMapper(); - //JSON from String to Object - //RequestDetails mso_request; - - try { - //mso_request = new RequestDetails(); - //mso_request = mapper.readValue(request.getInputStream(), RequestDetails.class); - } - catch ( Exception e ) { - logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " Unable to read json object RequestDetails e=" + e.getMessage()); - throw e; - } - if ( mso_request == null) { - logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " mso_request is null"); - throw new Exception ("RequestDetails is missing"); - } - try { - String json_req = mapper.writeValueAsString(mso_request); - logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " request=[" + json_req + "]"); - } - catch ( Exception e ) { - logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " Unable to convert RequestDetails to json string e=" + e.getMessage()); - throw e; - } - return (mso_request); - } + /** + * Parses the orchestration requests for svc instance. + * + * @param resp the resp + * @return the list + * @throws Exception the exception + */ + @SuppressWarnings("unchecked") + public List parseOrchestrationRequestsForSvcInstance(ClientResponse resp) throws org.json.simple.parser.ParseException, Exception { + + String methodName = "parseOrchestrationRequestsForSvcInstance"; + + ArrayList json_list = new ArrayList(); + + String rlist_str = resp.readEntity(String.class); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Response string: " + rlist_str); + + JSONParser parser = new JSONParser(); + try { + Object obj = parser.parse(rlist_str); + + JSONObject jsonObject = (JSONObject) obj; + + JSONArray requestList = (JSONArray) jsonObject.get("requestList"); + + if (requestList != null && !(requestList.isEmpty())) + for (Object container : requestList) { + + JSONObject containerJsonObj = (JSONObject) container; + //logger.debug(dateFormat.format(new Date()) + "<== " + "." + methodName + " reqJsonObj: " + containerJsonObj.toJSONString()); + JSONObject reqJsonObj = (JSONObject) containerJsonObj.get("request"); + + //logger.debug(dateFormat.format(new Date()) + "<== " + "." + methodName + " reqJsonObj.requestId: " + + // reqJsonObj.get("requestId") ); + JSONObject result = new JSONObject(); + + result.put("requestId", reqJsonObj.get("requestId")); + if (reqJsonObj.get("requestType") != null) { + result.put("requestType", (reqJsonObj.get("requestType").toString())); + } + JSONObject req_status = (JSONObject) reqJsonObj.get("requestStatus"); + if (req_status != null) { + result.put("timestamp", (req_status.get("timestamp"))); + result.put("requestState", (req_status.get("requestState"))); + result.put("statusMessage", (req_status.get("statusMessage"))); + result.put("percentProgress", (req_status.get("percentProgress"))); + } + json_list.add(result); + } + } catch (org.json.simple.parser.ParseException pe) { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Parse exception: " + pe.toString()); + throw pe; + } catch (Exception e) { + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + "." + methodName + " Exception: " + e.toString()); + throw e; + } + return (json_list); + } + + /** + * Retrieve request object. + * + * @param request the request + * @return the request details + * @throws Exception the exception + */ + public RequestDetails retrieveRequestObject(HttpServletRequest request, @RequestBody RequestDetails mso_request) throws Exception { + + String methodName = "retrieveRequestObject"; + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " start"); + + ObjectMapper mapper = new ObjectMapper(); + //JSON from String to Object + //RequestDetails mso_request; + + try { + //mso_request = new RequestDetails(); + //mso_request = mapper.readValue(request.getInputStream(), RequestDetails.class); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " Unable to read json object RequestDetails e=" + e.getMessage()); + throw e; + } + if (mso_request == null) { + logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " mso_request is null"); + throw new Exception("RequestDetails is missing"); + } + try { + String json_req = mapper.writeValueAsString(mso_request); + logger.debug(EELFLoggerDelegate.debugLogger, dateFormat.format(new Date()) + "<== " + methodName + " request=[" + json_req + "]"); + } catch (Exception e) { + logger.error(EELFLoggerDelegate.errorLogger, dateFormat.format(new Date()) + "<== " + methodName + " Unable to convert RequestDetails to json string e=" + e.getMessage()); + throw e; + } + return (mso_request); + } } diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java index 9972ae5d..838793a7 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/VidController.java @@ -24,18 +24,22 @@ import org.openecomp.portalsdk.core.controller.RestrictedBaseController; import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; import org.openecomp.sdc.tosca.parser.exceptions.SdcToscaParserException; import org.openecomp.vid.asdc.AsdcCatalogException; +import org.openecomp.vid.asdc.beans.SecureServices; import org.openecomp.vid.exceptions.VidServiceUnavailableException; import org.openecomp.vid.model.ServiceModel; +import org.openecomp.vid.roles.Role; +import org.openecomp.vid.roles.RoleProvider; +import org.openecomp.vid.roles.RoleValidator; +import org.openecomp.vid.services.VidService; import org.springframework.beans.factory.annotation.Autowired; 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.RestController; import org.springframework.web.servlet.ModelAndView; -import org.openecomp.vid.services.*; import javax.servlet.http.HttpServletRequest; -import java.util.Collection; +import java.util.List; import java.util.Map; //import org.openecomp.vid.model.Service; @@ -61,10 +65,15 @@ public class VidController extends RestrictedBaseController { * @throws VidServiceUnavailableException the vid service unavailable exception */ @RequestMapping(value={"/rest/models/services"}, method = RequestMethod.GET) - public Collection getServices(HttpServletRequest request) throws VidServiceUnavailableException { + public SecureServices getServices(HttpServletRequest request) throws VidServiceUnavailableException { try { + SecureServices secureServices = new SecureServices(); + RoleProvider roleProvider = new RoleProvider(); Map requestParams = request.getParameterMap(); - return service.getServices(requestParams); + List roles = new RoleProvider().getUserRoles(request); + secureServices.setServices(service.getServices(requestParams)); + secureServices.setReadOnly(roleProvider.userPermissionIsReadOnly(roles)); + return secureServices; } catch (AsdcCatalogException e) { LOG.error("Failed to retrieve service definitions from SDC", e); throw new VidServiceUnavailableException("Failed to retrieve service definitions from SDC", e); @@ -83,8 +92,9 @@ public class VidController extends RestrictedBaseController { * @throws VidServiceUnavailableException the vid service unavailable exception */ @RequestMapping(value={"/rest/models/services/{uuid}"}, method = RequestMethod.GET) - public ServiceModel getServices(@PathVariable("uuid") String uuid) throws VidServiceUnavailableException { + public ServiceModel getServices(@PathVariable("uuid") String uuid, HttpServletRequest request) throws VidServiceUnavailableException { try { +// RoleValidator roleValidator = new RoleValidator(new RoleProvider().getUserRoles(request)); return service.getService(uuid); } catch (AsdcCatalogException e) { LOG.error("Failed to retrieve service definitions from SDC", e); diff --git a/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java b/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java index 9a258a10..1e13d0eb 100644 --- a/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/controller/WebConfig.java @@ -4,13 +4,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.json.JSONObject; import org.json.JSONTokener; +import org.openecomp.vid.aai.AaiClient; +import org.openecomp.vid.aai.AaiClientInterface; import org.openecomp.vid.asdc.AsdcClient; import org.openecomp.vid.asdc.local.LocalAsdcClient; import org.openecomp.vid.asdc.memory.InMemoryAsdcClient; -import org.openecomp.vid.asdc.rest.RestfulAsdcClient; import org.openecomp.vid.asdc.parser.ToscaParserImpl2; +import org.openecomp.vid.asdc.rest.RestfulAsdcClient; import org.openecomp.vid.properties.AsdcClientConfiguration; import org.openecomp.vid.properties.AsdcClientConfiguration.AsdcClientType; +import org.openecomp.vid.services.AaiService; +import org.openecomp.vid.services.AaiServiceImpl; import org.openecomp.vid.services.VidService; import org.openecomp.vid.services.VidServiceImpl; import org.springframework.context.annotation.Bean; @@ -40,11 +44,23 @@ public class WebConfig { return new ObjectMapper(); } + + @Bean public VidService vidService(AsdcClient asdcClient) { return new VidServiceImpl(asdcClient); } + @Bean + public AaiService getAaiService(){ + return new AaiServiceImpl(); + } + + @Bean + public AaiClientInterface getAaiClientInterface(){ + return new AaiClient(); + } + @Bean public AsdcClient asdcClient(AsdcClientConfiguration asdcClientConfig) throws IOException { switch (asdcClientConfig.getAsdcClientType()) { diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java b/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java new file mode 100644 index 00000000..f0208185 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/ModelUtil.java @@ -0,0 +1,45 @@ +/** + * + */ +package org.openecomp.vid.model; + +/** + * The Class ModelUtil. + * + */ +public class ModelUtil { + /** + * Gets the tags for the given element according to the configured namespace + * @param namespaces the namespace list from the configuration + * @param constantValue the constant portion of the tag name, i.e. resource.vf... + * @return the tags + */ + public static String[] getTags ( String[] namespaces, String constantValue ) { + String[] tags; + if ( namespaces == null || namespaces.length == 0 ) { + return null; + } + int le = namespaces.length; + tags = new String[le]; + for ( int i = 0; i < le; i++ ) { + tags[i] = namespaces[i] + constantValue; + } + return (tags); + } + /** + * Determine if a note template type matches a set of configurable tags + * @param type the node template type + * @param tags the model configurable namespaces + * @return true if type starts with a tag in the array, false otherwise + */ + public static boolean isType ( String type, String[] tags ) { + if ( (tags != null) && (tags.length > 0) ) { + for ( int i = 0; i < tags.length; i++ ) { + if ( type.startsWith (tags[i]) ) { + return (true); + } + } + } + return (false); + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/ProxyResponse.java b/vid-app-common/src/main/java/org/openecomp/vid/model/ProxyResponse.java new file mode 100644 index 00000000..132c54ad --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/ProxyResponse.java @@ -0,0 +1,21 @@ +package org.openecomp.vid.model; + +/** + * Created by Oren on 7/10/17. + */ +public class ProxyResponse { + + protected String errorMessage; + + protected int httpCode; + + public String getErrorMessage() { + return errorMessage; + } + + + public int getHttpCode() { + return httpCode; + } + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/Service.java b/vid-app-common/src/main/java/org/openecomp/vid/model/Service.java index f3c6e4da..c6269b97 100755 --- a/vid-app-common/src/main/java/org/openecomp/vid/model/Service.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/Service.java @@ -48,6 +48,12 @@ public class Service { /** The category. */ private String category; + + /** The Service Type. */ + private String serviceType; + + /** The Service Role */ + private String serviceRole; /** The description. */ private String description; @@ -238,6 +244,23 @@ public class Service { return (service.getUuid().equals(getUuid())); } + + public String getServiceType() { + return serviceType; + } + + public void setServiceType(String serviceType) { + this.serviceType = serviceType; + } + + public String getServiceRole() { + return serviceRole; + } + + public void setServiceRole(String serviceRole) { + this.serviceRole = serviceRole; + } + /*public static void extractVfModuleCustomizationUUID (Service s, String vnfCustomizationName, VfModule vfMod ) { //Look for vnfCustomizationName..vfModuleCustomizationName diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/Subscriber.java b/vid-app-common/src/main/java/org/openecomp/vid/model/Subscriber.java new file mode 100644 index 00000000..c5db6fda --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/Subscriber.java @@ -0,0 +1,26 @@ +package org.openecomp.vid.model; + + +import org.codehaus.jackson.annotate.JsonProperty; + +/** + * Created by Oren on 7/4/17. + */ +public class Subscriber { + + @JsonProperty("global-customer-id") + public String globalCustomerId; + + @JsonProperty("subscriber-name") + public String subscriberName; + + @JsonProperty("subscriber-type") + public String subscriberType; + + @JsonProperty("resource-version") + public String resourceVersion; + + + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/model/SubscriberList.java b/vid-app-common/src/main/java/org/openecomp/vid/model/SubscriberList.java new file mode 100644 index 00000000..7f593e4d --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/model/SubscriberList.java @@ -0,0 +1,15 @@ +package org.openecomp.vid.model; + +import org.openecomp.portalsdk.core.web.support.UserUtils; + +import java.util.List; + +/** + * Created by Oren on 7/4/17. + */ +public class SubscriberList { + + public List customer; + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/roles/EcompRole.java b/vid-app-common/src/main/java/org/openecomp/vid/roles/EcompRole.java new file mode 100644 index 00000000..5242f5aa --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/roles/EcompRole.java @@ -0,0 +1,5 @@ +package org.openecomp.vid.roles; + +public enum EcompRole { + READ; +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/roles/Role.java b/vid-app-common/src/main/java/org/openecomp/vid/roles/Role.java new file mode 100644 index 00000000..d4ded530 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/roles/Role.java @@ -0,0 +1,48 @@ +package org.openecomp.vid.roles; + + +/** + * Created by Oren on 7/1/17. + */ +public class Role { + + private EcompRole ecompRole; + + private String subscribeName; + + private String serviceType; + + private String tenant; + + public Role(EcompRole ecompRole, String serviceName, String serviceType, String tenant) { + this.ecompRole = ecompRole; + this.subscribeName = serviceName; + this.serviceType = serviceType; + this.tenant = tenant; + } + + public EcompRole getEcompRole() { + return ecompRole; + } + + + public String getSubscribeName() { + return subscribeName; + } + + public void setSubscribeName(String subscribeName) { + this.subscribeName = subscribeName; + } + + public String getServiceType() { + return serviceType; + } + + + public String getTenant() { + return tenant; + } + + + +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleProvider.java b/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleProvider.java new file mode 100644 index 00000000..99645a10 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleProvider.java @@ -0,0 +1,62 @@ +package org.openecomp.vid.roles; + +import org.openecomp.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.openecomp.portalsdk.core.web.support.UserUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Created by Oren on 7/1/17. + */ +public class RoleProvider { + + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(RoleProvider.class); + final String readPermissionString = "read"; + + public static List extractRoleFromSession(HttpServletRequest request) { + + return new ArrayList(); + + } + + public List getUserRoles(HttpServletRequest request) { + List roleList = new ArrayList<>(); + HashMap roles = UserUtils.getRoles(request); + for (Object role : roles.keySet()) { + org.openecomp.portalsdk.core.domain.Role sdkRol = (org.openecomp.portalsdk.core.domain.Role) roles.get(role); + try { + if (sdkRol.getName().contains(readPermissionString)) + continue; + String[] roleParts = splitRole((sdkRol.getName())); + roleList.add(createRoleFromStringArr(roleParts)); + } catch (Exception e) { + LOG.error("Failed to parse permission", e); + + } + } + + return roleList; + } + + public String[] splitRole(String roleAsString) { + return roleAsString.split("_"); + } + + public boolean userPermissionIsReadOnly(List roles) { + + return (!(roles.size() > 0)); + } + + public Role createRoleFromStringArr(String[] roleParts) { + if (roleParts.length > 2) { + return new Role(EcompRole.READ, roleParts[0], roleParts[1], roleParts[2]); + } else { + return new Role(EcompRole.READ, roleParts[0], roleParts[1], null); + } + } + +} + diff --git a/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleValidator.java b/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleValidator.java new file mode 100644 index 00000000..e26c5231 --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/roles/RoleValidator.java @@ -0,0 +1,57 @@ +package org.openecomp.vid.roles; + +import org.openecomp.vid.mso.rest.RequestDetails; + +import java.util.List; +import java.util.Map; + +/** + * Created by Oren on 7/12/17. + */ +public class RoleValidator { + + private List userRoles; + + public RoleValidator(List roles) { + this.userRoles = roles; + } + + public boolean isSubscriberPermitted(String subscriberName) { + for (Role role : userRoles) { + if (role.getSubscribeName().equals(subscriberName)) + return true; + } + return false; + } + + public boolean isServicePermitted(String subscriberName, String serviceType) { + for (Role role : userRoles) { + if (role.getSubscribeName().equals(subscriberName) && role.getServiceType().equals(serviceType)) + return true; + } + return false; + } + + public boolean isMsoRequestValid(RequestDetails mso_request) { + try { + String globalSubscriberIdRequested = (String) ((Map) ((Map) mso_request.getAdditionalProperties().get("requestDetails")).get("subscriberInfo")).get("globalSubscriberId"); + String serviceType = (String) ((Map) ((Map) mso_request.getAdditionalProperties().get("requestDetails")).get("requestParameters")).get("subscriptionServiceType"); + return isServicePermitted(globalSubscriberIdRequested, serviceType); + } catch (Exception e) { + //Until we'll get the exact information regarding the tenants and the global customer id, we'll return true on unknown requests to mso + return true; + } +// return false; + } + + public boolean isTenantPermitted(String globalCustomerId, String serviceType, String tenant) { + for (Role role : userRoles) { + if (role.getSubscribeName().equals(globalCustomerId) + && role.getServiceType().equals(serviceType) + && (role.getTenant() == null || role.getTenant().equals(tenant))) { + return true; + } + } + return false; + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/services/AaiService.java b/vid-app-common/src/main/java/org/openecomp/vid/services/AaiService.java new file mode 100644 index 00000000..274419fb --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/services/AaiService.java @@ -0,0 +1,25 @@ +package org.openecomp.vid.services; + +import org.openecomp.vid.aai.AaiResponse; +import org.openecomp.vid.aai.SubscriberFilteredResults; +import org.openecomp.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.openecomp.vid.roles.RoleValidator; + +import java.util.List; + +/** + * Created by Oren on 7/4/17. + */ +public interface AaiService { + + + SubscriberFilteredResults getFullSubscriberList(RoleValidator roleValidator); + + AaiResponse getSubscriberData(String subscriberId, RoleValidator roleValidator); + + AaiResponse getServices(RoleValidator roleValidator); + + AaiResponse getAaiZones(); + + AaiResponse getTenants(String globalCustomerId, String serviceType, RoleValidator roleValidator); +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/services/AaiServiceImpl.java b/vid-app-common/src/main/java/org/openecomp/vid/services/AaiServiceImpl.java new file mode 100644 index 00000000..38b670fe --- /dev/null +++ b/vid-app-common/src/main/java/org/openecomp/vid/services/AaiServiceImpl.java @@ -0,0 +1,70 @@ +package org.openecomp.vid.services; + +import org.ecomp.aai.model.AaiAICZones.AicZones; +import org.openecomp.vid.aai.*; +import org.openecomp.vid.aai.model.AaiGetServicesRequestModel.*; +import org.openecomp.vid.aai.model.AaiGetTenatns.GetTenantsResponse; +import org.openecomp.vid.model.*; +import org.openecomp.vid.roles.RoleValidator; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * Created by Oren on 7/4/17. + */ +public class AaiServiceImpl implements AaiService { + + + @Autowired + private AaiClientInterface aaiClient; + + + @Override + public SubscriberFilteredResults getFullSubscriberList(RoleValidator roleValidator) { + AaiResponse subscriberResponse = aaiClient.getAllSubscribers(); + SubscriberFilteredResults subscriberFilteredResults = + new SubscriberFilteredResults(roleValidator,subscriberResponse.getT(), + subscriberResponse.getErrorMessage(), + subscriberResponse.getHttpCode()); + + return subscriberFilteredResults; + } + + @Override + public AaiResponse getSubscriberData(String subscriberId, RoleValidator roleProvider) { + AaiResponse subscriberResponse = aaiClient.getSubscriberData(subscriberId); + String subscriberGlobalId = subscriberResponse.getT().globalCustomerId; + for (ServiceSubscription serviceSubscription : subscriberResponse.getT().serviceSubscriptions.serviceSubscription) { + String serviceType = serviceSubscription.serviceType; + serviceSubscription.isPermitted = roleProvider.isServicePermitted(subscriberGlobalId,serviceType);; + } + return subscriberResponse; + + } + + @Override + public AaiResponse getServices(RoleValidator roleValidator) { + AaiResponse subscriberResponse = aaiClient.getServices(); + for (org.openecomp.vid.aai.model.AaiGetServicesRequestModel.Service service :subscriberResponse.getT().service){ + service.isPermitted = true; + } + return subscriberResponse; + } + + @Override + public AaiResponse getTenants(String globalCustomerId, String serviceType, RoleValidator roleValidator) { + AaiResponse aaiGetTenantsResponse = aaiClient.getTenants(globalCustomerId,serviceType); + GetTenantsResponse[] tenants = aaiGetTenantsResponse.getT(); + for (int i=0;i response = aaiClient.getAllAicZones(); + return response; + } +} diff --git a/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java b/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java index 9844842f..f647af45 100644 --- a/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java +++ b/vid-app-common/src/main/java/org/openecomp/vid/services/VidServiceImpl.java @@ -14,9 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired; import java.nio.file.Path; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Map; -import java.util.UUID; +import java.util.*; /** * The Class VidController. diff --git a/vid-app-common/src/main/resources/csar3933948645405128424.zip b/vid-app-common/src/main/resources/csar3933948645405128424.zip new file mode 100644 index 0000000000000000000000000000000000000000..b92d5339f8307918edd4606772fb9f080d2281b5 GIT binary patch literal 104292 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%0Djt2qqQ~^(RIb9E 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