diff options
Diffstat (limited to 'src/main/java/org')
21 files changed, 701 insertions, 224 deletions
diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java index edc522e..2ebf403 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java @@ -3,13 +3,14 @@ * org.onap.dmaap * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2018 IBM. * ================================================================================ * 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. @@ -42,15 +43,15 @@ import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; * this service uses the AAF Lur object to lookup identities and perms */ public class AafLurService extends BaseLoggingClass { - - static Logger logger = Logger.getLogger(AafLurService.class.getName()); - - private static AAFConHttp aafcon; - private static AAFLurPerm aafLur; - private static AAFAuthn<?> aafAuthn; + static Logger log = Logger.getLogger(AafLurService.class.getName()); + + + private static AAFConHttp aafcon; + private static AAFLurPerm aafLur; + private static AAFAuthn<?> aafAuthn; + - /* * singleton pattern suggested by AAF */ @@ -58,7 +59,7 @@ public class AafLurService extends BaseLoggingClass { private AafLurService() {} - + private static void init( Access myAccess ) throws APIException, CadiException, LocatorException { appLogger.info( "myAccess=" + myAccess ); try { @@ -66,54 +67,53 @@ public class AafLurService extends BaseLoggingClass { } catch ( CadiException | LocatorException e) { appLogger.error( "Failure of AAFConHttp: " + e.getMessage() ); errorLogger.error( "Failure of AAFConHttp: " + e.getMessage() ); - logger.error(e); - e.printStackTrace(); + log.error(e); + throw e; - } + } try { aafLur = aafcon.newLur(); } catch ( CadiException e) { appLogger.error( "Failure of newLur(): " + e.getMessage() ); errorLogger.error( "Failure of newLur(): " + e.getMessage() ); - logger.error(e); - e.printStackTrace(); + log.error(e); + throw e; - } - aafAuthn = aafcon.newAuthn( aafLur ); + } + aafAuthn = aafcon.newAuthn( aafLur ); } - + public static synchronized AafLurService getInstance( Access myAccess ) throws APIException, CadiException, LocatorException{ if ( singleton == null ) { singleton = new AafLurService(); try { init( myAccess ); } catch (APIException | CadiException | LocatorException e) { - // TODO Auto-generated catch block - logger.error(e); - e.printStackTrace(); + + log.error(e); throw e; - } - + } + } return singleton; } - + public boolean checkPerm(String ns, String fqi, String pwd, DmaapPerm p) throws IOException, CadiException { boolean rc = false; - + if ( aafAuthn == null ) { appLogger.error( "AafLurService: aafAuthn not set as expected."); return rc; } - + String ok = aafAuthn.validate( fqi, pwd ); if ( ok != null ) { appLogger.info( "FAILED validation of fqi=" + fqi + "with response:" + ok ); return rc; - } - + } + Principal principal = new UnAuthPrincipal( fqi ); // if we pass ns as first arg to AAFPermission constructor it gets prpended to the instance... // as in ns|instance|type|action. we don't want that. @@ -127,7 +127,7 @@ public class AafLurService extends BaseLoggingClass { if (rc == flag ) { return rc; } - + List<Permission> perms = new ArrayList<Permission>(); aafLur.fishAll( principal, perms); String key = aafPerm.getKey(); @@ -138,10 +138,8 @@ public class AafLurService extends BaseLoggingClass { appLogger.info( principal + " has non-matching perm " + prm.getKey() ); } } - - + return rc; - - + } } 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<String> admin; + private ArrayList<String> 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<String>(); + this.responsible = new ArrayList<String>(); + + 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<String> getAdmin() { + return admin; + } + public void setAdmin(ArrayList<String> admin) { + this.admin = admin; + } + public ArrayList<String> getResponsible() { + return responsible; + } + public void setResponsible(ArrayList<String> 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<String> 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..253ad11 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,16 @@ 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 addUserRole( AafUserRole ur ) { + logger.info( "entry: addUserRole() " ); + return doPost( ur, "authz/userRole", 201 ); } public int delGrant( DmaapGrant grant ) { @@ -191,5 +150,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/aaf/AafUserRole.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java new file mode 100644 index 0000000..7b4f882 --- /dev/null +++ b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java @@ -0,0 +1,69 @@ +/*- + * ============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 AafUserRole extends AafObject { + static final Logger logger = Logger.getLogger(AafUserRole.class); + + private String identity; + private String role; + + + + public AafUserRole(String identity, String role ) { + super(); + this.identity = identity; + this.role = role; + } + + public void setRole(String role) { + this.role = role; + } + public String getRole() { + return role; + } + + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + public String toJSON() { + + String postJSON = String.format(" { \"user\": \"%s\", \"role\": \"%s\" }", + this.getIdentity(), + this.getRole() + ); + logger.info( "returning JSON: " + postJSON); + + return postJSON; + } + + + + +} diff --git a/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java b/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java index 0c81fa9..5b4d7de 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java +++ b/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java @@ -3,6 +3,7 @@ * org.onap.dmaap * ================================================================================ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2018 IBM. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,13 +39,14 @@ import org.onap.dmaap.dbcapi.util.DmaapConfig; public class AafLurAndFish implements ApiAuthorizationCheckInterface { private AafLurService svc; - private static String api_namespace; + private static String apiNamespace; + private static final String ERROR="Error"; static final Logger logger = Logger.getLogger(AafLurAndFish.class); AafLurAndFish() throws AuthenticationErrorException { DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); - api_namespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api"); + apiNamespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api"); String cadiprop = p.getProperty( "cadi.properties", "/opt/app/osaaf/local/org.onap.dmaap-bc.props"); logger.info( "cadiprops in " + cadiprop ); @@ -58,7 +60,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface { } } catch ( IOException e ) { logger.error( "Unable to load " + cadiprop ); - logger.error("Error", e); + logger.error(ERROR, e); throw new AuthenticationErrorException( ); } try { @@ -66,7 +68,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface { svc = AafLurService.getInstance(myAccess); } catch (APIException | CadiException | LocatorException e ) { - logger.error("Error", e); + logger.error(ERROR, e); logger.error( e.toString() ); throw new AuthenticationErrorException(); } @@ -76,13 +78,13 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface { public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException { try { - boolean resp = svc.checkPerm( api_namespace, mechid, pwd, p ); + boolean resp = svc.checkPerm( apiNamespace, mechid, pwd, p ); boolean flag = false; if ( resp == flag ) { throw new AuthenticationErrorException(); } } catch ( IOException | CadiException e ) { - logger.error("Error", e); + logger.error(ERROR, e); logger.error( e.toString() ); throw new AuthenticationErrorException(); } @@ -97,7 +99,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface { alaf.check("mmanager@people.osaaf.org", "demo123456!", p); } catch (AuthenticationErrorException aee ) { logger.error( "Check failed for: " + p.toJSON()); - System.exit(-1); + System.exit(-1); } logger.info( "Check succeeded for: " + p.toJSON() ); diff --git a/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java b/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java index 26c494f..94d671f 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java +++ b/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java @@ -3,6 +3,7 @@ * org.onap.dmaap * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2018 IBM. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,9 +38,6 @@ import javax.net.ssl.HttpsURLConnection; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.onap.dmaap.dbcapi.aaf.AafDecrypt; -import org.onap.dmaap.dbcapi.aaf.AafService; -import org.onap.dmaap.dbcapi.aaf.DecryptionInterface; -import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; import org.onap.dmaap.dbcapi.model.ApiError; @@ -227,13 +225,15 @@ public class MrProvConnection extends BaseLoggingClass{ } } catch (Exception e) { - System.err.println("Unable to read response " ); - e.printStackTrace(); + errorLogger.error("Unable to read response " ); + } finally { try { uc.disconnect(); - } catch ( Exception e ) {} + } catch ( Exception e ) { + errorLogger.error("Unable to disconnect"); + } } return new String( rc +": " + responsemessage ); diff --git a/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java b/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java index 11b0d15..934b7bb 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java +++ b/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java @@ -200,22 +200,18 @@ public class DatabaseClass extends BaseLoggingClass { mirrors = new HashMap<>(); } dmaap.init(new Dmaap("0", "", "", "", "", "", "", "")); - // check for, and set up initial data, if it isn't already there + // force initial read from DB, if it exists + @SuppressWarnings("unused") Dmaap dmx = dmaap.get(); - if ("0".equals(dmx.getVersion())) { - - dmx = new Dmaap("0", "", "", "", "", "", "", ""); - dmx.setDmaapName(p.getProperty("DmaapName")); - dmx.setDrProvUrl("https://" + p.getProperty("DR.provhost", "notSet")); - dmx.setTopicNsRoot(p.getProperty("topicNsRoot")); - dmx.setBridgeAdminTopic("DCAE_MM_AGENT"); + + // old code in this spot would read from properties file as part of init. + // but all those properties are now set via /dmaap API - dmaap.update(dmx); - } } catch (Exception e) { errorLogger.error("Error", e); errorLogger.error(DmaapbcLogMessageEnum.DB_UPDATE_ERROR, e.getMessage()); } + } public static synchronized String getNextClientId() { diff --git a/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java b/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java index 8e804b2..1b3f824 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java +++ b/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java @@ -30,8 +30,11 @@ import javax.xml.bind.annotation.XmlRootElement; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; +import io.swagger.annotations.ApiModelProperty; + @XmlRootElement public abstract class DmaapObject extends BaseLoggingClass { + @ApiModelProperty( value = "datestamp for last update to this object") protected Date lastMod; protected DmaapObject_Status status; @@ -81,6 +84,7 @@ public abstract class DmaapObject extends BaseLoggingClass { } } + @ApiModelProperty( hidden=true ) public boolean isStatusValid() { if ( this.status == DmaapObject_Status.VALID ) { return true; diff --git a/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java b/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java index 580800c..f182fd6 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java +++ b/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java @@ -26,15 +26,25 @@ import javax.xml.bind.annotation.XmlRootElement; import org.onap.dmaap.dbcapi.database.DatabaseClass; +import io.swagger.annotations.ApiModelProperty; + @XmlRootElement public class MR_Client extends DmaapObject { + @ApiModelProperty( value="a tag indicating a logical deployment site") private String dcaeLocationName; + @ApiModelProperty( value="the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic") private String topicURL; + @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi") private String fqtn; + @ApiModelProperty( value="an AAF Role to be granted an appropriate Permission. If specified, takes precedence over clientIdentity, for backwards compatibility.") private String clientRole; + @ApiModelProperty( value="one or more actions from the set (\"pub\", \"sub\", \"view\") for which this client needs Permission") private String[] action; + @ApiModelProperty( value="a unique identifier generated by dbcapi for this client") private String mrClientId; + @ApiModelProperty( value="an AAF identity to be associated to an appropriate topic Role") + private String clientIdentity; public MR_Client() { @@ -97,7 +107,24 @@ public class MR_Client extends DmaapObject { public void setAction(String[] action) { this.action = action; } - + + @ApiModelProperty( hidden=true ) + public boolean isPublisher() { + return hasAction( "pub"); + } + @ApiModelProperty( hidden=true ) + public boolean isSubscriber() { + return hasAction( "sub"); + } + + public boolean hasAction( String val ) { + for (String s: this.action) { + if ( s!= null && s.equals(val)) { + return true; + } + } + return false; + } public String getMrClientId() { return mrClientId; } @@ -115,5 +142,21 @@ public class MR_Client extends DmaapObject { public void setTopicURL(String topicURL) { this.topicURL = topicURL; } + + public String getClientIdentity() { + return clientIdentity; + } + + public void setClientIdentity(String clientIdentity) { + this.clientIdentity = clientIdentity; + } + public boolean hasClientIdentity() { + if ( this.clientIdentity == null || this.clientIdentity.isEmpty() ) { + return false; + } else { + return true; + } + } + } 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 6364382..7eea5f1 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java +++ b/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java @@ -30,29 +30,54 @@ import javax.xml.bind.annotation.XmlRootElement; import org.onap.dmaap.dbcapi.util.DmaapConfig; +import io.swagger.annotations.ApiModelProperty; + import org.onap.dmaap.dbcapi.service.DmaapService; +import org.onap.dmaap.dbcapi.service.TopicService; @XmlRootElement public class Topic extends DmaapObject { + @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`") private String fqtn; + @ApiModelProperty( value="the short name used by humans, and utilized to construct the `FQTN`") private String topicName; + @ApiModelProperty( value="a description of what this Topic is used for") private String topicDescription; private String tnxEnabled; + @ApiModelProperty( value="a label used to identify who requested this `Topic` to be provisioned. In the future this " + + "may be an AAF Identity.") private String owner; + @ApiModelProperty( value="a reference to an identifier that describes a data format used for this `Topic`") private String formatUuid; + @ApiModelProperty( value="An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances") private ReplicationType replicationCase; + @ApiModelProperty( value="the URL of an outside MR instance") private String globalMrURL; // optional: URL of global MR to replicate to/from + @ApiModelProperty( value="the construction rule for the `fqtn` field") private FqtnType fqtnStyle; + @ApiModelProperty( value="a hook for any versioning needed for managing a `Topic` over time") private String version; - + @ApiModelProperty( value="the kafka attribute for specifying the number of partitions") + private String partitionCount; + @ApiModelProperty( value="the kafka attribute for specifying replication within an `MR_Cluster` instance") + private String replicationCount; + @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`") + private String publisherRole; + @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`") + private String subscriberRole; + + @ApiModelProperty( value="an array of `MR_Client` objects associated to this `Topic`") private ArrayList<MR_Client> clients; private static Dmaap dmaap = new DmaapService().getDmaap(); - + + private static String defaultPartitionCount; + private static String defaultReplicationCount; + // during unit testing, discovered that presence of dots in some values // creates an unplanned topic namespace as we compose the FQTN. // this may create sensitivity (i.e. 403) for subsequent creation of AAF perms, so best to not allow it @@ -118,11 +143,24 @@ public class Topic extends DmaapObject { //this.dcaeLocationName = dcaeLocationName; this.tnxEnabled = tnxEnabled; this.owner = owner; + this.init(); this.setLastMod(); + logger.debug( "Topic constructor w args " + this.getLastMod() ); + } + + public Topic init() { + DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); + + defaultPartitionCount = p.getProperty( "MR.partitionCount", "2"); + defaultReplicationCount = p.getProperty( "MR.replicationCount", "1"); + this.setStatus( DmaapObject_Status.NEW ); this.replicationCase = ReplicationType.Validator("none"); this.fqtnStyle = FqtnType.Validator("none"); - logger.debug( "Topic constructor " + this.getLastMod() ); + this.setPartitionCount( defaultPartitionCount ); + this.setReplicationCount( defaultReplicationCount ); + + return this; } // expects a String in JSON format, with known fields to populate Topic object @@ -144,6 +182,7 @@ public class Topic extends DmaapObject { this.setStatus( (String) jsonObj.get( "status" ) ); this.setReplicationCase( ReplicationType.Validator( (String) jsonObj.get( "replicationCase" ) )); this.setFqtnStyle( FqtnType.Validator( (String) jsonObj.get( "fqtnStyle" ) ) ); + this.setPartitionCount( (String) jsonObj.get("partitionCount")); } public String getFqtn() { @@ -177,6 +216,18 @@ public class Topic extends DmaapObject { public void setOwner(String owner) { this.owner = owner; } + public String getPartitionCount() { + return partitionCount; + } + public void setPartitionCount(String partitions) { + this.partitionCount = partitions; + } + public String getReplicationCount() { + return replicationCount; + } + public void setReplicationCount(String replicationCount) { + this.replicationCount = replicationCount; + } public void setClients(ArrayList<MR_Client> clients) { @@ -187,6 +238,7 @@ public class Topic extends DmaapObject { return clients; } + @ApiModelProperty( hidden=true ) public int getNumClients() { if ( this.clients == null ) { return 0; @@ -212,13 +264,6 @@ public class Topic extends DmaapObject { return replicationCase; } - - - /* - public void setReplicationCase(String val) { - this.replicationCase = ReplicationType.Validator(val); - } - */ public void setReplicationCase(ReplicationType t) { this.replicationCase = t; @@ -256,17 +301,34 @@ 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\": \""); str.append( this.getFqtn() ); str.append( "\", \"topicDescription\": \""); str.append( this.getTopicDescription()); - str.append( "\", \"partitionCount\": \"2\", \"replicationCount\": \"1\" } "); + str.append( "\", \"partitionCount\": \""); + str.append( this.getPartitionCount()); + str.append( "\", \"replicationCount\": \""); + str.append( this.getReplicationCount()); + str.append( "\" } "); + logger.info( str.toString() ); return str.toString(); } - + @ApiModelProperty( hidden=true ) public byte[] getBytes() { return toProvJSON().getBytes(StandardCharsets.UTF_8); } diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java index becc7df..192b63d 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java @@ -37,7 +37,6 @@ import javax.ws.rs.core.Response.Status; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; import org.onap.dmaap.dbcapi.model.BrTopic; -import org.onap.dmaap.dbcapi.model.Dmaap; import org.onap.dmaap.dbcapi.model.MirrorMaker; import org.onap.dmaap.dbcapi.service.ApiService; import org.onap.dmaap.dbcapi.service.MirrorMakerService; @@ -62,7 +61,7 @@ public class BridgeResource extends BaseLoggingClass { + "If detail param is true, list topics names, else just a count is returned.", response = BrTopic.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = BrTopic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getBridgedTopics(@QueryParam("mmagent") String mmagent, @@ -150,7 +149,7 @@ public class BridgeResource extends BaseLoggingClass { + "If split param is true, spread whitelist over smaller mmagents.", response = MirrorMaker.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = BrTopic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response putBridgedTopics(@QueryParam("mmagent") String mmagent, diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java index 41a491d..58a39df 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java @@ -38,7 +38,6 @@ import javax.ws.rs.core.Response.Status; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; -import org.onap.dmaap.dbcapi.model.DR_Pub; import org.onap.dmaap.dbcapi.model.DR_Sub; import org.onap.dmaap.dbcapi.model.Feed; import org.onap.dmaap.dbcapi.service.ApiService; @@ -63,7 +62,7 @@ public class DR_SubResource extends BaseLoggingClass { notes = "Returns array of `DR_Sub` objects. Add filter for feedId.", response = DR_Sub.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getDr_Subs() { @@ -83,7 +82,7 @@ public class DR_SubResource extends BaseLoggingClass { notes = "Create a `DR_Sub` object. ", response = DR_Sub.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addDr_Sub( @@ -132,7 +131,7 @@ public class DR_SubResource extends BaseLoggingClass { notes = "Update a `DR_Sub` object, selected by subId", response = DR_Sub.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{subId}") @@ -173,7 +172,7 @@ public class DR_SubResource extends BaseLoggingClass { notes = "Delete a `DR_Sub` object, selected by subId", response = DR_Sub.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{subId}") @@ -202,7 +201,7 @@ public class DR_SubResource extends BaseLoggingClass { notes = "Retrieve a `DR_Sub` object, selected by subId", response = DR_Sub.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = DR_Sub.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{subId}") diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java index 25e6f3a..8cb5336 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java @@ -45,7 +45,6 @@ import org.apache.log4j.Logger; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; import org.onap.dmaap.dbcapi.model.DcaeLocation; -import org.onap.dmaap.dbcapi.model.Dmaap; import org.onap.dmaap.dbcapi.service.ApiService; import org.onap.dmaap.dbcapi.service.DcaeLocationService; @@ -64,7 +63,7 @@ public class DcaeLocationResource extends BaseLoggingClass { notes = "Returns array of `dcaeLocation` objects. All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer* (ecomp or edge).", response = DcaeLocation.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getDcaeLocations() { @@ -82,7 +81,7 @@ public class DcaeLocationResource extends BaseLoggingClass { notes = "Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer* (ecomp or edge).", response = DcaeLocation.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addDcaeLocation( @@ -108,7 +107,7 @@ public class DcaeLocationResource extends BaseLoggingClass { notes = "update the openStackAvailabilityZone of a dcaeLocation", response = DcaeLocation.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{locationName}") @@ -135,9 +134,9 @@ public class DcaeLocationResource extends BaseLoggingClass { } @DELETE - @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = Dmaap.class) + @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = DcaeLocation.class) @ApiResponses( value = { - @ApiResponse( code = 204, message = "Success", response = Dmaap.class), + @ApiResponse( code = 204, message = "Success", response = DcaeLocation.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{locationName}") @@ -151,9 +150,9 @@ public class DcaeLocationResource extends BaseLoggingClass { } @GET - @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = Dmaap.class) + @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = DcaeLocation.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = Dmaap.class), + @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{locationName}") diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java index 97336c1..a7b4f73 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java @@ -31,29 +31,20 @@ import javax.jws.WebParam; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.UriInfo; - -import org.apache.log4j.Logger; -import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; -import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; import org.onap.dmaap.dbcapi.model.ApiError; import org.onap.dmaap.dbcapi.model.DR_Pub; -import org.onap.dmaap.dbcapi.model.DR_Sub; import org.onap.dmaap.dbcapi.model.Feed; -import org.onap.dmaap.dbcapi.model.Topic; import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; import org.onap.dmaap.dbcapi.service.ApiService; import org.onap.dmaap.dbcapi.service.FeedService; @@ -71,7 +62,7 @@ public class FeedResource extends BaseLoggingClass { notes = "Returns array of `Feed` objects.", response = Feed.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Feed.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getFeeds( @@ -96,7 +87,7 @@ public class FeedResource extends BaseLoggingClass { notes = "Create a of `Feed` object.", response = Feed.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Feed.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addFeed( @@ -151,7 +142,7 @@ public class FeedResource extends BaseLoggingClass { notes = "Update a `Feed` object, specified by id.", response = Feed.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Feed.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{id}") @@ -196,7 +187,7 @@ public class FeedResource extends BaseLoggingClass { notes = "Delete a `Feed` object, specified by id.", response = Feed.class) @ApiResponses( value = { - @ApiResponse( code = 204, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 204, message = "Success", response = Feed.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{id}") diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java index 8f6a506..a621338 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java @@ -42,7 +42,6 @@ import javax.ws.rs.core.Response.Status; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; -import org.onap.dmaap.dbcapi.model.DR_Pub; import org.onap.dmaap.dbcapi.model.MR_Client; import org.onap.dmaap.dbcapi.model.MR_Cluster; import org.onap.dmaap.dbcapi.model.Topic; @@ -66,7 +65,7 @@ public class MR_ClientResource extends BaseLoggingClass { notes = "Returns array of `MR_Client` objects.", response = MR_Client.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getMr_Clients() { @@ -80,11 +79,14 @@ public class MR_ClientResource extends BaseLoggingClass { } @POST - @ApiOperation( value = "return MR_Client details", - notes = "Create a `MR_Client` object.", + @ApiOperation( value = "Associate an MR_Client object to a Topic", + notes = "Create a `MR_Client` object." + + "The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic." + + " In legacy implementation, the `clientRole` is granted appropriate permission in AAF." + + " Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.", response = MR_Client.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addMr_Client( @@ -95,7 +97,11 @@ public class MR_ClientResource extends BaseLoggingClass { try { resp.required( "fqtn", client.getFqtn(), ""); resp.required( "dcaeLocationName", client.getDcaeLocationName(), ""); - resp.required( "clientRole", client.getClientRole(), "" ); + String s = client.getClientRole(); + if ( s == null ) { + s = client.getClientIdentity(); + } + resp.required( "clientRole or clientIdentity", s, "" ); resp.required( "action", client.getAction(), ""); } catch ( RequiredFieldException rfe ) { @@ -140,11 +146,11 @@ public class MR_ClientResource extends BaseLoggingClass { } @PUT - @ApiOperation( value = "return MR_Client details", + @ApiOperation( value = "Update an MR_Client object", notes = "Update a `MR_Client` object, specified by clientId", response = MR_Client.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{clientId}") @@ -176,11 +182,11 @@ public class MR_ClientResource extends BaseLoggingClass { } @DELETE - @ApiOperation( value = "return MR_Client details", + @ApiOperation( value = "Delete an MR_Client object", notes = "Delete a `MR_Client` object, specified by clientId", response = MR_Client.class) @ApiResponses( value = { - @ApiResponse( code = 204, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 204, message = "Success", response = MR_Client.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{subId}") @@ -208,7 +214,7 @@ public class MR_ClientResource extends BaseLoggingClass { notes = "Retrieve a `MR_Client` object, specified by clientId", response = MR_Client.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Client.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{subId}") diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java index 41aaead..50ab4db 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java @@ -42,7 +42,6 @@ import javax.ws.rs.core.Response.Status; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; -import org.onap.dmaap.dbcapi.model.DR_Pub; import org.onap.dmaap.dbcapi.model.MR_Cluster; import org.onap.dmaap.dbcapi.service.ApiService; import org.onap.dmaap.dbcapi.service.MR_ClientService; @@ -64,7 +63,7 @@ public class MR_ClusterResource extends BaseLoggingClass { notes = "Returns array of `MR_Cluster` objects.", response = MR_Cluster.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getMr_Clusters() { @@ -82,7 +81,7 @@ public class MR_ClusterResource extends BaseLoggingClass { notes = "Create an `MR_Cluster` object.", response = MR_Cluster.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addMr_Cluster( @@ -109,7 +108,7 @@ public class MR_ClusterResource extends BaseLoggingClass { notes = "Update an `MR_Cluster` object, specified by clusterId.", response = MR_Cluster.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{clusterId}") @@ -138,7 +137,7 @@ public class MR_ClusterResource extends BaseLoggingClass { notes = "Delete an `MR_Cluster` object, specified by clusterId.", response = MR_Cluster.class) @ApiResponses( value = { - @ApiResponse( code = 204, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 204, message = "Success", response = MR_Cluster.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{clusterId}") @@ -164,7 +163,7 @@ public class MR_ClusterResource extends BaseLoggingClass { notes = "Retrieve an `MR_Cluster` object, specified by clusterId.", response = MR_Cluster.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{clusterId}") diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java index 8ed2558..be1b3ac 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java @@ -43,7 +43,6 @@ import javax.ws.rs.core.Response.Status; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; import org.onap.dmaap.dbcapi.model.ApiError; -import org.onap.dmaap.dbcapi.model.DR_Pub; import org.onap.dmaap.dbcapi.model.ReplicationType; import org.onap.dmaap.dbcapi.model.FqtnType; import org.onap.dmaap.dbcapi.model.Topic; @@ -58,11 +57,16 @@ import org.onap.dmaap.dbcapi.util.DmaapConfig; @Authorization public class TopicResource extends BaseLoggingClass { private static FqtnType defaultTopicStyle; + private static String defaultPartitionCount; + private static String defaultReplicationCount; TopicService mr_topicService = new TopicService(); public TopicResource() { DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); - defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT")); + defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT")); + defaultPartitionCount = p.getProperty( "MR.partitionCount", "2"); + defaultReplicationCount = p.getProperty( "MR.replicationCount", "1"); + logger.info( "Setting defaultTopicStyle=" + defaultTopicStyle ); } @@ -71,7 +75,7 @@ public class TopicResource extends BaseLoggingClass { notes = "Returns array of `Topic` objects.", response = Topic.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Topic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response getTopics() { @@ -87,11 +91,14 @@ public class TopicResource extends BaseLoggingClass { } @POST - @ApiOperation( value = "return Topic details", - notes = "Create `Topic` object.", + @ApiOperation( value = "Create a Topic object", + notes = "Create `Topic` object." + + "For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`." + + " Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic." + + " MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.", response = Topic.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Topic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) public Response addTopic( @@ -119,6 +126,14 @@ public class TopicResource extends BaseLoggingClass { logger.info( "setting defaultTopicStyle=" + defaultTopicStyle + " for topic " + topic.getTopicName() ); topic.setFqtnStyle( defaultTopicStyle ); } + String pc = topic.getPartitionCount(); + if ( pc == null ) { + topic.setPartitionCount(defaultPartitionCount); + } + String rc = topic.getReplicationCount(); + if ( rc == null ) { + topic.setReplicationCount(defaultReplicationCount); + } topic.setLastMod(); Boolean flag = false; if (useExisting != null) { @@ -137,7 +152,7 @@ public class TopicResource extends BaseLoggingClass { notes = "Update a `Topic` object, identified by topicId", response = Topic.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Topic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{topicId}") @@ -157,7 +172,7 @@ public class TopicResource extends BaseLoggingClass { notes = "Delete a `Topic` object, identified by topicId", response = Topic.class) @ApiResponses( value = { - @ApiResponse( code = 204, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 204, message = "Success", response = Topic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{topicId}") @@ -186,7 +201,7 @@ public class TopicResource extends BaseLoggingClass { notes = "Retrieve a `Topic` object, identified by topicId", response = Topic.class) @ApiResponses( value = { - @ApiResponse( code = 200, message = "Success", response = DR_Pub.class), + @ApiResponse( code = 200, message = "Success", response = Topic.class), @ApiResponse( code = 400, message = "Error", response = ApiError.class ) }) @Path("/{topicId}") 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 5aae1d4..48ee661 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java @@ -43,6 +43,7 @@ import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum; import org.onap.dmaap.dbcapi.model.ApiError; import org.onap.dmaap.dbcapi.model.Dmaap; import org.onap.dmaap.dbcapi.model.MR_Client; +import org.onap.dmaap.dbcapi.model.ReplicationType; import org.onap.dmaap.dbcapi.model.Topic; import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; import org.onap.dmaap.dbcapi.util.DmaapConfig; @@ -57,7 +58,7 @@ public class DmaapService extends BaseLoggingClass { String topicFactory; // = "org.openecomp.dcae.dmaap.topicFactory"; String topicMgrRole; // = "org.openecomp.dmaapBC.TopicMgr"; - String dcaeTopicNs; // = "org.openecomp.dcae.dmaap"; + private boolean multiSite; @@ -65,14 +66,14 @@ public class DmaapService extends BaseLoggingClass { DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig(); topicFactory = p.getProperty("MR.TopicFactoryNS", "MR.topicFactoryNS.not.set"); topicMgrRole = p.getProperty("MR.TopicMgrRole", "MR.TopicMgrRole.not.set" ); - dcaeTopicNs = dmaapholder.get().getTopicNsRoot(); + multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true")); noEnvironmentPrefix = p.getProperty( "AAF.NoEnvironmentPrefix", "org.onap"); logger.info( "DmaapService settings: " + " topicFactory=" + topicFactory + " topicMgrRole=" + topicMgrRole + - " dcaeTopicNs=" + dcaeTopicNs + + " multisite=" + multiSite + " noEnvironmentPrefix=" + noEnvironmentPrefix ); @@ -92,7 +93,7 @@ public class DmaapService extends BaseLoggingClass { nd.setLastMod(); dmaapholder.update(nd); - + AafService aaf = new AafService( ServiceType.AAF_Admin); ApiPolicy apiPolicy = new ApiPolicy(); if ( apiPolicy.getUseAuthClass() ) { @@ -136,6 +137,7 @@ public class DmaapService extends BaseLoggingClass { if ( ! dmaap.isStatusValid() || ! nd.getDmaapName().equals(dmaap.getDmaapName()) || dmaap.getVersion().equals( "0") ) { nd.setLastMod(); dmaapholder.update(nd); //need to set this so the following perms will pick up any new vals. + //dcaeTopicNs = dmaapholder.get().getTopicNsRoot(); ApiPolicy apiPolicy = new ApiPolicy(); if ( apiPolicy.getUseAuthClass()) { ApiPerms p = new ApiPerms(); @@ -166,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) ) { @@ -190,7 +194,7 @@ public class DmaapService extends BaseLoggingClass { private boolean setTopicMgtPerms( Dmaap nd, AafService aaf ){ String[] actions = { "create", "destroy" }; - String instance = ":" + dcaeTopicNs + "." + nd.getDmaapName() + ".mr.topic:" + dcaeTopicNs + "." + nd.getDmaapName(); + String instance = ":" + nd.getTopicNsRoot() + "." + nd.getDmaapName() + ".mr.topic:" + nd.getTopicNsRoot() + "." + nd.getDmaapName(); for( String action : actions ) { @@ -210,7 +214,7 @@ public class DmaapService extends BaseLoggingClass { } } - String t = dcaeTopicNs +"." + nd.getDmaapName() + ".mr.topic"; + String t = nd.getTopicNsRoot() +"." + nd.getDmaapName() + ".mr.topic"; String[] s = { "view", "pub", "sub" }; actions = s; instance = "*"; @@ -279,12 +283,14 @@ public class DmaapService extends BaseLoggingClass { clients.add( nClient ); // initialize Topic - Topic mmaTopic = new Topic(); + Topic mmaTopic = new Topic().init(); mmaTopic.setTopicName(dmaap.getBridgeAdminTopic()); mmaTopic.setClients(clients); mmaTopic.setOwner("BusController"); mmaTopic.setTopicDescription("topic reserved for MirrorMaker Administration"); mmaTopic.setTnxEnabled("false"); + mmaTopic.setPartitionCount("1"); // a single partition should guarantee message order + ApiError err = new ApiError(); TopicService svc = new TopicService(); diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java b/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java index 5bd62cb..186a003 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java @@ -38,6 +38,7 @@ import org.onap.dmaap.dbcapi.aaf.AafService; import org.onap.dmaap.dbcapi.aaf.DmaapGrant; import org.onap.dmaap.dbcapi.aaf.DmaapPerm; import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType; +import org.onap.dmaap.dbcapi.aaf.AafUserRole; import org.onap.dmaap.dbcapi.client.MrProvConnection; import org.onap.dmaap.dbcapi.database.DatabaseClass; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; @@ -128,7 +129,22 @@ public class MR_ClientService extends BaseLoggingClass{ logger.info( "Client dcaeLocation that doesn't exist or not specified" ); return null; } - grantClientPerms( client, err); + // original style: clients specified Role. This has precedence for backwards + // compatibility. + // ONAP style: clients specify Identity to be assigned to generated Role + String role = client.getClientRole(); + if ( role != null ) { + grantClientRolePerms( client, err); + } else if ( client.hasClientIdentity() ){ + if ( client.isSubscriber() ) { + role = topic.getSubscriberRole(); + assignIdentityToRole( client, role, err ); + } + if (client.isPublisher() ) { + role = topic.getPublisherRole(); + assignIdentityToRole( client, role, err ); + } + } if ( ! client.isStatusValid()) { return null; } @@ -189,26 +205,46 @@ public class MR_ClientService extends BaseLoggingClass{ return DmaapObject_Status.INVALID; } - private void grantClientPerms( MR_Client client, ApiError err) { + private void grantClientRolePerms( MR_Client client, ApiError err) { AafService aaf = new AafService(ServiceType.AAF_TopicMgr); String instance = ":topic." + client.getFqtn(); client.setStatus( DmaapObject_Status.VALID); + String role = client.getClientRole(); for( String want : client.getAction() ) { int rc; DmaapPerm perm = new DmaapPerm( dmaap.getTopicPerm(), instance, want ); - DmaapGrant g = new DmaapGrant( perm, client.getClientRole() ); - rc = aaf.addGrant( g ); - if ( rc != 201 && rc != 409 ) { - client.setStatus( DmaapObject_Status.INVALID); - err.setCode(rc); - err.setMessage( "Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " failed for " + client.getClientRole() ); - logger.warn( err.getMessage()); - return; - } + if ( role != null ) { + DmaapGrant g = new DmaapGrant( perm, role ); + rc = aaf.addGrant( g ); + if ( rc != 201 && rc != 409 ) { + client.setStatus( DmaapObject_Status.INVALID); + err.setCode(rc); + err.setMessage( "Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " failed for " + role ); + logger.warn( err.getMessage()); + return; + } + } else { + logger.warn( "No Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " because role is null " ); + } } } + private void assignIdentityToRole( MR_Client client, String role, ApiError err ) { + AafService aaf = new AafService(ServiceType.AAF_TopicMgr); + + AafUserRole ur = new AafUserRole( client.getClientIdentity(), role ); + int rc = aaf.addUserRole( ur ); + if ( rc != 201 && rc != 409 ) { + client.setStatus( DmaapObject_Status.INVALID); + err.setCode(rc); + err.setMessage( "Failed to add user " + client.getClientIdentity()+ " to " + role ); + logger.warn( err.getMessage()); + return; + } + client.setStatus( DmaapObject_Status.VALID); + + } private void revokeClientPerms( MR_Client client, ApiError err) { AafService aaf = new AafService(ServiceType.AAF_TopicMgr); 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 56ed967..cfec54e 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; @@ -58,7 +61,6 @@ public class TopicService extends BaseLoggingClass { private Map<String, Topic> mr_topics = DatabaseClass.getTopics(); private static DmaapService dmaapSvc = new DmaapService(); - private static Dmaap dmaap = new DmaapService().getDmaap(); private MR_ClientService clientService = new MR_ClientService(); private MR_ClusterService clusters = new MR_ClusterService(); private DcaeLocationService locations = new DcaeLocationService(); @@ -112,6 +114,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 +225,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); @@ -179,6 +257,7 @@ public class TopicService extends BaseLoggingClass { logger.info( "c fqtn=" + c.getFqtn() + " ID=" + c.getMrClientId() + " url=" + c.getTopicURL()); MR_Client nc = new MR_Client( c.getDcaeLocationName(), topic.getFqtn(), c.getClientRole(), c.getAction()); nc.setFqtn(topic.getFqtn()); + nc.setClientIdentity( c.getClientIdentity()); logger.info( "nc fqtn=" + nc.getFqtn() + " ID=" + nc.getMrClientId() + " url=" + nc.getTopicURL()); clients2.add( clientService.addMr_Client(nc, topic, err)); if ( ! err.is2xx()) { @@ -251,10 +330,11 @@ public class TopicService extends BaseLoggingClass { String mmAgentRole = p.getProperty("MM.AgentRole"); String[] Roles = { mmProvRole, mmAgentRole }; String[] actions = { "view", "pub", "sub" }; - Topic bridgeAdminTopic = new Topic(); + Topic bridgeAdminTopic = new Topic().init(); bridgeAdminTopic.setTopicName( dmaapSvc.getBridgeAdminFqtn() ); bridgeAdminTopic.setTopicDescription( "RESERVED topic for MirroMaker Provisioning"); bridgeAdminTopic.setOwner( "DBC" ); + ArrayList<MR_Client> clients = new ArrayList<MR_Client>(); for( String role: Roles ) { MR_Client client = new MR_Client(); |