From 297ea699d8f7f3be96abea6c28e4204f7c4687a4 Mon Sep 17 00:00:00 2001 From: dglFromAtt Date: Thu, 27 Dec 2018 09:01:49 -0500 Subject: [PATCH] Introduce default pub/sub Roles for Topic Change-Id: I79b553648a9a151c420ee66ef08ebe713d8d2515 Signed-off-by: dglFromAtt Issue-ID: DMAAP-856 --- .../org/onap/dmaap/dbcapi/aaf/AafNamespace.java | 104 +++++++++++++++++ .../java/org/onap/dmaap/dbcapi/aaf/AafRole.java | 66 +++++++++++ .../java/org/onap/dmaap/dbcapi/aaf/AafService.java | 125 ++++++++++----------- .../java/org/onap/dmaap/dbcapi/model/Topic.java | 14 +++ .../onap/dmaap/dbcapi/service/DmaapService.java | 2 + .../onap/dmaap/dbcapi/service/TopicService.java | 111 +++++++++++++++--- src/main/resources/schema_11.sql | 27 +++++ 7 files changed, 370 insertions(+), 79 deletions(-) create mode 100644 src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java create mode 100644 src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java create mode 100644 src/main/resources/schema_11.sql diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java new file mode 100644 index 0000000..aa4fb89 --- /dev/null +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dmaap.dbcapi.aaf; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; +import org.onap.dmaap.dbcapi.util.DmaapConfig; + + +public class AafNamespace extends AafObject { + static final Logger logger = Logger.getLogger(AafNamespace.class); + + private String name; + private ArrayList admin; + private ArrayList responsible; + + // in some environments, an AAF Namespace must be owned by a human. + // So, when needed, this var can be set via a property + static private String NsOwnerIdentity; + + public AafNamespace(String ns, String identity ) { + super(); + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + NsOwnerIdentity = p.getProperty( "aaf.NsOwnerIdentity", ""); + this.admin = new ArrayList(); + this.responsible = new ArrayList(); + + this.name = ns; + this.admin.add( identity ); + this.responsible.add( NsOwnerIdentity ); + } + public void setName( String ns ) { + this.name = ns; + } + public String getName() { + return name; + } + public ArrayList getAdmin() { + return admin; + } + public void setAdmin(ArrayList admin) { + this.admin = admin; + } + public ArrayList getResponsible() { + return responsible; + } + public void setResponsible(ArrayList responsible) { + this.responsible = responsible; + } + + + // given an Array of Strings, return a String that is a separated list of quoted strings. + // e.g. input [ a, b, c ] + // output "a", "b", "c" + private String separatedList( ArrayList list, String sep ) { + if (list.isEmpty()) return null; + String aList = new String(); + String delim = ""; + for( String item: list) { + if( ! item.isEmpty()) { + aList += String.format( "%s\"%s\"", delim, item ); + delim = sep; + } + } + return aList; + } + + public String toJSON() { + + String postJSON = String.format(" { \"name\": \"%s\", \"admin\": [", + this.getName() + ); + postJSON += separatedList( this.getAdmin(), "," ); + postJSON += "], \"responsible\":["; + postJSON += separatedList( this.getResponsible(), ","); + postJSON += "]}"; + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + + + +} diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java new file mode 100644 index 0000000..6acbefd --- /dev/null +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * org.onap.dmaap + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dmaap.dbcapi.aaf; + +import org.apache.log4j.Logger; + + +public class AafRole extends AafObject { + static final Logger logger = Logger.getLogger(AafRole.class); + + private String namespace; + private String role; + + public AafRole(String ns, String role) { + super(); + this.namespace = ns; + this.role = role; + } + public void setNamespace( String ns ) { + this.namespace = ns; + } + public String getNamespace() { + return namespace; + } + public void setRole(String role) { + this.role = role; + } + public String getRole() { + return role; + } + public String getFullyQualifiedRole() { + return namespace + "." + role; + } + + public String toJSON() { + + String postJSON = String.format(" { \"name\": \"%s.%s\"}", + this.getNamespace(), + this.getRole() ); + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + + + +} diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java index 112ab11..4778aff 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java @@ -36,9 +36,21 @@ public class AafService extends BaseLoggingClass { private AafConnection aaf; private ServiceType ctype; private String aafURL ; + private String identity; private boolean useAAF = false; + + public String getIdentity() { + return identity; + } + + + public void setIdentity(String identity) { + this.identity = identity; + } + + private String getCred( boolean wPwd ) { String mechIdProperty = null; String pwdProperty = null; @@ -55,7 +67,7 @@ public class AafService extends BaseLoggingClass { logger.error( "Unexpected case for AAF credential type: " + ctype ); return null; } - String user = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" ); + identity = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" ); String pwd = ""; String encPwd = p.getProperty( pwdProperty, "notSet" ); @@ -64,14 +76,15 @@ public class AafService extends BaseLoggingClass { pwd = decryptor.decrypt(encPwd); if ( wPwd ) { - return user + ":" + pwd; + return identity + ":" + pwd; } else { - return user; + return identity; } } + public AafService(ServiceType t ) { DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); aafURL = p.getProperty( "aaf.URL", "https://authentication.domain.netset.com:8095/proxy/"); @@ -92,70 +105,12 @@ public class AafService extends BaseLoggingClass { } public int addPerm(DmaapPerm perm) { - - int rc = -1; logger.info( "entry: addPerm() " ); - String pURL = aafURL + "authz/perm"; - logger.info( "addPerm=" + useAAF ); - if ( useAAF ) { - logger.info( "addPerm: " + perm.toJSON()); - rc = aaf.postAaf( perm, pURL ); - } else { - rc = 201; - } - switch( rc ) { - case 401: - case 403: - errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, getCred( false ) ); - System.exit(1); - case 409: - logger.warn( "Perm already exists. Possible conflict."); - break; - - case 201: - logger.info( "expected response: " + rc); - break; - default : - logger.error( "Unexpected response: " + rc ); - break; - } - - return rc; + return doPost( perm, "authz/perm", 201); } public int addGrant(DmaapGrant grant ) { - - int rc = -1; logger.info( "entry: addGrant() " ); - - String pURL = aafURL + "authz/role/perm"; - logger.info( "addGrant: useAAF=" + useAAF ); - if ( useAAF ) { - logger.info( "addGrant: " + grant.toJSON() ); - rc = aaf.postAaf( grant, pURL ); - } else { - rc = 201; - } - - switch( rc ) { - case 401: - case 403: - errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, getCred( false ) ); - System.exit(1); - break; - - case 409: - logger.warn( "Perm already exists. Possible conflict."); - break; - - case 201: - logger.info( "expected response" ); - break; - default : - logger.error( "Unexpected response: " + rc ); - break; - } - - return rc; + return doPost( grant, "authz/role/perm", 201 ); } public int delGrant( DmaapGrant grant ) { @@ -191,5 +146,49 @@ public class AafService extends BaseLoggingClass { return rc; } + public int addRole(AafRole role) { + logger.info( "entry: addRole() " ); + return doPost( role, "authz/role", 201 ); + } + + + + public int addNamespace(AafNamespace ns) { + logger.info( "entry: addNamespace() " ); + return doPost( ns, "authz/ns", 201 ); + } + + + private int doPost( AafObject obj, String uri, int expect ) { + int rc = -1; + logger.info( "entry: doPost() " ); + String pURL = aafURL + uri; + logger.info( "doPost: useAAF=" + useAAF ); + if ( useAAF ) { + logger.info( "doPost: " + obj.toJSON()); + rc = aaf.postAaf( obj, pURL ); + } else { + rc = expect; + } + switch( rc ) { + case 401: + case 403: + errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, getCred( false ) ); + System.exit(1); + case 409: + logger.warn( "Object for " + uri + " already exists. Possible conflict."); + break; + + default : + if ( rc == expect ) { + logger.info( "expected response: " + rc); + } else { + logger.error( "Unexpected response: " + rc ); + } + break; + } + + return rc; + } } diff --git a/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java b/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java index c2f278d..bfd948b 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java +++ b/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java @@ -49,6 +49,8 @@ public class Topic extends DmaapObject { private String version; private String partitionCount; private String replicationCount; + private String publisherRole; + private String subscriberRole; private ArrayList clients; @@ -282,6 +284,18 @@ public class Topic extends DmaapObject { + public String getPublisherRole() { + return publisherRole; + } + public void setPublisherRole(String publisherRole) { + this.publisherRole = publisherRole; + } + public String getSubscriberRole() { + return subscriberRole; + } + public void setSubscriberRole(String subscriberRole) { + this.subscriberRole = subscriberRole; + } public String toProvJSON() { StringBuilder str = new StringBuilder(); str.append("{ \"topicName\": \""); diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java b/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java index 5c2c8a3..9d9b922 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java @@ -168,6 +168,8 @@ public class DmaapService extends BaseLoggingClass { public String getTopicPerm( String val ) { Dmaap dmaap = dmaapholder.get(); String nsRoot = dmaap.getTopicNsRoot(); + if ( nsRoot == null ) { return null; } + String t; // in ONAP Casablanca, we assume no distinction of environments reflected in topic namespace if ( nsRoot.startsWith(noEnvironmentPrefix) ) { diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java b/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java index 49966d7..a26205c 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java @@ -29,7 +29,10 @@ import java.util.Set; import javax.ws.rs.core.Response.Status; +import org.onap.dmaap.dbcapi.aaf.AafNamespace; +import org.onap.dmaap.dbcapi.aaf.AafRole; import org.onap.dmaap.dbcapi.aaf.AafService; +import org.onap.dmaap.dbcapi.aaf.DmaapGrant; import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; import org.onap.dmaap.dbcapi.aaf.DmaapPerm; import org.onap.dmaap.dbcapi.database.DatabaseClass; @@ -112,6 +115,94 @@ public class TopicService extends BaseLoggingClass { apiError.setCode(Status.OK.getStatusCode()); return t; } + + private void aafTopicSetup(Topic topic, ApiError err ) { + + String t = dmaapSvc.getTopicPerm(); + if ( t == null ) { + err.setCode(500); + err.setMessage("Unable to establish AAF namespace root: (check /dmaap object)" ); + err.setFields("topicNsRoot"); + return; + } + + // establish AAF Connection using TopicMgr identity + AafService aaf = new AafService(ServiceType.AAF_TopicMgr); + + + + // create AAF namespace for this topic + AafNamespace ns = new AafNamespace( topic.getFqtn(), aaf.getIdentity()); + { + int rc = aaf.addNamespace( ns ); + if ( rc != 201 && rc != 409 ) { + err.setCode(500); + err.setMessage("Unexpected response from AAF:" + rc ); + err.setFields("namespace:" + topic.getFqtn() + " identity="+ aaf.getIdentity()); + return; + } + } + + // create AAF Roles for MR clients of this topic + String rn = "publisher"; + AafRole pubRole = new AafRole( topic.getFqtn(), rn ); + int rc = aaf.addRole( pubRole ); + if ( rc != 201 && rc != 409 ) { + err.setCode(500); + err.setMessage("Unexpected response from AAF:" + rc ); + err.setFields("topic:" + topic.getFqtn() + " role="+ rn); + return; + } + topic.setPublisherRole( pubRole.getFullyQualifiedRole() ); + + rn = "subscriber"; + AafRole subRole = new AafRole( topic.getFqtn(), rn ); + rc = aaf.addRole( subRole ); + if ( rc != 201 && rc != 409 ) { + err.setCode(500); + err.setMessage("Unexpected response from AAF:" + rc ); + err.setFields("topic:" + topic.getFqtn() + " role="+ rn); + return; + } + topic.setSubscriberRole( subRole.getFullyQualifiedRole() ); + + + // create AAF perms checked by MR + String instance = ":topic." + topic.getFqtn(); + String[] actions = { "pub", "sub", "view" }; + for ( String action : actions ){ + DmaapPerm perm = new DmaapPerm( t, instance, action ); + rc = aaf.addPerm( perm ); + if ( rc != 201 && rc != 409 ) { + err.setCode(500); + err.setMessage("Unexpected response from AAF:" + rc ); + err.setFields("t="+t + " instance="+ instance + " action="+ action); + return; + } + // Grant perms to our default Roles + if ( action.equals( "pub") || action.equals( "view") ) { + DmaapGrant g = new DmaapGrant( perm, pubRole.getFullyQualifiedRole() ); + rc = aaf.addGrant( g ); + if ( rc != 201 && rc != 409 ) { + err.setCode(rc); + err.setMessage( "Grant of " + perm.toString() + " failed for " + pubRole.getFullyQualifiedRole() ); + logger.warn( err.getMessage()); + return; + } + } + if ( action.equals( "sub") || action.equals( "view") ) { + DmaapGrant g = new DmaapGrant( perm, subRole.getFullyQualifiedRole() ); + rc = aaf.addGrant( g ); + if ( rc != 201 && rc != 409 ) { + err.setCode(rc); + err.setMessage( "Grant of " + perm.toString() + " failed for " + subRole.getFullyQualifiedRole() ); + logger.warn( err.getMessage()); + return; + } + } + + } + } public Topic addTopic( Topic topic, ApiError err, Boolean useExisting ) { logger.info( "Entry: addTopic"); @@ -135,23 +226,11 @@ public class TopicService extends BaseLoggingClass { topic.setFqtn( nFqtn ); - AafService aaf = new AafService(ServiceType.AAF_TopicMgr); - - String t = dmaapSvc.getTopicPerm(); - - String instance = ":topic." + topic.getFqtn(); + aafTopicSetup( topic, err ); + if ( err.getCode() >= 400 ) { + return null; + } - String[] actions = { "pub", "sub", "view" }; - for ( String action : actions ){ - DmaapPerm perm = new DmaapPerm( t, instance, action ); - int rc = aaf.addPerm( perm ); - if ( rc != 201 && rc != 409 ) { - err.setCode(500); - err.setMessage("Unexpected response from AAF:" + rc ); - err.setFields("t="+t + " instance="+ instance + " action="+ action); - return null; - } - } if ( topic.getReplicationCase().involvesGlobal() ) { if ( topic.getGlobalMrURL() == null ) { topic.setGlobalMrURL(defaultGlobalMrHost); diff --git a/src/main/resources/schema_11.sql b/src/main/resources/schema_11.sql new file mode 100644 index 0000000..de848b1 --- /dev/null +++ b/src/main/resources/schema_11.sql @@ -0,0 +1,27 @@ +--- +-- ============LICENSE_START======================================================= +-- OpenECOMP - org.onap.dbcapi +-- ================================================================================ +-- Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. +-- ================================================================================ +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- ============LICENSE_END========================================================= +--- + + +@alter table topic + add column publisher_role varchar(100), + add column subscriber_role varchar(100) +; + +update dmaapbc_sch_ver set version = 11 where version = 10; -- 2.16.6