summaryrefslogtreecommitdiffstats
path: root/auth/auth-certman/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'auth/auth-certman/src/main')
-rw-r--r--auth/auth-certman/src/main/config/.gitignore1
-rw-r--r--auth/auth-certman/src/main/config/certman.props22
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/AAF_CM.java239
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Artifact.java134
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Cert.java142
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/CA.java209
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/JscepCA.java268
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java182
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509ChainWithIssuer.java74
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509andChain.java79
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/BCFactory.java151
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/CSRMeta.java266
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/RDN.java101
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertDrop.java26
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertRenew.java26
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertReq.java49
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertResp.java94
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade.java182
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade1_0.java46
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeFactory.java41
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeImpl.java643
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper.java54
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper1_0.java274
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper2_0.java268
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/CMService.java693
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/Code.java45
-rw-r--r--auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/validation/CertmanValidator.java121
27 files changed, 4430 insertions, 0 deletions
diff --git a/auth/auth-certman/src/main/config/.gitignore b/auth/auth-certman/src/main/config/.gitignore
new file mode 100644
index 00000000..e53ef90a
--- /dev/null
+++ b/auth/auth-certman/src/main/config/.gitignore
@@ -0,0 +1 @@
+/log4j.properties
diff --git a/auth/auth-certman/src/main/config/certman.props b/auth/auth-certman/src/main/config/certman.props
new file mode 100644
index 00000000..1cd42f48
--- /dev/null
+++ b/auth/auth-certman/src/main/config/certman.props
@@ -0,0 +1,22 @@
+##
+## AUTHZ Certman (authz-certman) Properties
+##
+
+hostname=_HOSTNAME_
+
+## DISCOVERY (DME2) Parameters on the Command Line
+AFT_LATITUDE=_AFT_LATITUDE_
+AFT_LONGITUDE=_AFT_LONGITUDE_
+AFT_ENVIRONMENT=_AFT_ENVIRONMENT_
+DEPLOYED_VERSION=_ARTIFACT_VERSION_
+
+## Pull in common/security properties
+
+cadi_prop_files=_COMMON_DIR_/com.att.aaf.common.props:_COMMON_DIR_/com.att.aaf.props
+
+##DME2 related parameters
+DMEServiceName=service=com.att.authz.certman/version=_MAJOR_VER_._MINOR_VER_._PATCH_VER_/envContext=_ENV_CONTEXT_/routeOffer=_ROUTE_OFFER_
+AFT_DME2_PORT_RANGE=_AUTHZ_CERTMAN_PORT_RANGE_
+
+
+
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/AAF_CM.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/AAF_CM.java
new file mode 100644
index 00000000..c1bc8202
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/AAF_CM.java
@@ -0,0 +1,239 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm;
+
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.servlet.Filter;
+
+import org.onap.aaf.auth.cache.Cache;
+import org.onap.aaf.auth.cache.Cache.Dated;
+import org.onap.aaf.auth.cm.api.API_Artifact;
+import org.onap.aaf.auth.cm.api.API_Cert;
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.facade.Facade1_0;
+import org.onap.aaf.auth.cm.facade.FacadeFactory;
+import org.onap.aaf.auth.cm.mapper.Mapper.API;
+import org.onap.aaf.auth.cm.service.CMService;
+import org.onap.aaf.auth.cm.service.Code;
+import org.onap.aaf.auth.dao.CassAccess;
+import org.onap.aaf.auth.dao.cass.LocateDAO;
+import org.onap.aaf.auth.direct.DirectLocatorCreator;
+import org.onap.aaf.auth.direct.DirectRegistrar;
+import org.onap.aaf.auth.env.AuthzEnv;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.env.AuthzTransFilter;
+import org.onap.aaf.auth.rserv.HttpMethods;
+import org.onap.aaf.auth.server.AbsService;
+import org.onap.aaf.auth.server.JettyServiceStarter;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.aaf.v2_0.AAFTrustChecker;
+import org.onap.aaf.cadi.aaf.v2_0.AbsAAFLocator;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.register.Registrant;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.util.Split;
+
+import com.datastax.driver.core.Cluster;
+
+public class AAF_CM extends AbsService<AuthzEnv, AuthzTrans> {
+
+ private static final String USER_PERMS = "userPerms";
+ private static final Map<String,CA> certAuths = new TreeMap<String,CA>();
+ public Facade1_0 facade1_0; // this is the default Facade
+ public Facade1_0 facade1_0_XML; // this is the XML Facade
+ public Map<String, Dated> cacheUser;
+ public AAFAuthn<?> aafAuthn;
+ public AAFLurPerm aafLurPerm;
+ final public Cluster cluster;
+ public final LocateDAO locateDAO;
+
+
+ /**
+ * Construct AuthzAPI with all the Context Supporting Routes that Authz needs
+ *
+ * @param env
+ * @param si
+ * @param dm
+ * @param decryptor
+ * @throws APIException
+ */
+ public AAF_CM(AuthzEnv env) throws Exception {
+ super(env.access(),env);
+ aafLurPerm = aafCon().newLur();
+ // Note: If you need both Authn and Authz construct the following:
+ aafAuthn = aafCon().newAuthn(aafLurPerm);
+
+ String aaf_env = env.getProperty(Config.AAF_ENV);
+ if(aaf_env==null) {
+ throw new APIException("aaf_env needs to be set");
+ }
+
+ // Initialize Facade for all uses
+ AuthzTrans trans = env.newTrans();
+
+ cluster = org.onap.aaf.auth.dao.CassAccess.cluster(env,null);
+ locateDAO = new LocateDAO(trans,cluster,CassAccess.KEYSPACE);
+
+ // Have AAFLocator object Create DirectLocators for Location needs
+ AbsAAFLocator.setCreator(new DirectLocatorCreator(env, locateDAO));
+
+ // Load Supported Certificate Authorities by property
+ // Note: Some will be dynamic Properties, so we need to look through all
+ for(Entry<Object, Object> es : env.access().getProperties().entrySet()) {
+ String key = es.getKey().toString();
+ if(key.startsWith(CA.CM_CA_PREFIX)) {
+ int idx = key.indexOf('.');
+ if(idx==key.lastIndexOf('.')) { // else it's a regular property
+
+ env.log(Level.INIT, "Loading Certificate Authority Module: " + key.substring(idx+1));
+ String[] segs = Split.split(',', env.getProperty(key));
+ if(segs.length>0) {
+ String[][] multiParams = new String[segs.length-1][];
+ for(int i=0;i<multiParams.length;++i) {
+ multiParams[i]=Split.split(';',segs[1+i]);
+ }
+ @SuppressWarnings("unchecked")
+ Class<CA> cac = (Class<CA>)Class.forName(segs[0]);
+ Constructor<CA> cons = cac.getConstructor(new Class<?>[] {
+ Access.class,String.class,String.class,String[][].class
+ });
+ Object pinst[] = new Object[4];
+ pinst[0]=env;
+ pinst[1]= key.substring(idx+1);
+ pinst[2]= aaf_env;
+ pinst[3] = multiParams;
+ CA ca = cons.newInstance(pinst);
+ certAuths.put(ca.getName(),ca);
+ }
+ }
+ }
+ }
+ if(certAuths.size()==0) {
+ throw new APIException("No Certificate Authorities have been configured in CertMan");
+ }
+
+ CMService service = new CMService(trans, this);
+ // note: Service knows how to shutdown Cluster on Shutdown, etc. See Constructor
+ facade1_0 = FacadeFactory.v1_0(this,trans, service,Data.TYPE.JSON); // Default Facade
+ facade1_0_XML = FacadeFactory.v1_0(this,trans,service,Data.TYPE.XML);
+
+
+ synchronized(env) {
+ if(cacheUser == null) {
+ cacheUser = Cache.obtain(USER_PERMS);
+ Cache.startCleansing(env, USER_PERMS);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // APIs
+ ////////////////////////////////////////////////////////////////////////
+ API_Cert.init(this);
+ API_Artifact.init(this);
+
+ StringBuilder sb = new StringBuilder();
+ trans.auditTrail(2, sb);
+ trans.init().log(sb);
+ }
+
+ public CA getCA(String key) {
+ return certAuths.get(key);
+ }
+
+ /**
+ * Setup XML and JSON implementations for each supported Version type
+ *
+ * We do this by taking the Code passed in and creating clones of these with the appropriate Facades and properties
+ * to do Versions and Content switches
+ *
+ */
+ public void route(HttpMethods meth, String path, API api, Code code) throws Exception {
+ String version = "1.0";
+ // Get Correct API Class from Mapper
+ Class<?> respCls = facade1_0.mapper().getClass(api);
+ if(respCls==null) throw new Exception("Unknown class associated with " + api.getClass().getName() + ' ' + api.name());
+ // setup Application API HTML ContentTypes for JSON and Route
+ String application = applicationJSON(respCls, version);
+ route(env,meth,path,code,application,"application/json;version="+version,"*/*");
+
+ // setup Application API HTML ContentTypes for XML and Route
+ application = applicationXML(respCls, version);
+ route(env,meth,path,code.clone(facade1_0_XML),application,"application/xml;version="+version);
+
+ // Add other Supported APIs here as created
+ }
+
+ public void routeAll(HttpMethods meth, String path, API api, Code code) throws Exception {
+ route(env,meth,path,code,""); // this will always match
+ }
+
+ @Override
+ public Filter[] filters() throws CadiException, LocatorException {
+ try {
+ return new Filter[] {
+ new AuthzTransFilter(env,aafCon(),
+ new AAFTrustChecker((Env)env))
+ };
+ } catch (NumberFormatException e) {
+ throw new CadiException("Invalid Property information", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Registrant<AuthzEnv>[] registrants(final int port) throws CadiException, LocatorException {
+ return new Registrant[] {
+ new DirectRegistrar(access,locateDAO,app_name,app_version,port)
+ };
+ }
+
+ public void destroy() {
+ Cache.stopTimer();
+ locateDAO.close(env.newTransNoAvg());
+ cluster.close();
+ }
+
+ public static void main(final String[] args) {
+ PropAccess propAccess = new PropAccess(args);
+ try {
+ AAF_CM service = new AAF_CM(new AuthzEnv(propAccess));
+// env.setLog4JNames("log4j.properties","authz","cm","audit","init","trace");
+ JettyServiceStarter<AuthzEnv,AuthzTrans> jss = new JettyServiceStarter<AuthzEnv,AuthzTrans>(service);
+ jss.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Artifact.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Artifact.java
new file mode 100644
index 00000000..5c067ce1
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Artifact.java
@@ -0,0 +1,134 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.api;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.mapper.Mapper.API;
+import org.onap.aaf.auth.cm.service.Code;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.auth.rserv.HttpMethods;
+
+/**
+ * API Deployment Artifact Apis.. using Redirect for mechanism
+ *
+ * @author Jonathan
+ *
+ */
+public class API_Artifact {
+ private static final String GET_ARTIFACTS = "Get Artifacts";
+
+ /**
+ * Normal Init level APIs
+ *
+ * @param cmAPI
+ * @param facade
+ * @throws Exception
+ */
+ public static void init(final AAF_CM cmAPI) throws Exception {
+ cmAPI.route(HttpMethods.POST, "/cert/artifacts", API.ARTIFACTS, new Code(cmAPI,"Create Artifacts") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.createArtifacts(trans, req, resp);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.CREATED_201);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+ /**
+ * Use Query Params to get Artifacts by Machine or MechID
+ */
+ cmAPI.route(HttpMethods.GET, "/cert/artifacts", API.ARTIFACTS, new Code(cmAPI,GET_ARTIFACTS) {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.readArtifacts(trans, req, resp);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+
+ cmAPI.route(HttpMethods.GET, "/cert/artifacts/:mechid/:machine", API.ARTIFACTS, new Code(cmAPI,GET_ARTIFACTS) {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+
+ Result<Void> r = context.readArtifacts(trans, resp, pathParam(req,":mechid"), pathParam(req,":machine"));
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+
+ cmAPI.route(HttpMethods.PUT, "/cert/artifacts", API.ARTIFACTS, new Code(cmAPI,"Update Artifacts") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.updateArtifacts(trans, req, resp);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+ cmAPI.route(HttpMethods.DELETE, "/cert/artifacts/:mechid/:machine", API.VOID, new Code(cmAPI,"Delete Artifacts") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.deleteArtifacts(trans, resp,
+ pathParam(req, ":mechid"), pathParam(req,":machine"));
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+
+ cmAPI.route(HttpMethods.DELETE, "/cert/artifacts", API.VOID, new Code(cmAPI,"Delete Artifacts") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.deleteArtifacts(trans, req, resp);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Cert.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Cert.java
new file mode 100644
index 00000000..0cea9c73
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/api/API_Cert.java
@@ -0,0 +1,142 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.api;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.mapper.Mapper.API;
+import org.onap.aaf.auth.cm.service.Code;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.auth.rserv.HttpMethods;
+import org.onap.aaf.misc.env.Slot;
+
+/**
+ * API Apis.. using Redirect for mechanism
+ *
+ * @author Jonathan
+ *
+ */
+public class API_Cert {
+ public static final String CERT_AUTH = "CertAuthority";
+ private static Slot sCertAuth;
+
+ /**
+ * Normal Init level APIs
+ *
+ * @param aafCM
+ * @param facade
+ * @throws Exception
+ */
+ public static void init(final AAF_CM aafCM) throws Exception {
+ // Check for Created Certificate Authorities in TRANS
+ sCertAuth = aafCM.env.slot(CERT_AUTH);
+
+ ////////
+ // Overall APIs
+ ///////
+ aafCM.route(HttpMethods.PUT,"/cert/:ca",API.CERT_REQ,new Code(aafCM,"Request Certificate") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ String key = pathParam(req, ":ca");
+ CA ca;
+ if((ca = aafCM.getCA(key))==null) {
+ context.error(trans,resp,Result.ERR_BadData,"CA %s is not supported",key);
+ } else {
+ trans.put(sCertAuth, ca);
+ Result<Void> r = context.requestCert(trans, req, resp, ca);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ }
+ });
+
+ aafCM.route(HttpMethods.GET,"/cert/:ca/personal",API.CERT,new Code(aafCM,"Request Personal Certificate") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ String key = pathParam(req, ":ca");
+ CA ca;
+ if((ca = aafCM.getCA(key))==null) {
+ context.error(trans,resp,Result.ERR_BadData,"CA %s is not supported",key);
+ } else {
+ trans.put(sCertAuth, ca);
+ Result<Void> r = context.requestPersonalCert(trans, req, resp, ca);
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ }
+ });
+
+
+ /**
+ *
+ */
+ aafCM.route(HttpMethods.GET, "/cert/may/:perm", API.VOID, new Code(aafCM,"Check Permission") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.check(trans, resp, pathParam(req,"perm"));
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ trans.checkpoint(r.errorString());
+ context.error(trans,resp,Result.err(Result.ERR_Denied,"%s does not have Permission.",trans.user()));
+ }
+ }
+ });
+
+ /**
+ * Get Cert by ID and Machine
+ */
+
+
+ /**
+ * Get Certs by ID
+ */
+ aafCM.route(HttpMethods.GET, "/cert/id/:id", API.CERT, new Code(aafCM,"GetByID") {
+ @Override
+ public void handle(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) throws Exception {
+ Result<Void> r = context.readCertsByMechID(trans, resp, pathParam(req,"id"));
+ if(r.isOK()) {
+ resp.setStatus(HttpStatus.OK_200);
+ } else {
+ context.error(trans,resp,r);
+ }
+ }
+ });
+
+
+ /**
+ * Get Certs by Machine
+ */
+
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/CA.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/CA.java
new file mode 100644
index 00000000..521c5016
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/CA.java
@@ -0,0 +1,209 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.ca;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.auth.cm.cert.RDN;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.util.Split;
+
+public abstract class CA {
+ private static final String MUST_EXIST_TO_CREATE_CSRS_FOR = " must exist to create CSRs for ";
+ //TODO figuring out what is an Issuing CA is a matter of convention. Consider SubClassing for Open Source
+ public static final String ISSUING_CA = "Issuing CA";
+ public static final String CM_CA_PREFIX = "cm_ca.";
+ public static final String CM_CA_BASE_SUBJECT = ".baseSubject";
+ protected static final String CM_PUBLIC_DIR = "cm_public_dir";
+ private static final String CM_TRUST_CAS = "cm_trust_cas";
+ protected static final String CM_BACKUP_CAS = "cm_backup_cas";
+
+ public static final Set<String> EMPTY = Collections.unmodifiableSet(new HashSet<String>());
+
+
+ private final String name,env;
+ private MessageDigest messageDigest;
+ private final String permType;
+ private Set<String> caIssuerDNs;
+ private final ArrayList<String> idDomains;
+ private String[] trustedCAs;
+ private List<RDN> rdns;
+
+
+ protected CA(Access access, String caName, String env) throws IOException, CertException {
+ trustedCAs = new String[4]; // starting array
+ this.name = caName;
+ this.env = env;
+ permType = access.getProperty(CM_CA_PREFIX + name + ".perm_type",null);
+ if(permType==null) {
+ throw new CertException(CM_CA_PREFIX + name + ".perm_type" + MUST_EXIST_TO_CREATE_CSRS_FOR + caName);
+ }
+ caIssuerDNs = new HashSet<String>();
+
+ String tag = CA.CM_CA_PREFIX+caName+CA.CM_CA_BASE_SUBJECT;
+
+ String fields = access.getProperty(tag, null);
+ if(fields==null) {
+ throw new CertException(tag + MUST_EXIST_TO_CREATE_CSRS_FOR + caName);
+ }
+ for(RDN rdn : rdns = RDN.parse('/',fields)) {
+ if(rdn.aoi==BCStyle.EmailAddress) { // Cert Specs say Emails belong in Subject
+ throw new CertException("email address is not allowed in " + CM_CA_BASE_SUBJECT);
+ }
+ }
+
+ idDomains = new ArrayList<String>();
+ StringBuilder sb = null;
+ for(String s : Split.splitTrim(',', access.getProperty(CA.CM_CA_PREFIX+caName+".idDomains", ""))) {
+ if(s.length()>0) {
+ if(sb==null) {
+ sb = new StringBuilder();
+ } else {
+ sb.append(", ");
+ }
+ idDomains.add(s);
+ sb.append(s);
+ }
+ }
+ if(sb!=null) {
+ access.printf(Level.INIT, "CA '%s' supports Personal Certificates for %s", caName, sb);
+ }
+
+ String data_dir = access.getProperty(CM_PUBLIC_DIR,null);
+ if(data_dir!=null) {
+ File data = new File(data_dir);
+ byte[] bytes;
+ if(data.exists()) {
+ String trust_cas = access.getProperty(CM_TRUST_CAS,null);
+ if(trust_cas!=null) {
+ for(String fname : Split.splitTrim(',', trust_cas)) {
+ File crt = new File(data,fname);
+ if(crt.exists()) {
+ access.printf(Level.INIT, "Loading CA Cert from %s", crt.getAbsolutePath());
+ bytes = new byte[(int)crt.length()];
+ FileInputStream fis = new FileInputStream(crt);
+ try {
+ fis.read(bytes);
+ addTrustedCA(new String(bytes));
+ } finally {
+ fis.close();
+ }
+ } else {
+ access.printf(Level.INIT, "FAILED to Load CA Cert from %s", crt.getAbsolutePath());
+ }
+ }
+ } else {
+ access.printf(Level.INIT, "Cannot load external TRUST CAs: No property %s",CM_TRUST_CAS);
+ }
+ } else {
+ access.printf(Level.INIT, "Cannot load external TRUST CAs: %s doesn't exist, or is not accessible",data.getAbsolutePath());
+ }
+ }
+ }
+
+ protected void addCaIssuerDN(String issuerDN) {
+ caIssuerDNs.add(issuerDN);
+ }
+
+ protected synchronized void addTrustedCA(final String crtString) {
+ String crt;
+ if(crtString.endsWith("\n")) {
+ crt = crtString;
+ } else {
+ crt = crtString + '\n';
+ }
+ for(int i=0;i<trustedCAs.length;++i) {
+ if(trustedCAs[i]==null) {
+ trustedCAs[i]=crt;
+ return;
+ }
+ }
+ String[] temp = new String[trustedCAs.length+5];
+ System.arraycopy(trustedCAs,0,temp, 0, trustedCAs.length);
+ temp[trustedCAs.length]=crt;
+ trustedCAs = temp;
+ }
+
+ public Set<String> getCaIssuerDNs() {
+ return caIssuerDNs;
+ }
+
+ public String[] getTrustedCAs() {
+ return trustedCAs;
+ }
+
+ public String getEnv() {
+ return env;
+ }
+
+ protected void setMessageDigest(MessageDigest md) {
+ messageDigest = md;
+ }
+
+ /*
+ * End Required Constructor calls
+ */
+
+ public String getName() {
+ return name;
+ }
+
+
+ public String getPermType() {
+ return permType;
+ }
+
+ public abstract X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException;
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.ca.CA#inPersonalDomains(java.security.Principal)
+ */
+ public boolean inPersonalDomains(Principal p) {
+ int at = p.getName().indexOf('@');
+ if(at>=0) {
+ return idDomains.contains(p.getName().substring(at+1));
+ } else {
+ return false;
+ }
+ }
+
+ public MessageDigest messageDigest() {
+ return messageDigest;
+ }
+
+ public CSRMeta newCSRMeta() {
+ return new CSRMeta(rdns);
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/JscepCA.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/JscepCA.java
new file mode 100644
index 00000000..ae4e21aa
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/JscepCA.java
@@ -0,0 +1,268 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.ca;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.Authenticator;
+import java.net.MalformedURLException;
+import java.net.PasswordAuthentication;
+import java.net.URL;
+import java.security.cert.CertStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.jscep.client.Client;
+import org.jscep.client.ClientException;
+import org.jscep.client.EnrollmentResponse;
+import org.jscep.client.verification.CertificateVerifier;
+import org.jscep.transaction.TransactionException;
+import org.onap.aaf.auth.cm.cert.BCFactory;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.Locator.Item;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.locator.HotPeerLocator;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.Trans;
+import org.onap.aaf.misc.env.util.Split;
+
+public class JscepCA extends CA {
+ static final String CA_PREFIX = "http://";
+ static final String CA_POSTFIX="/certsrv/mscep_admin/mscep.dll";
+
+ private final static String MS_PROFILE="1";
+ private final static int MAX_RETRY=3;
+ public static final long INVALIDATE_TIME = 1000*60*10; // 10 mins
+
+ // package on purpose
+ private Map<String,X509ChainWithIssuer> mxcwi_s;
+ private Map<Client,X509ChainWithIssuer> mxcwi_c;
+
+
+ private JscepClientLocator clients;
+
+ public JscepCA(final Access access, final String name, final String env, String [][] params) throws IOException, CertException, LocatorException {
+ super(access, name, env);
+ mxcwi_s = new ConcurrentHashMap<String,X509ChainWithIssuer>();
+ mxcwi_c = new ConcurrentHashMap<Client,X509ChainWithIssuer>();
+
+ if(params.length<2) {
+ throw new CertException("No Trust Chain parameters are included");
+ }
+ if(params[0].length<2) {
+ throw new CertException("User/Password required for JSCEP");
+ }
+ final String id = params[0][0];
+ final String pw = params[0][1];
+
+ // Set this for NTLM password Microsoft
+ Authenticator.setDefault(new Authenticator() {
+ public PasswordAuthentication getPasswordAuthentication () {
+ try {
+ return new PasswordAuthentication (id,access.decrypt(pw,true).toCharArray());
+ } catch (IOException e) {
+ access.log(e);
+ }
+ return null;
+ }
+ });
+
+ StringBuilder urlstr = new StringBuilder();
+
+ for(int i=1;i<params.length;++i) { // skip first section, which is user/pass
+ // Work
+ if(i>1) {
+ urlstr.append(','); // delimiter
+ }
+ urlstr.append(params[i][0]);
+
+ String dir = access.getProperty(CM_PUBLIC_DIR, "");
+ if(!"".equals(dir) && !dir.endsWith("/")) {
+ dir = dir + '/';
+ }
+ String path;
+ List<FileReader> frs = new ArrayList<FileReader>(params.length-1);
+ try {
+ for(int j=1; j<params[i].length; ++j) { // first 3 taken up, see above
+ path = !params[i][j].contains("/")?dir+params[i][j]:params[i][j];
+ access.printf(Level.INIT, "Loading a TrustChain Member for %s from %s",name, path);
+ frs.add(new FileReader(path));
+ }
+ X509ChainWithIssuer xcwi = new X509ChainWithIssuer(frs);
+ addCaIssuerDN(xcwi.getIssuerDN());
+ mxcwi_s.put(params[i][0],xcwi);
+ } finally {
+ for(FileReader fr : frs) {
+ if(fr!=null) {
+ fr.close();
+ }
+ }
+ }
+ }
+ clients = new JscepClientLocator(access,urlstr.toString());
+ }
+
+ // package on purpose
+
+ @Override
+ public X509ChainWithIssuer sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException {
+ TimeTaken tt = trans.start("Generating CSR and Keys for New Certificate", Env.SUB);
+ PKCS10CertificationRequest csr;
+ try {
+ csr = csrmeta.generateCSR(trans);
+ if(trans.info().isLoggable()) {
+ trans.info().log(BCFactory.toString(csr));
+ }
+ if(trans.info().isLoggable()) {
+ trans.info().log(csr);
+ }
+ } finally {
+ tt.done();
+ }
+
+ tt = trans.start("Enroll CSR", Env.SUB);
+ Client client = null;
+ Item item = null;
+ for(int i=0; i<MAX_RETRY;++i) {
+ try {
+ item = clients.best();
+ client = clients.get(item);
+
+ EnrollmentResponse er = client.enrol(
+ csrmeta.initialConversationCert(trans),
+ csrmeta.keypair(trans).getPrivate(),
+ csr,
+ MS_PROFILE /* profile... MS can't deal with blanks*/);
+
+ while(true) {
+ if(er.isSuccess()) {
+ trans.checkpoint("Cert from " + clients.info(item));
+ X509Certificate x509 = null;
+ for( Certificate cert : er.getCertStore().getCertificates(null)) {
+ if(x509==null) {
+ x509 = (X509Certificate)cert;
+ break;
+ }
+ }
+ X509ChainWithIssuer mxcwi = mxcwi_c.get(client);
+ return new X509ChainWithIssuer(mxcwi,x509);
+// break;
+ } else if (er.isPending()) {
+ trans.checkpoint("Polling, waiting on CA to complete");
+ Thread.sleep(3000);
+ } else if (er.isFailure()) {
+// switch(er.getFailInfo()) {
+// case badMessageCheck:
+// throw new ClientException("Received BadMessageCheck from Jscep");
+// case badAlg:
+// case badCertId:
+// case badRequest:
+// case badTime:
+// default:
+// }
+ throw new CertException(clients.info(item)+':'+er.getFailInfo().toString());
+ }
+ }
+ //i=MAX_RETRY;
+ } catch(LocatorException e) {
+ trans.error().log(e);
+ i=MAX_RETRY;
+ } catch (ClientException e) {
+ trans.error().log(e,"SCEP Client Error, Temporarily Invalidating Client: " + clients.info(item));
+ try {
+ clients.invalidate(client);
+ if(!clients.hasItems()) {
+ clients.refresh();
+ }
+ } catch (LocatorException e1) {
+ trans.error().log(e,clients.info(item));
+ i=MAX_RETRY; // can't go any further
+ }
+ } catch (InterruptedException|TransactionException|CertificateException|OperatorCreationException | CertStoreException e) {
+ trans.error().log(e);
+ i=MAX_RETRY;
+ } finally {
+ tt.done();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Locator specifically for Jscep Clients.
+ *
+ * Class based client for access to common Map
+ */
+ private class JscepClientLocator extends HotPeerLocator<Client> {
+
+ protected JscepClientLocator(Access access, String urlstr)throws LocatorException {
+ super(access, urlstr, JscepCA.INVALIDATE_TIME,
+ access.getProperty("cadi_latitude","39.833333"), //Note: Defaulting to GEO center of US
+ access.getProperty("cadi_longitude","-98.583333")
+ );
+ }
+
+ @Override
+ protected Client _newClient(String urlinfo) throws LocatorException {
+ try {
+ String[] info = Split.split('/', urlinfo);
+ Client c = new Client(new URL(JscepCA.CA_PREFIX + info[0] + JscepCA.CA_POSTFIX),
+ new CertificateVerifier() {
+ @Override
+ public boolean verify(X509Certificate cert) {
+ //TODO checkIssuer
+ return true;
+ }
+ }
+ );
+ // Map URL to Client, because Client doesn't expose Connection
+ mxcwi_c.put(c,mxcwi_s.get(urlinfo));
+ return c;
+ } catch (MalformedURLException e) {
+ throw new LocatorException(e);
+ }
+ }
+
+ @Override
+ protected Client _invalidate(Client client) {
+ return null;
+ }
+
+ @Override
+ protected void _destroy(Client client) {
+ mxcwi_c.remove(client);
+ }
+
+
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java
new file mode 100644
index 00000000..b6a2a0a9
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java
@@ -0,0 +1,182 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.ca;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.onap.aaf.auth.cm.cert.BCFactory;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.auth.cm.cert.RDN;
+import org.onap.aaf.auth.env.NullTrans;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.Trans;
+
+public class LocalCA extends CA {
+
+ // Extensions
+ private static final KeyPurposeId[] ASN_WebUsage = new KeyPurposeId[] {
+ KeyPurposeId.id_kp_serverAuth, // WebServer
+ KeyPurposeId.id_kp_clientAuth};// WebClient
+
+ private final RSAPrivateKey caKey;
+ private final X500Name issuer;
+ private final SecureRandom random = new SecureRandom();
+ private byte[] serialish;
+ private final X509ChainWithIssuer x509cwi; // "Cert" is CACert
+
+ public LocalCA(Access access, final String name, final String env, final String[][] params) throws IOException, CertException {
+ super(access, name, env);
+ serialish = new byte[24];
+ if(params.length<1 || params[0].length<2) {
+ throw new IOException("LocalCA expects cm_ca.<ca name>=org.onap.aaf.auth.cm.ca.LocalCA,<full path to key file>[;<Full Path to Trust Chain, ending with actual CA>]+");
+ }
+
+ // Read in the Private Key
+ File f = new File(params[0][0]); // key
+ if(f.exists()) {
+ caKey = (RSAPrivateKey)Factory.toPrivateKey(NullTrans.singleton(),f);
+ } else {
+ throw new CertException("Private Key, " + f.getPath() + ", does not exist");
+ }
+
+ String dir = access.getProperty(CM_PUBLIC_DIR, "");
+ if(!"".equals(dir) && !dir.endsWith("/")) {
+ dir = dir + '/';
+ }
+ List<FileReader> frs = new ArrayList<FileReader>(params.length-1);
+ try {
+ String path;
+ for(int i=1; i<params[0].length; ++i) { // first param is Private Key, remainder are TrustChain
+ path = !params[0][i].contains("/")?dir+params[0][i]:params[0][i];
+ access.printf(Level.INIT, "Loading a TrustChain Member for %s from %s\n",name, path);
+ frs.add(new FileReader(path));
+ }
+ x509cwi = new X509ChainWithIssuer(frs);
+ X500NameBuilder xnb = new X500NameBuilder();
+ for(RDN rnd : RDN.parse(',', x509cwi.getIssuerDN())) {
+ xnb.addRDN(rnd.aoi,rnd.value);
+ }
+ issuer = xnb.build();
+ } finally {
+ for(FileReader fr : frs) {
+ if(fr!=null) {
+ fr.close();
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.service.CA#sign(org.bouncycastle.pkcs.PKCS10CertificationRequest)
+ */
+ @Override
+ public X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException {
+ GregorianCalendar gc = new GregorianCalendar();
+ Date start = gc.getTime();
+ gc.add(GregorianCalendar.MONTH, 2);
+ Date end = gc.getTime();
+ X509Certificate x509;
+ TimeTaken tt = trans.start("Create/Sign Cert",Env.SUB);
+ try {
+ BigInteger bi;
+ synchronized(serialish) {
+ random.nextBytes(serialish);
+ bi = new BigInteger(serialish);
+ }
+
+ RSAPublicKey rpk = (RSAPublicKey)csrmeta.keypair(trans).getPublic();
+ X509v3CertificateBuilder xcb = new X509v3CertificateBuilder(
+ issuer,
+ bi, // replace with Serialnumber scheme
+ start,
+ end,
+ csrmeta.x500Name(),
+ SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(new RSAKeyParameters(false,rpk.getModulus(),rpk.getPublicExponent()))
+// new SubjectPublicKeyInfo(ASN1Sequence.getInstance(caCert.getPublicKey().getEncoded()))
+ );
+ List<GeneralName> lsan = new ArrayList<GeneralName>();
+ for(String s : csrmeta.sans()) {
+ lsan.add(new GeneralName(GeneralName.dNSName,s));
+ }
+ GeneralName[] sans = new GeneralName[lsan.size()];
+ lsan.toArray(sans);
+
+ JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+ xcb.addExtension(Extension.basicConstraints,
+ false, new BasicConstraints(false))
+ .addExtension(Extension.keyUsage,
+ true, new KeyUsage(KeyUsage.digitalSignature
+ | KeyUsage.keyEncipherment))
+ .addExtension(Extension.extendedKeyUsage,
+ true, new ExtendedKeyUsage(ASN_WebUsage))
+
+ .addExtension(Extension.authorityKeyIdentifier,
+ false, extUtils.createAuthorityKeyIdentifier(x509cwi.cert))
+ .addExtension(Extension.subjectKeyIdentifier,
+ false, extUtils.createSubjectKeyIdentifier(x509cwi.cert.getPublicKey()))
+ .addExtension(Extension.subjectAlternativeName,
+ false, new GeneralNames(sans))
+ ;
+
+ x509 = new JcaX509CertificateConverter().getCertificate(
+ xcb.build(BCFactory.contentSigner(caKey)));
+ } catch (GeneralSecurityException|OperatorCreationException e) {
+ throw new CertException(e);
+ } finally {
+ tt.done();
+ }
+
+ return new X509ChainWithIssuer(x509cwi,x509);
+ }
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509ChainWithIssuer.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509ChainWithIssuer.java
new file mode 100644
index 00000000..e0a85676
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509ChainWithIssuer.java
@@ -0,0 +1,74 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.ca;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.List;
+
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+
+public class X509ChainWithIssuer extends X509andChain {
+ private String issuerDN;
+
+ public X509ChainWithIssuer(X509ChainWithIssuer orig, X509Certificate x509) {
+ super(x509,orig.trustChain);
+ issuerDN=orig.issuerDN;
+ }
+
+ public X509ChainWithIssuer(final List<? extends Reader> rdrs) throws IOException, CertException {
+ // Trust Chain. Last one should be the CA
+ Collection<? extends Certificate> certs;
+ X509Certificate x509;
+ for(Reader rdr : rdrs) {
+ if(rdr!=null) { // cover for badly formed array
+ byte[] bytes = Factory.decode(rdr);
+ try {
+ certs = Factory.toX509Certificate(bytes);
+ } catch (CertificateException e) {
+ throw new CertException(e);
+ }
+ for(Certificate c : certs) {
+ x509=(X509Certificate)c;
+ Principal subject = x509.getSubjectDN();
+ if(subject!=null) {
+ if(cert==null) { // first in Trust Chain
+ issuerDN= subject.toString();
+ }
+ addTrustChainEntry(x509);
+ cert=x509; // adding each time makes sure last one is signer.
+ }
+ }
+ }
+ }
+ }
+
+ public String getIssuerDN() {
+ return issuerDN;
+ }
+
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509andChain.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509andChain.java
new file mode 100644
index 00000000..24416c92
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/X509andChain.java
@@ -0,0 +1,79 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.ca;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import org.onap.aaf.auth.env.NullTrans;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+
+
+/**
+ * Have to put the Cert and resulting Trust Chain together.
+ * Treating them separately has caused issues
+ *
+ * @author jg1555
+ *
+ */
+public class X509andChain {
+ protected X509Certificate cert;
+ protected String[] trustChain;
+
+ public X509andChain() {
+ cert = null;
+ trustChain = null;
+ }
+
+ public X509andChain(X509Certificate cert, String[] trustChain) {
+ this.cert = cert;
+ this.trustChain = trustChain;
+ }
+
+ public X509andChain(X509Certificate cert, List<String> chain) {
+ this.cert = cert;
+ trustChain = new String[chain.size()];
+ chain.toArray(trustChain);
+ }
+
+
+ public void addTrustChainEntry(X509Certificate x509) throws IOException, CertException {
+ if(trustChain==null) {
+ trustChain = new String[] {Factory.toString(NullTrans.singleton(),x509)};
+ } else {
+ String[] temp = new String[trustChain.length+1];
+ System.arraycopy(trustChain, 0, temp, 0, trustChain.length);
+ temp[trustChain.length]=Factory.toString(NullTrans.singleton(),x509);
+ trustChain=temp;
+ }
+ }
+
+ public X509Certificate getX509() {
+ return cert;
+ }
+
+ public String[] getTrustChain() {
+ return trustChain;
+ }
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/BCFactory.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/BCFactory.java
new file mode 100644
index 00000000..4fdac6a6
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/BCFactory.java
@@ -0,0 +1,151 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.cert;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.SignatureException;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.validation.CertmanValidator;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.Trans;
+
+
+/**
+ * Additional Factory mechanisms for CSRs, and BouncyCastle. The main Factory
+ * utilizes only Java abstractions, and is useful in Client code.
+ *
+ * @author jg1555
+ *
+ */
+public class BCFactory extends Factory {
+ private static final JcaContentSignerBuilder jcsb;
+
+
+ static {
+ // Bouncy
+ jcsb = new JcaContentSignerBuilder(Factory.SIG_ALGO);
+ }
+
+ public static ContentSigner contentSigner(PrivateKey pk) throws OperatorCreationException {
+ return jcsb.build(pk);
+ }
+
+ public static String toString(PKCS10CertificationRequest csr) throws IOException, CertException {
+ if(csr==null) {
+ throw new CertException("x509 Certificate Request not built");
+ }
+ return textBuilder("CERTIFICATE REQUEST",csr.getEncoded());
+ }
+
+ public static PKCS10CertificationRequest toCSR(Trans trans, File file) throws IOException {
+ TimeTaken tt = trans.start("Reconstitute CSR", Env.SUB);
+ try {
+ FileReader fr = new FileReader(file);
+ return new PKCS10CertificationRequest(decode(strip(fr)));
+ } finally {
+ tt.done();
+ }
+ }
+
+ public static byte[] sign(Trans trans, ASN1Object toSign, PrivateKey pk) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
+ TimeTaken tt = trans.start("Encode Security Object", Env.SUB);
+ try {
+ return sign(trans,toSign.getEncoded(),pk);
+ } finally {
+ tt.done();
+ }
+ }
+
+ public static CSRMeta createCSRMeta(CA ca, String mechid, String sponsorEmail, List<String> fqdns) throws CertException {
+ CSRMeta csr = ca.newCSRMeta();
+ boolean first = true;
+ // Set CN (and SAN)
+ for(String fqdn : fqdns) {
+ if(first) {
+ first = false;
+ csr.cn(fqdn);
+ }
+ csr.san(fqdn); // duplicate CN in SAN, per RFC 5280 section 4.2.1.6
+ }
+
+ csr.challenge(new String(Symm.randomGen(24)));
+ csr.mechID(mechid);
+ csr.email(sponsorEmail);
+ String errs;
+ if((errs=validateApp(csr))!=null) {
+ throw new CertException(errs);
+ }
+ return csr;
+ }
+
+ private static String validateApp(CSRMeta csr) {
+ CertmanValidator v = new CertmanValidator();
+ if(v.nullOrBlank("cn", csr.cn())
+ .nullOrBlank("mechID", csr.mechID())
+ .nullOrBlank("email", csr.email())
+ .err()) {
+ return v.errs();
+ } else {
+ return null;
+ }
+ }
+
+ public static CSRMeta createPersonalCSRMeta(CA ca, String personal, String email) throws CertException {
+ CSRMeta csr = ca.newCSRMeta();
+ csr.cn(personal);
+ csr.challenge(new String(Symm.randomGen(24)));
+ csr.email(email);
+ String errs;
+ if((errs=validatePersonal(csr))!=null) {
+ throw new CertException(errs);
+ }
+ return csr;
+ }
+
+ private static String validatePersonal(CSRMeta csr) {
+ CertmanValidator v = new CertmanValidator();
+ if(v.nullOrBlank("cn", csr.cn())
+ .nullOrBlank("email", csr.email())
+ .err()) {
+ return v.errs();
+ } else {
+ return null;
+ }
+ }
+
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/CSRMeta.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/CSRMeta.java
new file mode 100644
index 00000000..2541bea0
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/CSRMeta.java
@@ -0,0 +1,266 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.cert;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERPrintableString;
+import org.bouncycastle.asn1.pkcs.Attribute;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.misc.env.Trans;
+
+public class CSRMeta {
+ private String cn;
+ private String mechID;
+ private String environment;
+ private String email;
+ private String challenge;
+ private List<RDN> rdns;
+
+ public CSRMeta(List<RDN> rdns) {
+ this.rdns = rdns;
+ }
+
+ private ArrayList<String> sanList = new ArrayList<String>();
+ private KeyPair keyPair;
+ private X500Name name = null;
+ private SecureRandom random = new SecureRandom();
+
+ public X500Name x500Name() throws IOException {
+ if(name==null) {
+ X500NameBuilder xnb = new X500NameBuilder();
+ xnb.addRDN(BCStyle.CN,cn);
+ xnb.addRDN(BCStyle.E,email);
+ if(mechID!=null) {
+ if(environment==null) {
+ xnb.addRDN(BCStyle.OU,mechID);
+ } else {
+ xnb.addRDN(BCStyle.OU,mechID+':'+environment);
+ }
+ }
+ for(RDN rdn : rdns) {
+ xnb.addRDN(rdn.aoi,rdn.value);
+ }
+ name = xnb.build();
+ }
+ return name;
+ }
+
+
+ public PKCS10CertificationRequest generateCSR(Trans trans) throws IOException, CertException {
+ PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(x500Name(),keypair(trans).getPublic());
+ if(challenge!=null) {
+ DERPrintableString password = new DERPrintableString(challenge);
+ builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_challengePassword, password);
+ }
+
+ int plus = email==null?0:1;
+ if(sanList.size()>0) {
+ GeneralName[] gna = new GeneralName[sanList.size()+plus];
+ int i=-1;
+ for(String s : sanList) {
+ gna[++i]=new GeneralName(GeneralName.dNSName,s);
+ }
+ gna[++i]=new GeneralName(GeneralName.rfc822Name,email);
+
+ builder.addAttribute(
+ PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
+ new Extensions(new Extension[] {
+ new Extension(Extension.subjectAlternativeName,false,new GeneralNames(gna).getEncoded())
+ })
+ );
+ }
+
+ if(email!=null) {
+
+ }
+ try {
+ return builder.build(BCFactory.contentSigner(keypair(trans).getPrivate()));
+ } catch (OperatorCreationException e) {
+ throw new CertException(e);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public static void dump(PKCS10CertificationRequest csr) {
+ Attribute[] certAttributes = csr.getAttributes();
+ for (Attribute attribute : certAttributes) {
+ if (attribute.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) {
+ Extensions extensions = Extensions.getInstance(attribute.getAttrValues().getObjectAt(0));
+ GeneralNames gns = GeneralNames.fromExtensions(extensions,Extension.subjectAlternativeName);
+ GeneralName[] names = gns.getNames();
+ for(int k=0; k < names.length; k++) {
+ String title = "";
+ if(names[k].getTagNo() == GeneralName.dNSName) {
+ title = "dNSName";
+ } else if(names[k].getTagNo() == GeneralName.iPAddress) {
+ title = "iPAddress";
+ // Deprecated, but I don't see anything better to use.
+ names[k].toASN1Object();
+ } else if(names[k].getTagNo() == GeneralName.otherName) {
+ title = "otherName";
+ } else if(names[k].getTagNo() == GeneralName.rfc822Name) {
+ title = "email";
+ }
+
+ System.out.println(title + ": "+ names[k].getName());
+ }
+ }
+ }
+ }
+
+ public X509Certificate initialConversationCert(Trans trans) throws IOException, CertificateException, OperatorCreationException {
+ GregorianCalendar gc = new GregorianCalendar();
+ Date start = gc.getTime();
+ gc.add(GregorianCalendar.DAY_OF_MONTH,2);
+ Date end = gc.getTime();
+ X509v3CertificateBuilder xcb = new X509v3CertificateBuilder(
+ x500Name(),
+ new BigInteger(12,random), // replace with Serialnumber scheme
+ start,
+ end,
+ x500Name(),
+ new SubjectPublicKeyInfo(ASN1Sequence.getInstance(keypair(trans).getPublic().getEncoded()))
+ );
+ return new JcaX509CertificateConverter().getCertificate(
+ xcb.build(BCFactory.contentSigner(keypair(trans).getPrivate())));
+ }
+
+ public CSRMeta san(String v) {
+ sanList.add(v);
+ return this;
+ }
+
+ public List<String> sans() {
+ return sanList;
+ }
+
+
+ public KeyPair keypair(Trans trans) {
+ if(keyPair == null) {
+ keyPair = Factory.generateKeyPair(trans);
+ }
+ return keyPair;
+ }
+
+ /**
+ * @return the cn
+ */
+ public String cn() {
+ return cn;
+ }
+
+
+ /**
+ * @param cn the cn to set
+ */
+ public void cn(String cn) {
+ this.cn = cn;
+ }
+
+ /**
+ * Environment of Service MechID is good for
+ */
+ public void environment(String env) {
+ environment = env;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public String environment() {
+ return environment;
+ }
+
+ /**
+ * @return the mechID
+ */
+ public String mechID() {
+ return mechID;
+ }
+
+
+ /**
+ * @param mechID the mechID to set
+ */
+ public void mechID(String mechID) {
+ this.mechID = mechID;
+ }
+
+
+ /**
+ * @return the email
+ */
+ public String email() {
+ return email;
+ }
+
+
+ /**
+ * @param email the email to set
+ */
+ public void email(String email) {
+ this.email = email;
+ }
+
+ /**
+ * @return the challenge
+ */
+ public String challenge() {
+ return challenge;
+ }
+
+
+ /**
+ * @param challenge the challenge to set
+ */
+ public void challenge(String challenge) {
+ this.challenge = challenge;
+ }
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/RDN.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/RDN.java
new file mode 100644
index 00000000..7d4161f2
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/cert/RDN.java
@@ -0,0 +1,101 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.cert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.util.Split;
+
+public class RDN {
+ public String tag;
+ public String value;
+ public ASN1ObjectIdentifier aoi;
+ public RDN(final String tagValue) throws CertException {
+ String[] tv = Split.splitTrim('=',tagValue);
+ switch(tv[0]) {
+ case "cn":case "CN": aoi = BCStyle.CN; break;
+ case "c":case "C": aoi = BCStyle.C;break;
+ case "st":case "ST": aoi = BCStyle.ST;break;
+ case "l":case "L": aoi = BCStyle.L;break;
+ case "o":case "O": aoi = BCStyle.O;break;
+ case "ou":case "OU": aoi = BCStyle.OU;break;
+ case "dc":case "DC": aoi = BCStyle.DC;break;
+ case "gn":case "GN": aoi = BCStyle.GIVENNAME; break;
+ case "sn":case "SN": aoi = BCStyle.SN; break; // surname
+ case "email":case "EMAIL":
+ case "emailaddress":
+ case "EMAILADDRESS": aoi = BCStyle.EmailAddress;break; // should be SAN extension
+ case "initials": aoi = BCStyle.INITIALS; break;
+ case "pseudonym": aoi = BCStyle.PSEUDONYM; break;
+ case "generationQualifier": aoi = BCStyle.GENERATION; break;
+ case "serialNumber": aoi = BCStyle.SERIALNUMBER; break;
+ default:
+ throw new CertException("Unknown ASN1ObjectIdentifier for " + tv[0]);
+ }
+ tag = tv[0];
+ value = tv[1];
+ }
+
+ /**
+ * Parse various forms of DNs into appropriate RDNs, which have the ASN1ObjectIdentifier
+ * @param delim
+ * @param dnString
+ * @return
+ * @throws CertException
+ */
+ public static List<RDN> parse(final char delim, final String dnString ) throws CertException {
+ List<RDN> lrnd = new ArrayList<RDN>();
+ StringBuilder sb = new StringBuilder();
+ boolean inQuotes = false;
+ for(int i=0;i<dnString.length();++i) {
+ char c = dnString.charAt(i);
+ if(inQuotes) {
+ if('"' == c) {
+ inQuotes=false;
+ } else {
+ sb.append(dnString.charAt(i));
+ }
+ } else {
+ if('"' == c) {
+ inQuotes=true;
+ } else if(delim==c) {
+ lrnd.add(new RDN(sb.toString()));
+ sb.setLength(0);
+ } else {
+ sb.append(dnString.charAt(i));
+ }
+ }
+ }
+ if(sb.indexOf("=")>0) {
+ lrnd.add(new RDN(sb.toString()));
+ }
+ return lrnd;
+ }
+
+ @Override
+ public String toString() {
+ return tag + '=' + value;
+ }
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertDrop.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertDrop.java
new file mode 100644
index 00000000..0a9d7661
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertDrop.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.data;
+
+public class CertDrop {
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertRenew.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertRenew.java
new file mode 100644
index 00000000..242a18ac
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertRenew.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.data;
+
+public class CertRenew {
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertReq.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertReq.java
new file mode 100644
index 00000000..aa0b9c26
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertReq.java
@@ -0,0 +1,49 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.data;
+
+import java.util.List;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.cert.BCFactory;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.cadi.cm.CertException;
+
+public class CertReq {
+ // These cannot be null
+ public CA certAuthority;
+ public String mechid;
+ public List<String> fqdns;
+ // Notify
+ public List<String> emails;
+
+
+ // These may be null
+ public String sponsor;
+ public XMLGregorianCalendar start, end;
+
+ public CSRMeta getCSRMeta() throws CertException {
+ return BCFactory.createCSRMeta(certAuthority, mechid, sponsor,fqdns);
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertResp.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertResp.java
new file mode 100644
index 00000000..595025e7
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/data/CertResp.java
@@ -0,0 +1,94 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.data;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.misc.env.Trans;
+
+public class CertResp {
+ private CA ca;
+ private KeyPair keyPair;
+ private String challenge;
+
+ private String privateKey, certString;
+ private String[] trustChain;
+ private String[] trustCAs;
+ private String[] notes;
+
+ public CertResp(Trans trans, CA ca, X509Certificate x509, CSRMeta csrMeta, String[] trustChain, String[] trustCAs, String[] notes) throws IOException, GeneralSecurityException, CertException {
+ keyPair = csrMeta.keypair(trans);
+ privateKey = Factory.toString(trans, keyPair.getPrivate());
+ certString = Factory.toString(trans,x509);
+ challenge=csrMeta.challenge();
+ this.ca = ca;
+ this.trustChain = trustChain;
+ this.trustCAs = trustCAs;
+ this.notes = notes;
+ }
+
+ // Use for Read Responses, etc
+ public CertResp(String cert) {
+ certString = cert;
+ }
+
+
+ public String asCertString() {
+ return certString;
+ }
+
+ public String privateString() throws IOException {
+ return privateKey;
+ }
+
+ public String challenge() {
+ return challenge==null?"":challenge;
+ }
+
+ public String[] notes() {
+ return notes;
+ }
+
+ public Set<String> caIssuerDNs() {
+ return ca.getCaIssuerDNs();
+ }
+
+ public String env() {
+ return ca.getEnv();
+ }
+
+ public String[] trustChain() {
+ return trustChain;
+ }
+
+ public String[] trustCAs() {
+ return trustCAs;
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade.java
new file mode 100644
index 00000000..9eb9c2fa
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade.java
@@ -0,0 +1,182 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.facade;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.mapper.Mapper;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+
+
+/**
+ *
+ * @author Jonathan
+ *
+ */
+public interface Facade<REQ,CERT,ARTIFACTS,ERROR> {
+
+///////////////////// STANDARD ELEMENTS //////////////////
+ /**
+ * @param trans
+ * @param response
+ * @param result
+ */
+ void error(AuthzTrans trans, HttpServletResponse response, Result<?> result);
+
+ /**
+ *
+ * @param trans
+ * @param response
+ * @param status
+ */
+ void error(AuthzTrans trans, HttpServletResponse response, int status, String msg, String ... detail);
+
+ /**
+ * Permission checker
+ *
+ * @param trans
+ * @param resp
+ * @param perm
+ * @return
+ * @throws IOException
+ */
+ Result<Void> check(AuthzTrans trans, HttpServletResponse resp, String perm) throws IOException;
+
+ /**
+ *
+ * @return
+ */
+ public Mapper<REQ,CERT,ARTIFACTS,ERROR> mapper();
+
+///////////////////// STANDARD ELEMENTS //////////////////
+
+ /**
+ *
+ * @param trans
+ * @param resp
+ * @param rservlet
+ * @return
+ */
+ public abstract Result<Void> requestCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca);
+
+ /**
+ *
+ * @param trans
+ * @param resp
+ * @param rservlet
+ * @return
+ */
+ public abstract Result<Void> requestPersonalCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca);
+
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ public abstract Result<Void> renewCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, boolean withTrust);
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ public abstract Result<Void> dropCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp);
+
+
+ /**
+ *
+ * @param trans
+ * @param resp
+ * @param pathParam
+ * @return
+ */
+ public Result<Void> readCertsByMechID(AuthzTrans trans, HttpServletResponse resp, String mechID);
+
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ Result<Void> createArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp);
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ Result<Void> readArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp);
+
+ /**
+ *
+ * @param trans
+ * @param resp
+ * @param mechid
+ * @param machine
+ * @return
+ */
+ Result<Void> readArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine);
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ Result<Void> updateArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp);
+
+ /**
+ *
+ * @param trans
+ * @param req
+ * @param resp
+ * @return
+ */
+ Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp);
+
+ /**
+ *
+ * @param trans
+ * @param resp
+ * @param mechid
+ * @param machine
+ * @return
+ */
+ Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine);
+
+
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade1_0.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade1_0.java
new file mode 100644
index 00000000..49d976b2
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/Facade1_0.java
@@ -0,0 +1,46 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.facade;
+
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.mapper.Mapper;
+import org.onap.aaf.auth.cm.service.CMService;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+
+import aaf.v2_0.Error;
+import certman.v1_0.Artifacts;
+import certman.v1_0.BaseRequest;
+import certman.v1_0.CertInfo;
+
+/**
+ * @author Jonathan
+ *
+ */
+public class Facade1_0 extends FacadeImpl<BaseRequest,CertInfo, Artifacts, Error> {
+ public Facade1_0(AAF_CM certman,
+ CMService service,
+ Mapper<BaseRequest,CertInfo,Artifacts,Error> mapper,
+ Data.TYPE type) throws APIException {
+ super(certman, service, mapper, type);
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeFactory.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeFactory.java
new file mode 100644
index 00000000..6eb13f93
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeFactory.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.facade;
+
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.mapper.Mapper1_0;
+import org.onap.aaf.auth.cm.service.CMService;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+
+
+public class FacadeFactory {
+ public static Facade1_0 v1_0(AAF_CM certman, AuthzTrans trans, CMService service, Data.TYPE type) throws APIException {
+ return new Facade1_0(
+ certman,
+ service,
+ new Mapper1_0(),
+ type);
+ }
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeImpl.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeImpl.java
new file mode 100644
index 00000000..0598ee60
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/facade/FacadeImpl.java
@@ -0,0 +1,643 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.facade;
+
+import static org.onap.aaf.auth.layer.Result.ERR_ActionNotCompleted;
+import static org.onap.aaf.auth.layer.Result.ERR_BadData;
+import static org.onap.aaf.auth.layer.Result.ERR_ConflictAlreadyExists;
+import static org.onap.aaf.auth.layer.Result.ERR_Denied;
+import static org.onap.aaf.auth.layer.Result.ERR_NotFound;
+import static org.onap.aaf.auth.layer.Result.ERR_NotImplemented;
+import static org.onap.aaf.auth.layer.Result.ERR_Policy;
+import static org.onap.aaf.auth.layer.Result.ERR_Security;
+import static org.onap.aaf.auth.layer.Result.OK;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.data.CertResp;
+import org.onap.aaf.auth.cm.mapper.Mapper;
+import org.onap.aaf.auth.cm.mapper.Mapper.API;
+import org.onap.aaf.auth.cm.service.CMService;
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.Status;
+import org.onap.aaf.auth.env.AuthzEnv;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.cm.CertException;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Data;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+import org.onap.aaf.misc.env.util.Split;
+import org.onap.aaf.misc.rosetta.env.RosettaDF;
+import org.onap.aaf.misc.rosetta.env.RosettaData;
+
+/**
+ * AuthzFacade
+ *
+ * This Service Facade encapsulates the essence of the API Service can do, and provides
+ * a single created object for elements such as RosettaDF.
+ *
+ * The Responsibilities of this class are to:
+ * 1) Interact with the Service Implementation (which might be supported by various kinds of Backend Storage)
+ * 2) Validate incoming data (if applicable)
+ * 3) Convert the Service response into the right Format, and mark the Content Type
+ * a) In the future, we may support multiple Response Formats, aka JSON or XML, based on User Request.
+ * 4) Log Service info, warnings and exceptions as necessary
+ * 5) When asked by the API layer, this will create and write Error content to the OutputStream
+ *
+ * Note: This Class does NOT set the HTTP Status Code. That is up to the API layer, so that it can be
+ * clearly coordinated with the API Documentation
+ *
+ * @author Jonathan
+ *
+ */
+public abstract class FacadeImpl<REQ,CERT,ARTIFACTS,ERROR> extends org.onap.aaf.auth.layer.FacadeImpl implements Facade<REQ,CERT,ARTIFACTS,ERROR>
+ {
+ private static final String TRUE = "TRUE";
+ private static final String REQUEST_CERT = "Request New Certificate";
+ private static final String RENEW_CERT = "Renew Certificate";
+ private static final String DROP_CERT = "Drop Certificate";
+ private static final String READ_CERTS_MECHID = "Read Certificates by MechID";
+ private static final String CREATE_ARTIFACTS = "Create Deployment Artifact";
+ private static final String READ_ARTIFACTS = "Read Deployment Artifact";
+ private static final String UPDATE_ARTIFACTS = "Update Deployment Artifact";
+ private static final String DELETE_ARTIFACTS = "Delete Deployment Artifact";
+
+ private CMService service;
+
+ private final RosettaDF<ERROR> errDF;
+ private final RosettaDF<REQ> certRequestDF, certRenewDF, certDropDF;
+ private final RosettaDF<CERT> certDF;
+ private final RosettaDF<ARTIFACTS> artiDF;
+ private Mapper<REQ, CERT, ARTIFACTS, ERROR> mapper;
+// private Slot sCertAuth;
+ private AAF_CM certman;
+ private final String voidResp;
+
+ public FacadeImpl(AAF_CM certman,
+ CMService service,
+ Mapper<REQ,CERT,ARTIFACTS,ERROR> mapper,
+ Data.TYPE dataType) throws APIException {
+ this.service = service;
+ this.mapper = mapper;
+ this.certman = certman;
+ AuthzEnv env = certman.env;
+ //TODO: Gabe [JUnit] Static issue, talk to Jonathan
+ (errDF = env.newDataFactory(mapper.getClass(API.ERROR))).in(dataType).out(dataType);
+ (certRequestDF = env.newDataFactory(mapper.getClass(API.CERT_REQ))).in(dataType).out(dataType);
+ (certRenewDF = env.newDataFactory(mapper.getClass(API.CERT_RENEW))).in(dataType).out(dataType);
+ (certDropDF = env.newDataFactory(mapper.getClass(API.CERT_DROP))).in(dataType).out(dataType);
+ (certDF = env.newDataFactory(mapper.getClass(API.CERT))).in(dataType).out(dataType);
+ (artiDF = env.newDataFactory(mapper.getClass(API.ARTIFACTS))).in(dataType).out(dataType);
+// sCertAuth = env.slot(API_Cert.CERT_AUTH);
+ if(artiDF.getOutType().name().contains("xml")) {
+ voidResp = "application/Void+xml;charset=utf-8;version=1.0,application/xml;version=1.0,*/*";
+ } else {
+ voidResp = "application/Void+json;charset=utf-8;version=1.0,application/json;version=1.0,*/*";
+ }
+ }
+
+ public Mapper<REQ,CERT,ARTIFACTS,ERROR> mapper() {
+ return mapper;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.facade.AuthzFacade#error(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, int)
+ *
+ * Note: Conforms to AT&T TSS RESTful Error Structure
+ */
+ @Override
+ public void error(AuthzTrans trans, HttpServletResponse response, Result<?> result) {
+ error(trans, response, result.status,
+ result.details==null?"":result.details.trim(),
+ result.variables==null?new String[0]:result.variables);
+ }
+
+ @Override
+ public void error(AuthzTrans trans, HttpServletResponse response, int status, final String _msg, final String ... _detail) {
+ String msgId;
+ String prefix;
+ boolean hidemsg=false;
+ switch(status) {
+ case 202:
+ case ERR_ActionNotCompleted:
+ msgId = "SVC1202";
+ prefix = "Accepted, Action not complete";
+ response.setStatus(/*httpstatus=*/202);
+ break;
+
+ case 403:
+ case ERR_Policy:
+ case ERR_Security:
+ case ERR_Denied:
+ msgId = "SVC1403";
+ prefix = "Forbidden";
+ response.setStatus(/*httpstatus=*/403);
+ break;
+
+ case 404:
+ case ERR_NotFound:
+ msgId = "SVC1404";
+ prefix = "Not Found";
+ response.setStatus(/*httpstatus=*/404);
+ break;
+
+ case 406:
+ case ERR_BadData:
+ msgId="SVC1406";
+ prefix = "Not Acceptable";
+ response.setStatus(/*httpstatus=*/406);
+ break;
+
+ case 409:
+ case ERR_ConflictAlreadyExists:
+ msgId = "SVC1409";
+ prefix = "Conflict Already Exists";
+ response.setStatus(/*httpstatus=*/409);
+ break;
+
+ case 501:
+ case ERR_NotImplemented:
+ msgId = "SVC1501";
+ prefix = "Not Implemented";
+ response.setStatus(/*httpstatus=*/501);
+ break;
+
+
+ default:
+ msgId = "SVC1500";
+ prefix = "General Service Error";
+ response.setStatus(/*httpstatus=*/500);
+ hidemsg=true;
+ break;
+ }
+
+ try {
+ StringBuilder holder = new StringBuilder();
+ ERROR em = mapper().errorFromMessage(holder, msgId,prefix + ": " + _msg,_detail);
+ trans.checkpoint(
+ "ErrResp [" +
+ msgId +
+ "] " +
+ holder.toString(),
+ Env.ALWAYS);
+ if(hidemsg) {
+ holder.setLength(0);
+ em = mapper().errorFromMessage(holder, msgId, "Server had an issue processing this request");
+ }
+ errDF.newData(trans).load(em).to(response.getOutputStream());
+
+ } catch (Exception e) {
+ trans.error().log(e,"unable to send response for",_msg);
+ }
+ }
+
+ @Override
+ public Result<Void> check(AuthzTrans trans, HttpServletResponse resp, String perm) throws IOException {
+ String[] p = Split.split('|',perm);
+ if(p.length!=3) {
+ return Result.err(Result.ERR_BadData,"Invalid Perm String");
+ }
+ AAFPermission ap = new AAFPermission(p[0],p[1],p[2]);
+ if(certman.aafLurPerm.fish(trans.getUserPrincipal(), ap)) {
+ resp.setContentType(voidResp);
+ resp.getOutputStream().write(0);
+ return Result.ok();
+ } else {
+ return Result.err(Result.ERR_Denied,"%s does not have %s",trans.user(),ap.getKey());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.auth.certman.facade.Facade#requestCert(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public Result<Void> requestCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca) {
+ TimeTaken tt = trans.start(REQUEST_CERT, Env.SUB|Env.ALWAYS);
+ String wt;
+ boolean withTrust=(wt=req.getParameter("withTrust"))!=null || TRUE.equalsIgnoreCase(wt);
+ try {
+ REQ request;
+ try {
+ Data<REQ> rd = certRequestDF.newData().load(req.getInputStream());
+ request = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,REQUEST_CERT);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+ Result<CertResp> rcr = service.requestCert(trans,mapper.toReq(trans,request), ca);
+ if(rcr.notOK()) {
+ return Result.err(rcr);
+ }
+
+// CA certAuth = trans.get(sCertAuth,null);
+ Result<CERT> rc = mapper.toCert(trans, rcr, withTrust);
+ switch(rc.status) {
+ case OK:
+ RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
+ data.to(resp.getOutputStream());
+
+ setContentType(resp,certDF.getOutType());
+ return Result.ok();
+ default:
+ return Result.err(rc);
+ }
+
+ } catch (Exception e) {
+ trans.error().log(e,IN,REQUEST_CERT);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.facade.Facade#requestPersonalCert(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean)
+ */
+ @Override
+ public Result<Void> requestPersonalCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca) {
+ return Result.err(Result.ERR_NotImplemented,"not implemented yet");
+// Result<CertResp> rcr = service.requestPersonalCert(trans,ca);
+// if(rcr.notOK()) {
+// return Result.err(rcr);
+// } else {
+// try {
+// resp.setContentType("application/zip, application/octet-stream");
+// ZipOutputStream zos = new ZipOutputStream(resp.getOutputStream());
+// PrintStream ps = new PrintStream(zos);
+// ZipEntry ze = new ZipEntry(trans.user()+".key");
+// zos.putNextEntry(ze);
+// ps.print(rcr.value.privateString());
+// zos.closeEntry();
+//
+// zos.putNextEntry(new ZipEntry(trans.user()+".crt"));
+// ps.print(rcr.value.asCertString());
+// zos.closeEntry();
+//
+// String wt;
+// if((wt=req.getParameter("withTrust"))!=null || TRUE.equalsIgnoreCase(wt)) {
+// zos.putNextEntry(new ZipEntry(trans.user()+".trustCrts"));
+// for(String s : ca.getTrustChain()) {
+// ps.println(s);
+// }
+// zos.closeEntry();
+// }
+//
+// boolean withJKS = (wt=req.getParameter("withJKS"))!=null || TRUE.equalsIgnoreCase(wt);
+// if(withJKS) {
+// if(trans.getUserPrincipal() instanceof BasicPrincipal) {
+// char[] cap = new String(((BasicPrincipal)trans.getUserPrincipal()).getCred()).toCharArray();
+// KeyStore ks = keystore(trans, rcr.value, ca.getTrustChain(), trans.user(), cap);
+// zos.putNextEntry(new ZipEntry(trans.user()+".jks"));
+// ks.store(zos, cap);
+// zos.closeEntry();
+// }
+// }
+//
+// zos.putNextEntry(new ZipEntry("cert_deploy.sh"));
+// ps.println("# Deploy Certificate to ~/.aaf");
+// ps.println("if [ \"$1\" = \"\" ]; then echo \"sh deploy.sh <zipfile>\";exit; else chmod 700 $HOME/.aaf; fi");
+// ps.println("chmod 600 $1");
+// ps.println("if [ ! -e $HOME/.aaf ]; then mkdir -m 700 $HOME/.aaf; fi");
+// ps.println("THE_PWD=`pwd`");
+// ps.println("cd $HOME/.aaf");
+// ps.println("echo \"Deploying to `pwd`\"");
+// ps.println("jar -xvf $THE_PWD/$1 " + trans.user());
+// ps.println("chmod 600 " + trans.user() + ".key");
+// if(withJKS) {
+// ps.println("chmod 600 " + trans.user() + ".jks");
+// }
+// ps.println("cd $THE_PWD");
+// ps.println("rm cert_deploy.sh");
+// zos.closeEntry();
+//
+//
+// zos.close();
+//
+// } catch (IOException | KeyStoreException | CertificateException | APIException | CertException | NoSuchAlgorithmException e) {
+// return Result.err(e);
+// }
+// }
+//
+// return Result.ok();
+ }
+
+ private KeyStore keystore(AuthzTrans trans, CertResp cr, String[] trustChain, String name, char[] cap) throws KeyStoreException, CertificateException, APIException, IOException, CertException, NoSuchAlgorithmException {
+ KeyStore jks = KeyStore.getInstance("jks");
+ jks.load(null, cap);
+
+ // Get the Cert(s)... Might include Trust store
+ List<String> lcerts = new ArrayList<String>();
+ lcerts.add(cr.asCertString());
+ for(String s : trustChain) {
+ lcerts.add(s);
+ }
+
+ Collection<? extends Certificate> certColl = Factory.toX509Certificate(lcerts);
+ X509Certificate[] certs = new X509Certificate[certColl.size()];
+ certColl.toArray(certs);
+ KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(cap);
+
+ PrivateKey pk = Factory.toPrivateKey(trans, cr.privateString());
+ KeyStore.PrivateKeyEntry pkEntry =
+ new KeyStore.PrivateKeyEntry(pk, new Certificate[] {certs[0]});
+ jks.setEntry(name, pkEntry, protParam);
+
+ int i=0;
+ for(X509Certificate x509 : certs) {
+ jks.setCertificateEntry("cert_"+ ++i, x509);
+ }
+ return jks;
+ }
+
+ @Override
+ public Result<Void> renewCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, boolean withTrust) {
+ TimeTaken tt = trans.start(RENEW_CERT, Env.SUB|Env.ALWAYS);
+ try {
+ REQ request;
+ try {
+ Data<REQ> rd = certRenewDF.newData().load(req.getInputStream());
+ request = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,RENEW_CERT);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+// String certAuth = trans.get(sCertAuth,null);
+ Result<CertResp> rcr = service.renewCert(trans,mapper.toRenew(trans,request));
+ Result<CERT> rc = mapper.toCert(trans, rcr, withTrust);
+
+ switch(rc.status) {
+ case OK:
+ RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
+ data.to(resp.getOutputStream());
+
+ setContentType(resp,certDF.getOutType());
+ return Result.ok();
+ default:
+ return Result.err(rc);
+ }
+ } catch (Exception e) {
+ trans.error().log(e,IN,RENEW_CERT);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+
+ }
+
+ @Override
+ public Result<Void> dropCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
+ TimeTaken tt = trans.start(DROP_CERT, Env.SUB|Env.ALWAYS);
+ try {
+ REQ request;
+ try {
+ Data<REQ> rd = certDropDF.newData().load(req.getInputStream());
+ request = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,DROP_CERT);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+ Result<Void> rv = service.dropCert(trans,mapper.toDrop(trans, request));
+ switch(rv.status) {
+ case OK:
+ setContentType(resp,certRequestDF.getOutType());
+ return Result.ok();
+ default:
+ return Result.err(rv);
+ }
+ } catch (Exception e) {
+ trans.error().log(e,IN,DROP_CERT);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.facade.Facade#readCertsByMechID(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, java.lang.String)
+ */
+ @Override
+ public Result<Void> readCertsByMechID(AuthzTrans trans, HttpServletResponse resp, String mechID) {
+ TimeTaken tt = trans.start(READ_CERTS_MECHID, Env.SUB|Env.ALWAYS);
+ try {
+ Result<CERT> rc = mapper.toCert(trans, service.readCertsByMechID(trans,mechID));
+ switch(rc.status) {
+ case OK:
+ RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
+ data.to(resp.getOutputStream());
+
+ setContentType(resp,certDF.getOutType());
+ return Result.ok();
+ default:
+ return Result.err(rc);
+ }
+ } catch (Exception e) {
+ trans.error().log(e,IN,READ_CERTS_MECHID);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ ////////////////////////////
+ // Artifacts
+ ////////////////////////////
+ @Override
+ public Result<Void> createArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
+ TimeTaken tt = trans.start(CREATE_ARTIFACTS, Env.SUB);
+ try {
+ ARTIFACTS arti;
+ try {
+ Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
+ arti = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,CREATE_ARTIFACTS);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+ return service.createArtifact(trans,mapper.toArtifact(trans,arti));
+ } catch (Exception e) {
+
+ trans.error().log(e,IN,CREATE_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ @Override
+ public Result<Void> readArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
+ TimeTaken tt = trans.start(READ_ARTIFACTS, Env.SUB);
+ try {
+ String mechid = req.getParameter("mechid");
+ String machine = req.getParameter("machine");
+ String ns = req.getParameter("ns");
+
+ Result<ARTIFACTS> ra;
+ if( machine !=null && mechid == null) {
+ ra = mapper.fromArtifacts(service.readArtifactsByMachine(trans, machine));
+ } else if(mechid!=null && machine==null) {
+ ra = mapper.fromArtifacts(service.readArtifactsByMechID(trans, mechid));
+ } else if(mechid!=null && machine!=null) {
+ ArtiDAO.Data add = new ArtiDAO.Data();
+ add.mechid = mechid;
+ add.machine = machine;
+ add.ns = ns;
+ ra = mapper.fromArtifacts(service.readArtifacts(trans,add));
+ } else if(ns!=null) {
+ ra = mapper.fromArtifacts(service.readArtifactsByNs(trans, ns));
+ } else {
+ ra = Result.err(Status.ERR_BadData,"Invalid request inputs");
+ }
+
+ if(ra.isOK()) {
+ RosettaData<ARTIFACTS> data = artiDF.newData(trans).load(ra.value);
+ data.to(resp.getOutputStream());
+ setContentType(resp,artiDF.getOutType());
+ return Result.ok();
+ } else {
+ return Result.err(ra);
+ }
+
+ } catch (Exception e) {
+ trans.error().log(e,IN,READ_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ @Override
+ public Result<Void> readArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine) {
+ TimeTaken tt = trans.start(READ_ARTIFACTS, Env.SUB);
+ try {
+ ArtiDAO.Data add = new ArtiDAO.Data();
+ add.mechid = mechid;
+ add.machine = machine;
+ Result<ARTIFACTS> ra = mapper.fromArtifacts(service.readArtifacts(trans,add));
+ if(ra.isOK()) {
+ RosettaData<ARTIFACTS> data = artiDF.newData(trans).load(ra.value);
+ data.to(resp.getOutputStream());
+ setContentType(resp,artiDF.getOutType());
+ return Result.ok();
+ } else {
+ return Result.err(ra);
+ }
+ } catch (Exception e) {
+ trans.error().log(e,IN,READ_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+
+ @Override
+ public Result<Void> updateArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
+ TimeTaken tt = trans.start(UPDATE_ARTIFACTS, Env.SUB);
+ try {
+ ARTIFACTS arti;
+ try {
+ Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
+ arti = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,UPDATE_ARTIFACTS);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+ return service.updateArtifact(trans,mapper.toArtifact(trans,arti));
+ } catch (Exception e) {
+ trans.error().log(e,IN,UPDATE_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ @Override
+ public Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
+ TimeTaken tt = trans.start(DELETE_ARTIFACTS, Env.SUB);
+ try {
+ ARTIFACTS arti;
+ try {
+ Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
+ arti = rd.asObject();
+ } catch(APIException e) {
+ trans.error().log("Invalid Input",IN,DELETE_ARTIFACTS);
+ return Result.err(Result.ERR_BadData,"Invalid Input");
+ }
+
+ Result<Void> rv = service.deleteArtifact(trans,mapper.toArtifact(trans,arti));
+ switch(rv.status) {
+ case OK:
+ setContentType(resp,artiDF.getOutType());
+ }
+ return rv;
+ } catch (Exception e) {
+ trans.error().log(e,IN,DELETE_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+ @Override
+ public Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine) {
+ TimeTaken tt = trans.start(DELETE_ARTIFACTS, Env.SUB);
+ try {
+ Result<Void> rv = service.deleteArtifact(trans, mechid, machine);
+ switch(rv.status) {
+ case OK:
+ setContentType(resp,artiDF.getOutType());
+ }
+ return rv;
+ } catch (Exception e) {
+ trans.error().log(e,IN,DELETE_ARTIFACTS);
+ return Result.err(e);
+ } finally {
+ tt.done();
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper.java
new file mode 100644
index 00000000..aadb6650
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper.java
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.mapper;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.onap.aaf.auth.cm.data.CertDrop;
+import org.onap.aaf.auth.cm.data.CertRenew;
+import org.onap.aaf.auth.cm.data.CertReq;
+import org.onap.aaf.auth.cm.data.CertResp;
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.CertDAO;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+
+public interface Mapper<REQ,CERT,ARTIFACTS,ERROR>
+{
+ public enum API{ERROR,VOID,CERT,CERT_REQ,CERT_RENEW,CERT_DROP,ARTIFACTS};
+
+ public Class<?> getClass(API api);
+ public<A> A newInstance(API api);
+
+ public ERROR errorFromMessage(StringBuilder holder, String msgID, String text, String... detail);
+
+ public Result<CERT> toCert(AuthzTrans trans, Result<CertResp> in, boolean withTrustChain) throws IOException;
+ public Result<CERT> toCert(AuthzTrans trans, Result<List<CertDAO.Data>> in);
+
+ public Result<CertReq> toReq(AuthzTrans trans, REQ req);
+ public Result<CertRenew> toRenew(AuthzTrans trans, REQ req);
+ public Result<CertDrop> toDrop(AuthzTrans trans, REQ req);
+
+ public List<ArtiDAO.Data> toArtifact(AuthzTrans trans, ARTIFACTS arti);
+ public Result<ARTIFACTS> fromArtifacts(Result<List<ArtiDAO.Data>> readArtifactsByMachine);
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper1_0.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper1_0.java
new file mode 100644
index 00000000..3d865d30
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper1_0.java
@@ -0,0 +1,274 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.mapper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.auth.cm.data.CertDrop;
+import org.onap.aaf.auth.cm.data.CertRenew;
+import org.onap.aaf.auth.cm.data.CertReq;
+import org.onap.aaf.auth.cm.data.CertResp;
+import org.onap.aaf.auth.cm.validation.CertmanValidator;
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.CertDAO;
+import org.onap.aaf.auth.dao.cass.ArtiDAO.Data;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.cadi.util.Vars;
+
+import aaf.v2_0.Error;
+import certman.v1_0.Artifacts;
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.BaseRequest;
+import certman.v1_0.CertInfo;
+import certman.v1_0.CertificateDrop;
+import certman.v1_0.CertificateRenew;
+import certman.v1_0.CertificateRequest;
+
+
+public class Mapper1_0 implements Mapper<BaseRequest,CertInfo,Artifacts,Error> {
+
+ @Override
+ public Class<?> getClass(API api) {
+ switch(api) {
+ case CERT_REQ: return CertificateRequest.class;
+ case CERT_RENEW: return CertificateRenew.class;
+ case CERT_DROP: return CertificateDrop.class;
+ case CERT: return CertInfo.class;
+ case ARTIFACTS: return Artifacts.class;
+ case ERROR: return Error.class;
+ case VOID: return Void.class;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <A> A newInstance(API api) {
+ switch(api) {
+ case CERT_REQ: return (A) new CertificateRequest();
+ case CERT_RENEW: return (A) new CertificateRenew();
+ case CERT_DROP: return (A) new CertificateDrop();
+ case CERT: return (A) new CertInfo();
+ case ARTIFACTS: return (A) new Artifacts();
+ case ERROR: return (A)new Error();
+ case VOID: return null;
+ }
+ return null;
+ }
+
+ ////////////// Mapping Functions /////////////
+ @Override
+ public Error errorFromMessage(StringBuilder holder, String msgID, String text, String... var) {
+ Error err = new Error();
+ err.setMessageId(msgID);
+ // AT&T Restful Error Format requires numbers "%" placements
+ err.setText(Vars.convert(holder, text, var));
+ for(String s : var) {
+ err.getVariables().add(s);
+ }
+ return err;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toCert(org.onap.aaf.auth.env.test.AuthzTrans, org.onap.aaf.auth.layer.test.Result)
+ */
+ @Override
+ public Result<CertInfo> toCert(AuthzTrans trans, Result<CertResp> in, boolean withTrustChain) throws IOException {
+ if(in.isOK()) {
+ CertResp cin = in.value;
+ CertInfo cout = newInstance(API.CERT);
+ cout.setPrivatekey(cin.privateString());
+ String value;
+ if((value=cin.challenge())!=null) {
+ cout.setChallenge(value);
+ }
+ cout.getCerts().add(cin.asCertString());
+ if(cin.trustChain()!=null) {
+ for(String c : cin.trustChain()) {
+ if(c!=null) {
+ cout.getCerts().add(c);
+ }
+ }
+ }
+ // Adding all the Certs in one response is a mistake. Makes it very hard for Agent to setup
+ // Certs in keystore versus Truststore. Separate in Version 2_0
+ if(cin.trustCAs()!=null) {
+ for(String c : cin.trustCAs()) {
+ if(c!=null) {
+ cout.getCerts().add(c);
+ }
+ }
+ }
+ if(cin.notes()!=null) {
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ for(String n : cin.notes()) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append('\n');
+ }
+ sb.append(n);
+ }
+ cout.setNotes(sb.toString());
+ }
+ cout.getCaIssuerDNs().addAll(cin.caIssuerDNs());
+ cout.setEnv(cin.env());
+ return Result.ok(cout);
+ } else {
+ return Result.err(in);
+ }
+ }
+
+ @Override
+ public Result<CertInfo> toCert(AuthzTrans trans, Result<List<CertDAO.Data>> in) {
+ if(in.isOK()) {
+ CertInfo cout = newInstance(API.CERT);
+ List<String> certs = cout.getCerts();
+ for(CertDAO.Data cdd : in.value) {
+ certs.add(cdd.x509);
+ }
+ return Result.ok(cout);
+ } else {
+ return Result.err(in);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toReq(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertReq> toReq(AuthzTrans trans, BaseRequest req) {
+ CertificateRequest in;
+ try {
+ in = (CertificateRequest)req;
+ } catch(ClassCastException e) {
+ return Result.err(Result.ERR_BadData,"Request is not a CertificateRequest");
+ }
+
+ CertReq out = new CertReq();
+ CertmanValidator v = new CertmanValidator();
+ v.isNull("CertRequest", req)
+ .nullOrBlank("MechID", out.mechid=in.getMechid());
+ v.nullBlankMin("FQDNs", out.fqdns=in.getFqdns(),1);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData, v.errs());
+ }
+ out.emails = in.getEmail();
+ out.sponsor=in.getSponsor();
+ out.start = in.getStart();
+ out.end = in.getEnd();
+ out.fqdns = in.getFqdns();
+ return Result.ok(out);
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toRenew(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertRenew> toRenew(AuthzTrans trans, BaseRequest req) {
+ return Result.err(Result.ERR_NotImplemented,"Not Implemented... yet");
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toDrop(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertDrop> toDrop(AuthzTrans trans, BaseRequest req) {
+ return Result.err(Result.ERR_NotImplemented,"Not Implemented... yet");
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.mapper.Mapper#toArtifact(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public List<ArtiDAO.Data> toArtifact(AuthzTrans trans, Artifacts artifacts) {
+ List<ArtiDAO.Data> ladd = new ArrayList<ArtiDAO.Data>();
+ for(Artifact arti : artifacts.getArtifact()) {
+ ArtiDAO.Data data = new ArtiDAO.Data();
+ data.mechid = arti.getMechid();
+ data.machine = arti.getMachine();
+ data.type(true).addAll(arti.getType());
+ data.ca = arti.getCa();
+ data.dir = arti.getDir();
+ data.os_user = arti.getOsUser();
+ // Optional (on way in)
+ data.ns = arti.getNs();
+ data.renewDays = arti.getRenewDays();
+ data.notify = arti.getNotification();
+
+ // Ignored on way in for create/update
+ data.sponsor = arti.getSponsor();
+ data.expires = null;
+
+ // Derive Optional Data from Machine (Domain) if exists
+ if(data.machine!=null) {
+ if(data.ca==null) {
+ if(data.machine.endsWith(".att.com")) {
+ data.ca = "aaf"; // default
+ }
+ }
+ if(data.ns==null ) {
+ data.ns=FQI.reverseDomain(data.machine);
+ }
+ }
+ data.sans(true).addAll(arti.getSans());
+ ladd.add(data);
+ }
+ return ladd;
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.mapper.Mapper#fromArtifacts(org.onap.aaf.auth.layer.test.Result)
+ */
+ @Override
+ public Result<Artifacts> fromArtifacts(Result<List<Data>> lArtiDAO) {
+ if(lArtiDAO.isOK()) {
+ Artifacts artis = new Artifacts();
+ for(ArtiDAO.Data arti : lArtiDAO.value) {
+ Artifact a = new Artifact();
+ a.setMechid(arti.mechid);
+ a.setMachine(arti.machine);
+ a.setSponsor(arti.sponsor);
+ a.setNs(arti.ns);
+ a.setCa(arti.ca);
+ a.setDir(arti.dir);
+ a.getType().addAll(arti.type(false));
+ a.setOsUser(arti.os_user);
+ a.setRenewDays(arti.renewDays);
+ a.setNotification(arti.notify);
+ a.getSans().addAll(arti.sans(false));
+ artis.getArtifact().add(a);
+ }
+ return Result.ok(artis);
+ } else {
+ return Result.err(lArtiDAO);
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper2_0.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper2_0.java
new file mode 100644
index 00000000..13123bdf
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/mapper/Mapper2_0.java
@@ -0,0 +1,268 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.mapper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.auth.cm.data.CertDrop;
+import org.onap.aaf.auth.cm.data.CertRenew;
+import org.onap.aaf.auth.cm.data.CertReq;
+import org.onap.aaf.auth.cm.data.CertResp;
+import org.onap.aaf.auth.cm.validation.CertmanValidator;
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.ArtiDAO.Data;
+import org.onap.aaf.auth.dao.cass.CertDAO;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.cadi.util.Vars;
+
+import aaf.v2_0.Error;
+import certman.v1_0.Artifacts;
+import certman.v1_0.Artifacts.Artifact;
+import certman.v1_0.BaseRequest;
+import certman.v1_0.CertInfo;
+import certman.v1_0.CertificateDrop;
+import certman.v1_0.CertificateRenew;
+import certman.v1_0.CertificateRequest;
+
+
+public class Mapper2_0 implements Mapper<BaseRequest,CertInfo,Artifacts,Error> {
+
+ @Override
+ public Class<?> getClass(API api) {
+ switch(api) {
+ case CERT_REQ: return CertificateRequest.class;
+ case CERT_RENEW: return CertificateRenew.class;
+ case CERT_DROP: return CertificateDrop.class;
+ case CERT: return CertInfo.class;
+ case ARTIFACTS: return Artifacts.class;
+ case ERROR: return Error.class;
+ case VOID: return Void.class;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <A> A newInstance(API api) {
+ switch(api) {
+ case CERT_REQ: return (A) new CertificateRequest();
+ case CERT_RENEW: return (A) new CertificateRenew();
+ case CERT_DROP: return (A) new CertificateDrop();
+ case CERT: return (A) new CertInfo();
+ case ARTIFACTS: return (A) new Artifacts();
+ case ERROR: return (A)new Error();
+ case VOID: return null;
+ }
+ return null;
+ }
+
+ ////////////// Mapping Functions /////////////
+ @Override
+ public Error errorFromMessage(StringBuilder holder, String msgID, String text, String... var) {
+ Error err = new Error();
+ err.setMessageId(msgID);
+ // AT&T Restful Error Format requires numbers "%" placements
+ err.setText(Vars.convert(holder, text, var));
+ for(String s : var) {
+ err.getVariables().add(s);
+ }
+ return err;
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toCert(org.onap.aaf.auth.env.test.AuthzTrans, org.onap.aaf.auth.layer.test.Result)
+ */
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toCert(org.onap.aaf.auth.env.test.AuthzTrans, org.onap.aaf.auth.layer.test.Result)
+ */
+ @Override
+ public Result<CertInfo> toCert(AuthzTrans trans, Result<CertResp> in, boolean withTrustChain) throws IOException {
+ if(in.isOK()) {
+ CertResp cin = in.value;
+ CertInfo cout = newInstance(API.CERT);
+ cout.setPrivatekey(cin.privateString());
+ String value;
+ if((value=cin.challenge())!=null) {
+ cout.setChallenge(value);
+ }
+ cout.getCerts().add(cin.asCertString());
+ if(cin.trustChain()!=null) {
+ for(String c : cin.trustChain()) {
+ cout.getCerts().add(c);
+ }
+ }
+ if(cin.notes()!=null) {
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ for(String n : cin.notes()) {
+ if(first) {
+ first = false;
+ } else {
+ sb.append('\n');
+ }
+ sb.append(n);
+ }
+ cout.setNotes(sb.toString());
+ }
+ cout.getCaIssuerDNs().addAll(cin.caIssuerDNs());
+ cout.setEnv(cin.env());
+ return Result.ok(cout);
+ } else {
+ return Result.err(in);
+ }
+ }
+
+
+ @Override
+ public Result<CertInfo> toCert(AuthzTrans trans, Result<List<CertDAO.Data>> in) {
+ if(in.isOK()) {
+ CertInfo cout = newInstance(API.CERT);
+ List<String> certs = cout.getCerts();
+ for(CertDAO.Data cdd : in.value) {
+ certs.add(cdd.x509);
+ }
+ return Result.ok(cout);
+ } else {
+ return Result.err(in);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toReq(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertReq> toReq(AuthzTrans trans, BaseRequest req) {
+ CertificateRequest in;
+ try {
+ in = (CertificateRequest)req;
+ } catch(ClassCastException e) {
+ return Result.err(Result.ERR_BadData,"Request is not a CertificateRequest");
+ }
+
+ CertReq out = new CertReq();
+ CertmanValidator v = new CertmanValidator();
+ v.isNull("CertRequest", req)
+ .nullOrBlank("MechID", out.mechid=in.getMechid());
+ v.nullBlankMin("FQDNs", out.fqdns=in.getFqdns(),1);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData, v.errs());
+ }
+
+ out.emails = in.getEmail();
+ out.sponsor=in.getSponsor();
+ out.start = in.getStart();
+ out.end = in.getEnd();
+ out.fqdns = in.getFqdns();
+ return Result.ok(out);
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toRenew(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertRenew> toRenew(AuthzTrans trans, BaseRequest req) {
+ return Result.err(Result.ERR_NotImplemented,"Not Implemented... yet");
+ }
+
+ /* (non-Javadoc)
+ * @see com.att.authz.certman.mapper.Mapper#toDrop(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public Result<CertDrop> toDrop(AuthzTrans trans, BaseRequest req) {
+ return Result.err(Result.ERR_NotImplemented,"Not Implemented... yet");
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.mapper.Mapper#toArtifact(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
+ */
+ @Override
+ public List<ArtiDAO.Data> toArtifact(AuthzTrans trans, Artifacts artifacts) {
+ List<ArtiDAO.Data> ladd = new ArrayList<ArtiDAO.Data>();
+ for(Artifact arti : artifacts.getArtifact()) {
+ ArtiDAO.Data data = new ArtiDAO.Data();
+ data.mechid = arti.getMechid();
+ data.machine = arti.getMachine();
+ data.type(true).addAll(arti.getType());
+ data.ca = arti.getCa();
+ data.dir = arti.getDir();
+ data.os_user = arti.getOsUser();
+ // Optional (on way in)
+ data.ns = arti.getNs();
+ data.renewDays = arti.getRenewDays();
+ data.notify = arti.getNotification();
+
+ // Ignored on way in for create/update
+ data.sponsor = arti.getSponsor();
+ data.expires = null;
+
+ // Derive Optional Data from Machine (Domain) if exists
+ if(data.machine!=null) {
+ if(data.ca==null) {
+ if(data.machine.endsWith(".att.com")) {
+ data.ca = "aaf"; // default
+ }
+ }
+ if(data.ns==null ) {
+ data.ns=FQI.reverseDomain(data.machine);
+ }
+ }
+ data.sans(true).addAll(arti.getSans());
+ ladd.add(data);
+ }
+ return ladd;
+ }
+
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.cm.mapper.Mapper#fromArtifacts(org.onap.aaf.auth.layer.test.Result)
+ */
+ @Override
+ public Result<Artifacts> fromArtifacts(Result<List<Data>> lArtiDAO) {
+ if(lArtiDAO.isOK()) {
+ Artifacts artis = new Artifacts();
+ for(ArtiDAO.Data arti : lArtiDAO.value) {
+ Artifact a = new Artifact();
+ a.setMechid(arti.mechid);
+ a.setMachine(arti.machine);
+ a.setSponsor(arti.sponsor);
+ a.setNs(arti.ns);
+ a.setCa(arti.ca);
+ a.setDir(arti.dir);
+ a.getType().addAll(arti.type(false));
+ a.setOsUser(arti.os_user);
+ a.setRenewDays(arti.renewDays);
+ a.setNotification(arti.notify);
+ a.getSans().addAll(arti.sans(false));
+ artis.getArtifact().add(a);
+ }
+ return Result.ok(artis);
+ } else {
+ return Result.err(lArtiDAO);
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/CMService.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/CMService.java
new file mode 100644
index 00000000..f6407d90
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/CMService.java
@@ -0,0 +1,693 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.service;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.ca.CA;
+import org.onap.aaf.auth.cm.ca.X509andChain;
+import org.onap.aaf.auth.cm.cert.BCFactory;
+import org.onap.aaf.auth.cm.cert.CSRMeta;
+import org.onap.aaf.auth.cm.data.CertDrop;
+import org.onap.aaf.auth.cm.data.CertRenew;
+import org.onap.aaf.auth.cm.data.CertReq;
+import org.onap.aaf.auth.cm.data.CertResp;
+import org.onap.aaf.auth.cm.validation.CertmanValidator;
+import org.onap.aaf.auth.dao.CassAccess;
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.CacheInfoDAO;
+import org.onap.aaf.auth.dao.cass.CertDAO;
+import org.onap.aaf.auth.dao.cass.CertDAO.Data;
+import org.onap.aaf.auth.dao.cass.CredDAO;
+import org.onap.aaf.auth.dao.cass.HistoryDAO;
+import org.onap.aaf.auth.dao.cass.Status;
+import org.onap.aaf.auth.dao.hl.Question;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.layer.Result;
+import org.onap.aaf.auth.org.Organization;
+import org.onap.aaf.auth.org.Organization.Identity;
+import org.onap.aaf.auth.org.OrganizationException;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.cm.Factory;
+import org.onap.aaf.cadi.util.FQI;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
+
+
+public class CMService {
+ // If we add more CAs, may want to parameterize
+ private static final int STD_RENEWAL = 30;
+ private static final int MAX_RENEWAL = 60;
+ private static final int MIN_RENEWAL = 10;
+
+ public static final String REQUEST = "request";
+ public static final String RENEW = "renew";
+ public static final String DROP = "drop";
+// public static final String SANS = "san";
+ public static final String IPS = "ips";
+ public static final String DOMAIN = "domain";
+
+ private static final String[] NO_NOTES = new String[0];
+ private final CertDAO certDAO;
+ private final CredDAO credDAO;
+ private final ArtiDAO artiDAO;
+// private DAO<AuthzTrans, ?>[] daos;
+ private AAF_CM certman;
+
+// @SuppressWarnings("unchecked")
+ public CMService(final AuthzTrans trans, AAF_CM certman) throws APIException, IOException {
+ // Jonathan 4/2015 SessionFilter unneeded... DataStax already deals with Multithreading well
+
+ HistoryDAO hd = new HistoryDAO(trans, certman.cluster, CassAccess.KEYSPACE);
+ CacheInfoDAO cid = new CacheInfoDAO(trans, hd);
+ certDAO = new CertDAO(trans, hd, cid);
+ credDAO = new CredDAO(trans, hd, cid);
+ artiDAO = new ArtiDAO(trans, hd, cid);
+
+// daos =(DAO<AuthzTrans, ?>[]) new DAO<?,?>[] {
+// hd,cid,certDAO,credDAO,artiDAO
+// };
+//
+ this.certman = certman;
+ }
+
+ public Result<CertResp> requestCert(final AuthzTrans trans,final Result<CertReq> req, final CA ca) {
+ if(req.isOK()) {
+
+ if(req.value.fqdns.isEmpty()) {
+ return Result.err(Result.ERR_BadData,"No Machines passed in Request");
+ }
+
+ String key = req.value.fqdns.get(0);
+
+ // Policy 6: Requester must be granted Change permission in Namespace requested
+ String mechNS = FQI.reverseDomain(req.value.mechid);
+ if(mechNS==null) {
+ return Result.err(Status.ERR_Denied, "%s does not reflect a valid AAF Namespace",req.value.mechid);
+ }
+
+
+ // Disallow non-AAF CA without special permission
+ if(!ca.getName().equals("aaf") && !trans.fish( new AAFPermission(mechNS+".certman", ca.getName(), REQUEST))) {
+ return Result.err(Status.ERR_Denied, "'%s' does not have permission to request Certificates from Certificate Authority '%s'",
+ trans.user(),ca.getName());
+ }
+
+ List<String> notes = null;
+ List<String> fqdns = new ArrayList<String>(req.value.fqdns);
+
+
+ String email = null;
+
+ try {
+ Organization org = trans.org();
+
+ InetAddress primary = null;
+ // Organize incoming information to get to appropriate Artifact
+ if(fqdns.size()>=1) {
+ // Accept domain wild cards, but turn into real machines
+ // Need *domain.com:real.machine.domain.com:san.machine.domain.com:...
+ if(fqdns.get(0).startsWith("*")) { // Domain set
+ if(!trans.fish(new AAFPermission(ca.getPermType(), ca.getName(), DOMAIN))) {
+ return Result.err(Result.ERR_Denied, "Domain based Authorizations (" + fqdns.get(0) + ") requires Exception");
+ }
+
+ //TODO check for Permission in Add Artifact?
+ String domain = fqdns.get(0).substring(1);
+ fqdns.remove(0);
+ if(fqdns.size()>=1) {
+ InetAddress ia = InetAddress.getByName(fqdns.get(0));
+ if(ia==null) {
+ return Result.err(Result.ERR_Denied, "Request not made from matching IP matching domain");
+ } else if(ia.getHostName().endsWith(domain)) {
+ primary = ia;
+ }
+ } else {
+ return Result.err(Result.ERR_Denied, "Requests using domain require machine declaration");
+ }
+
+ } else {
+ for(String cn : req.value.fqdns) {
+ try {
+ InetAddress[] ias = InetAddress.getAllByName(cn);
+ Set<String> potentialSanNames = new HashSet<String>();
+ for(InetAddress ia1 : ias) {
+ InetAddress ia2 = InetAddress.getByAddress(ia1.getAddress());
+ if(primary==null && ias.length==1 && trans.ip().equals(ia1.getHostAddress())) {
+ primary = ia1;
+ } else if(!cn.equals(ia1.getHostName()) && !ia2.getHostName().equals(ia2.getHostAddress())) {
+ potentialSanNames.add(ia1.getHostName());
+ }
+ }
+ } catch (UnknownHostException e1) {
+ return Result.err(Result.ERR_BadData,"There is no DNS lookup for %s",cn);
+ }
+
+ }
+ }
+ }
+
+ if(primary==null) {
+ return Result.err(Result.ERR_Denied, "Request not made from matching IP (%s)",trans.ip());
+// return Result.err(Result.ERR_BadData,"Calling Machine does not match DNS lookup for %s",req.value.fqdns.get(0));
+ }
+
+ ArtiDAO.Data add = null;
+ Result<List<ArtiDAO.Data>> ra = artiDAO.read(trans, req.value.mechid,primary.getHostAddress());
+ if(ra.isOKhasData()) {
+ if(add==null) {
+ add = ra.value.get(0); // single key
+ }
+ } else {
+ ra = artiDAO.read(trans, req.value.mechid,key);
+ if(ra.isOKhasData()) { // is the Template available?
+ add = ra.value.get(0);
+ add.machine=primary.getHostName();
+ for(String s : fqdns) {
+ if(!s.equals(add.machine)) {
+ add.sans(true).add(s);
+ }
+ }
+ Result<ArtiDAO.Data> rc = artiDAO.create(trans, add); // Create new Artifact from Template
+ if(rc.notOK()) {
+ return Result.err(rc);
+ }
+ } else {
+ add = ra.value.get(0);
+ }
+ }
+
+ // Add Artifact listed FQDNs
+ if(add.sans!=null) {
+ for(String s : add.sans) {
+ if(!fqdns.contains(s)) {
+ fqdns.add(s);
+ }
+ }
+ }
+
+ // Policy 2: If Config marked as Expired, do not create or renew
+ Date now = new Date();
+ if(add.expires!=null && now.after(add.expires)) {
+ return Result.err(Result.ERR_Policy,"Configuration for %s %s is expired %s",add.mechid,add.machine,Chrono.dateFmt.format(add.expires));
+ }
+
+ // Policy 3: MechID must be current
+ Identity muser = org.getIdentity(trans, add.mechid);
+ if(muser == null) {
+ return Result.err(Result.ERR_Policy,"MechID must exist in %s",org.getName());
+ }
+
+ // Policy 4: Sponsor must be current
+ Identity ouser = muser.responsibleTo();
+ if(ouser==null) {
+ return Result.err(Result.ERR_Policy,"%s does not have a current sponsor at %s",add.mechid,org.getName());
+ } else if(!ouser.isFound() || ouser.mayOwn()!=null) {
+ return Result.err(Result.ERR_Policy,"%s reports that %s cannot be responsible for %s",org.getName(),trans.user());
+ }
+
+ // Set Email from most current Sponsor
+ email = ouser.email();
+
+ // Policy 5: keep Artifact data current
+ if(!ouser.fullID().equals(add.sponsor)) {
+ add.sponsor = ouser.fullID();
+ artiDAO.update(trans, add);
+ }
+
+ // Policy 7: Caller must be the MechID or have specifically delegated permissions
+ if(!(trans.user().equals(req.value.mechid) || trans.fish(new AAFPermission(mechNS + ".certman", ca.getName() , "request")))) {
+ return Result.err(Status.ERR_Denied, "%s must have access to modify x509 certs in NS %s",trans.user(),mechNS);
+ }
+
+ // Policy 8: SANs only allowed by Exception... need permission
+ // 7/25/2017 - SAN Permission no longer required. CSO
+// if(fqdns.size()>1 && !certman.aafLurPerm.fish(
+// new Principal() {
+// @Override
+// public String getName() {
+// return req.value.mechid;
+// }
+// },
+// new AAFPermission(ca.getPermType(), ca.getName(), SANS))) {
+// if(notes==null) {notes = new ArrayList<String>();}
+// notes.add("Warning: Subject Alternative Names only allowed by Permission: Get CSO Exception.");
+// return Result.err(Status.ERR_Denied, "%s must have a CSO Exception to work with SAN",trans.user());
+// }
+
+ // Make sure Primary is the first in fqdns
+ if(fqdns.size()>1) {
+ for(int i=0;i<fqdns.size();++i) {
+ if(fqdns.get(i).equals(primary.getHostName())) {
+ if(i!=0) {
+ String tmp = fqdns.get(0);
+ fqdns.set(0, primary.getHostName());
+ fqdns.set(i, tmp);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ trans.error().log(e);
+ return Result.err(Status.ERR_Denied,"MechID Sponsorship cannot be determined at this time. Try later");
+ }
+
+ CSRMeta csrMeta;
+ try {
+ csrMeta = BCFactory.createCSRMeta(
+ ca,
+ req.value.mechid,
+ email,
+ fqdns);
+ X509andChain x509ac = ca.sign(trans, csrMeta);
+ if(x509ac==null) {
+ return Result.err(Result.ERR_ActionNotCompleted,"x509 Certificate not signed by CA");
+ }
+ trans.info().printf("X509 Subject: %s", x509ac.getX509().getSubjectDN());
+// for(String s: x509ac.getTrustChain()) {
+// trans.warn().printf("Trust Cert: \n%s", s);
+// }
+
+ X509Certificate x509 = x509ac.getX509();
+ CertDAO.Data cdd = new CertDAO.Data();
+ cdd.ca=ca.getName();
+ cdd.serial=x509.getSerialNumber();
+ cdd.id=req.value.mechid;
+ cdd.x500=x509.getSubjectDN().getName();
+ cdd.x509=Factory.toString(trans, x509);
+ certDAO.create(trans, cdd);
+
+ CredDAO.Data crdd = new CredDAO.Data();
+ crdd.other = Question.random.nextInt();
+ crdd.cred=getChallenge256SaltedHash(csrMeta.challenge(),crdd.other);
+ crdd.expires = x509.getNotAfter();
+ crdd.id = req.value.mechid;
+ crdd.ns = Question.domain2ns(crdd.id);
+ crdd.type = CredDAO.CERT_SHA256_RSA;
+ credDAO.create(trans, crdd);
+
+ CertResp cr = new CertResp(trans, ca, x509, csrMeta, x509ac.getTrustChain(), ca.getTrustedCAs(), compileNotes(notes));
+ return Result.ok(cr);
+ } catch (Exception e) {
+ trans.error().log(e);
+ return Result.err(Result.ERR_ActionNotCompleted,e.getMessage());
+ }
+ } else {
+ return Result.err(req);
+ }
+ }
+
+ public Result<CertResp> renewCert(AuthzTrans trans, Result<CertRenew> renew) {
+ if(renew.isOK()) {
+ return Result.err(Result.ERR_NotImplemented,"Not implemented yet");
+ } else {
+ return Result.err(renew);
+ }
+ }
+
+ public Result<Void> dropCert(AuthzTrans trans, Result<CertDrop> drop) {
+ if(drop.isOK()) {
+ return Result.err(Result.ERR_NotImplemented,"Not implemented yet");
+ } else {
+ return Result.err(drop);
+ }
+ }
+
+ public Result<List<Data>> readCertsByMechID(AuthzTrans trans, String mechID) {
+ // Policy 1: To Read, must have NS Read or is Sponsor
+ String ns = Question.domain2ns(mechID);
+ try {
+ if( trans.user().equals(mechID)
+ || trans.fish(new AAFPermission(ns + ".access", "*", "read"))
+ || (trans.org().validate(trans,Organization.Policy.OWNS_MECHID,null,mechID))==null) {
+ return certDAO.readID(trans, mechID);
+ } else {
+ return Result.err(Result.ERR_Denied,"%s is not the ID, Sponsor or NS Owner/Admin for %s at %s",
+ trans.user(),mechID,trans.org().getName());
+ }
+ } catch(OrganizationException e) {
+ return Result.err(e);
+ }
+ }
+
+ public Result<CertResp> requestPersonalCert(AuthzTrans trans, CA ca) {
+ if(ca.inPersonalDomains(trans.getUserPrincipal())) {
+ Organization org = trans.org();
+
+ // Policy 1: MechID must be current
+ Identity ouser;
+ try {
+ ouser = org.getIdentity(trans, trans.user());
+ } catch (OrganizationException e1) {
+ trans.error().log(e1);
+ ouser = null;
+ }
+ if(ouser == null) {
+ return Result.err(Result.ERR_Policy,"Requesting User must exist in %s",org.getName());
+ }
+
+ // Set Email from most current Sponsor
+
+ CSRMeta csrMeta;
+ try {
+ csrMeta = BCFactory.createPersonalCSRMeta(
+ ca,
+ trans.user(),
+ ouser.email());
+ X509andChain x509ac = ca.sign(trans, csrMeta);
+ if(x509ac==null) {
+ return Result.err(Result.ERR_ActionNotCompleted,"x509 Certificate not signed by CA");
+ }
+ X509Certificate x509 = x509ac.getX509();
+ CertDAO.Data cdd = new CertDAO.Data();
+ cdd.ca=ca.getName();
+ cdd.serial=x509.getSerialNumber();
+ cdd.id=trans.user();
+ cdd.x500=x509.getSubjectDN().getName();
+ cdd.x509=Factory.toString(trans, x509);
+ certDAO.create(trans, cdd);
+
+ CertResp cr = new CertResp(trans, ca, x509, csrMeta, x509ac.getTrustChain(), ca.getTrustedCAs(), compileNotes(null));
+ return Result.ok(cr);
+ } catch (Exception e) {
+ trans.error().log(e);
+ return Result.err(Result.ERR_ActionNotCompleted,e.getMessage());
+ }
+ } else {
+ return Result.err(Result.ERR_Denied,trans.user()," not supported for CA",ca.getName());
+ }
+ }
+
+ ///////////////
+ // Artifact
+ //////////////
+ public Result<Void> createArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {
+ CertmanValidator v = new CertmanValidator().artisRequired(list, 1);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+ for(ArtiDAO.Data add : list) {
+ try {
+ // Policy 1: MechID must exist in Org
+ Identity muser = trans.org().getIdentity(trans, add.mechid);
+ if(muser == null) {
+ return Result.err(Result.ERR_Denied,"%s is not valid for %s", add.mechid,trans.org().getName());
+ }
+
+ // Policy 2: MechID must have valid Organization Owner
+ Identity ouser = muser.responsibleTo();
+ if(ouser == null) {
+ return Result.err(Result.ERR_Denied,"%s is not a valid Sponsor for %s at %s",
+ trans.user(),add.mechid,trans.org().getName());
+ }
+
+ // Policy 3: Calling ID must be MechID Owner
+ if(!trans.user().equals(ouser.fullID())) {
+ return Result.err(Result.ERR_Denied,"%s is not the Sponsor for %s at %s",
+ trans.user(),add.mechid,trans.org().getName());
+ }
+
+ // Policy 4: Renewal Days are between 10 and 60 (constants, may be parameterized)
+ if(add.renewDays<MIN_RENEWAL) {
+ add.renewDays = STD_RENEWAL;
+ } else if(add.renewDays>MAX_RENEWAL) {
+ add.renewDays = MAX_RENEWAL;
+ }
+
+ // Policy 5: If Notify is blank, set to Owner's Email
+ if(add.notify==null || add.notify.length()==0) {
+ add.notify = "mailto:"+ouser.email();
+ }
+
+ // Policy 6: Only do Domain by Exception
+ if(add.machine.startsWith("*")) { // Domain set
+ CA ca = certman.getCA(add.ca);
+
+
+ if(!trans.fish(new AAFPermission(ca.getPermType(), add.ca, DOMAIN))) {
+ return Result.err(Result.ERR_Denied,"Domain Artifacts (%s) requires specific Permission",
+ add.machine);
+ }
+ }
+
+ // Set Sponsor from Golden Source
+ add.sponsor = ouser.fullID();
+
+
+ } catch (OrganizationException e) {
+ return Result.err(e);
+ }
+ // Add to DB
+ Result<ArtiDAO.Data> rv = artiDAO.create(trans, add);
+ // TODO come up with Partial Reporting Scheme, or allow only one at a time.
+ if(rv.notOK()) {
+ return Result.err(rv);
+ }
+ }
+ return Result.ok();
+ }
+
+ public Result<List<ArtiDAO.Data>> readArtifacts(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {
+ CertmanValidator v = new CertmanValidator().keys(add);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+ Result<List<ArtiDAO.Data>> data = artiDAO.read(trans, add);
+ if(data.notOKorIsEmpty()) {
+ return data;
+ }
+ add = data.value.get(0);
+ if( trans.user().equals(add.mechid)
+ || trans.fish(new AAFPermission(add.ns + ".access", "*", "read"))
+ || trans.fish(new AAFPermission(add.ns+".certman",add.ca,"read"))
+ || trans.fish(new AAFPermission(add.ns+".certman",add.ca,"request"))
+ || (trans.org().validate(trans,Organization.Policy.OWNS_MECHID,null,add.mechid))==null) {
+ return data;
+ } else {
+ return Result.err(Result.ERR_Denied,"%s is not %s, is not the sponsor, and doesn't have delegated permission.",trans.user(),add.mechid,add.ns+".certman|"+add.ca+"|read or ...|request"); // note: reason is set by 2nd case, if 1st case misses
+ }
+
+ }
+
+ public Result<List<ArtiDAO.Data>> readArtifactsByMechID(AuthzTrans trans, String mechid) throws OrganizationException {
+ CertmanValidator v = new CertmanValidator();
+ v.nullOrBlank("mechid", mechid);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+ String ns = FQI.reverseDomain(mechid);
+
+ String reason;
+ if(trans.fish(new AAFPermission(ns + ".access", "*", "read"))
+ || (reason=trans.org().validate(trans,Organization.Policy.OWNS_MECHID,null,mechid))==null) {
+ return artiDAO.readByMechID(trans, mechid);
+ } else {
+ return Result.err(Result.ERR_Denied,reason); // note: reason is set by 2nd case, if 1st case misses
+ }
+
+ }
+
+ public Result<List<ArtiDAO.Data>> readArtifactsByMachine(AuthzTrans trans, String machine) {
+ CertmanValidator v = new CertmanValidator();
+ v.nullOrBlank("machine", machine);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+
+ // TODO do some checks?
+
+ Result<List<ArtiDAO.Data>> rv = artiDAO.readByMachine(trans, machine);
+ return rv;
+ }
+
+ public Result<List<ArtiDAO.Data>> readArtifactsByNs(AuthzTrans trans, String ns) {
+ CertmanValidator v = new CertmanValidator();
+ v.nullOrBlank("ns", ns);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+
+ // TODO do some checks?
+
+ Result<List<ArtiDAO.Data>> rv = artiDAO.readByNs(trans, ns);
+ return rv;
+ }
+
+
+ public Result<Void> updateArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) throws OrganizationException {
+ CertmanValidator v = new CertmanValidator();
+ v.artisRequired(list, 1);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+
+ // Check if requesting User is Sponsor
+ //TODO - Shall we do one, or multiples?
+ for(ArtiDAO.Data add : list) {
+ // Policy 1: MechID must exist in Org
+ Identity muser = trans.org().getIdentity(trans, add.mechid);
+ if(muser == null) {
+ return Result.err(Result.ERR_Denied,"%s is not valid for %s", add.mechid,trans.org().getName());
+ }
+
+ // Policy 2: MechID must have valid Organization Owner
+ Identity ouser = muser.responsibleTo();
+ if(ouser == null) {
+ return Result.err(Result.ERR_Denied,"%s is not a valid Sponsor for %s at %s",
+ trans.user(),add.mechid,trans.org().getName());
+ }
+
+ // Policy 3: Renewal Days are between 10 and 60 (constants, may be parameterized)
+ if(add.renewDays<MIN_RENEWAL) {
+ add.renewDays = STD_RENEWAL;
+ } else if(add.renewDays>MAX_RENEWAL) {
+ add.renewDays = MAX_RENEWAL;
+ }
+
+ // Policy 4: Data is always updated with the latest Sponsor
+ // Add to Sponsor, to make sure we are always up to date.
+ add.sponsor = ouser.fullID();
+
+ // Policy 5: If Notify is blank, set to Owner's Email
+ if(add.notify==null || add.notify.length()==0) {
+ add.notify = "mailto:"+ouser.email();
+ }
+ // Policy 6: Only do Domain by Exception
+ if(add.machine.startsWith("*")) { // Domain set
+ CA ca = certman.getCA(add.ca);
+ if(ca==null) {
+ return Result.err(Result.ERR_BadData, "CA is required in Artifact");
+ }
+ if(!trans.fish(new AAFPermission(ca.getPermType(), add.ca, DOMAIN))) {
+ return Result.err(Result.ERR_Denied,"Domain Artifacts (%s) requires specific Permission",
+ add.machine);
+ }
+ }
+
+ // Policy 7: only Owner may update info
+ if(trans.user().equals(add.sponsor)) {
+ return artiDAO.update(trans, add);
+ } else {
+ return Result.err(Result.ERR_Denied,"%s may not update info for %s",trans.user(),muser.fullID());
+ }
+ }
+ return Result.err(Result.ERR_BadData,"No Artifacts to update");
+ }
+
+ public Result<Void> deleteArtifact(AuthzTrans trans, String mechid, String machine) throws OrganizationException {
+ CertmanValidator v = new CertmanValidator();
+ v.nullOrBlank("mechid", mechid)
+ .nullOrBlank("machine", machine);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+
+ Result<List<ArtiDAO.Data>> rlad = artiDAO.read(trans, mechid, machine);
+ if(rlad.notOKorIsEmpty()) {
+ return Result.err(Result.ERR_NotFound,"Artifact for %s %s does not exist.",mechid,machine);
+ }
+
+ return deleteArtifact(trans,rlad.value.get(0));
+ }
+
+ private Result<Void> deleteArtifact(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {
+ // Policy 1: Record should be delete able only by Existing Sponsor.
+ String sponsor=null;
+ Identity muser = trans.org().getIdentity(trans, add.mechid);
+ if(muser != null) {
+ Identity ouser = muser.responsibleTo();
+ if(ouser!=null) {
+ sponsor = ouser.fullID();
+ }
+ }
+ // Policy 1.a: If Sponsorship is deleted in system of Record, then
+ // accept deletion by sponsor in Artifact Table
+ if(sponsor==null) {
+ sponsor = add.sponsor;
+ }
+
+ String ns = FQI.reverseDomain(add.mechid);
+
+ if(trans.fish(new AAFPermission(ns + ".access", "*", "write"))
+ || trans.user().equals(sponsor)) {
+ return artiDAO.delete(trans, add, false);
+ }
+ return null;
+ }
+
+ public Result<Void> deleteArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {
+ CertmanValidator v = new CertmanValidator().artisRequired(list, 1);
+ if(v.err()) {
+ return Result.err(Result.ERR_BadData,v.errs());
+ }
+
+ try {
+ boolean partial = false;
+ Result<Void> result=null;
+ for(ArtiDAO.Data add : list) {
+ result = deleteArtifact(trans, add);
+ if(result.notOK()) {
+ partial = true;
+ }
+ }
+ if(result == null) {
+ result = Result.err(Result.ERR_BadData,"No Artifacts to delete");
+ } else if(partial) {
+ result.partialContent(true);
+ }
+ return result;
+ } catch(Exception e) {
+ return Result.err(e);
+ }
+ }
+
+ private String[] compileNotes(List<String> notes) {
+ String[] rv;
+ if(notes==null) {
+ rv = NO_NOTES;
+ } else {
+ rv = new String[notes.size()];
+ notes.toArray(rv);
+ }
+ return rv;
+ }
+
+ private ByteBuffer getChallenge256SaltedHash(String challenge, int salt) throws NoSuchAlgorithmException {
+ ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + challenge.length());
+ bb.putInt(salt);
+ bb.put(challenge.getBytes());
+ byte[] hash = Hash.hashSHA256(bb.array());
+ return ByteBuffer.wrap(hash);
+ }
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/Code.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/Code.java
new file mode 100644
index 00000000..ce2ca065
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/service/Code.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.service;
+
+import org.onap.aaf.auth.cm.AAF_CM;
+import org.onap.aaf.auth.cm.facade.Facade1_0;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.rserv.HttpCode;
+
+public abstract class Code extends HttpCode<AuthzTrans,Facade1_0> implements Cloneable {
+
+ public Code(AAF_CM cma, String description, String ... roles) {
+ super(cma.facade1_0, description, roles);
+ // Note, the first "Code" will be created with default Facade, "JSON".
+ // use clone for another Code with XML
+ }
+
+
+ public <D extends Code> D clone(Facade1_0 facade) throws Exception {
+ @SuppressWarnings("unchecked")
+ D d = (D)clone();
+ d.context = facade;
+ return d;
+ }
+
+}
diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/validation/CertmanValidator.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/validation/CertmanValidator.java
new file mode 100644
index 00000000..d3ce0ace
--- /dev/null
+++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/validation/CertmanValidator.java
@@ -0,0 +1,121 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 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.aaf.auth.cm.validation;
+
+import java.util.List;
+
+import org.onap.aaf.auth.dao.cass.ArtiDAO;
+import org.onap.aaf.auth.dao.cass.ArtiDAO.Data;
+import org.onap.aaf.auth.validation.Validator;
+
+/**
+ * Validator
+ * Consistently apply content rules for content (incoming)
+ *
+ * Note: We restrict content for usability in URLs (because RESTful service), and avoid
+ * issues with Regular Expressions, and other enabling technologies.
+ * @author Jonathan
+ *
+ */
+public class CertmanValidator extends Validator{
+ // Repeated Msg fragments
+ private static final String MECHID = "mechid";
+ private static final String MACHINE = "machine";
+ private static final String ARTIFACT_LIST_IS_NULL = "Artifact List is null.";
+ private static final String Y = "y.";
+ private static final String IES = "ies.";
+ private static final String ENTR = " entr";
+ private static final String MUST_HAVE_AT_LEAST = " must have at least ";
+ private static final String IS_NULL = " is null.";
+ private static final String ARTIFACTS_MUST_HAVE_AT_LEAST = "Artifacts must have at least ";
+
+ public CertmanValidator nullBlankMin(String name, List<String> list, int min) {
+ if(list==null) {
+ msg(name + IS_NULL);
+ } else {
+ if(list.size()<min) {
+ msg(name + MUST_HAVE_AT_LEAST + min + ENTR + (min==1?Y:IES));
+ } else {
+ for(String s : list) {
+ nullOrBlank("List Item",s);
+ }
+ }
+ }
+ return this;
+ }
+
+ public CertmanValidator artisRequired(List<ArtiDAO.Data> list, int min) {
+ if(list==null) {
+ msg(ARTIFACT_LIST_IS_NULL);
+ } else {
+ if(list.size()<min) {
+ msg(ARTIFACTS_MUST_HAVE_AT_LEAST + min + ENTR + (min==1?Y:IES));
+ } else {
+ for(ArtiDAO.Data a : list) {
+ allRequired(a);
+ }
+ }
+ }
+ return this;
+ }
+
+ public CertmanValidator artisKeys(List<ArtiDAO.Data> list, int min) {
+ if(list==null) {
+ msg(ARTIFACT_LIST_IS_NULL);
+ } else {
+ if(list.size()<min) {
+ msg(ARTIFACTS_MUST_HAVE_AT_LEAST + min + ENTR + (min==1?Y:IES));
+ } else {
+ for(ArtiDAO.Data a : list) {
+ keys(a);
+ }
+ }
+ }
+ return this;
+ }
+
+
+ public CertmanValidator keys(ArtiDAO.Data add) {
+ if(add==null) {
+ msg("Artifact is null.");
+ } else {
+ nullOrBlank(MECHID, add.mechid);
+ nullOrBlank(MACHINE, add.machine);
+ }
+ return this;
+ }
+
+ private CertmanValidator allRequired(Data a) {
+ if(a==null) {
+ msg("Artifact is null.");
+ } else {
+ nullOrBlank(MECHID, a.mechid);
+ nullOrBlank(MACHINE, a.machine);
+ nullOrBlank("ca",a.ca);
+ nullOrBlank("dir",a.dir);
+ nullOrBlank("os_user",a.os_user);
+ // Note: AppName, Notify & Sponsor are currently not required
+ }
+ return this;
+ }
+
+}