AT&T 2.0.19 Code drop, stage 4
[aaf/authz.git] / authz-certman / src / main / java / org / onap / aaf / authz / cm / service / CMService.java
diff --git a/authz-certman/src/main/java/org/onap/aaf/authz/cm/service/CMService.java b/authz-certman/src/main/java/org/onap/aaf/authz/cm/service/CMService.java
deleted file mode 100644 (file)
index 9924973..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-/*******************************************************************************\r
- * ============LICENSE_START====================================================\r
- * * org.onap.aaf\r
- * * ===========================================================================\r
- * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
- * * ===========================================================================\r
- * * Licensed under the Apache License, Version 2.0 (the "License");\r
- * * you may not use this file except in compliance with the License.\r
- * * You may obtain a copy of the License at\r
- * * \r
- *  *      http://www.apache.org/licenses/LICENSE-2.0\r
- * * \r
- *  * Unless required by applicable law or agreed to in writing, software\r
- * * distributed under the License is distributed on an "AS IS" BASIS,\r
- * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * * See the License for the specific language governing permissions and\r
- * * limitations under the License.\r
- * * ============LICENSE_END====================================================\r
- * *\r
- * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
- * *\r
- ******************************************************************************/\r
-package org.onap.aaf.authz.cm.service;\r
-\r
-import java.io.IOException;\r
-import java.net.InetAddress;\r
-import java.net.UnknownHostException;\r
-import java.nio.ByteBuffer;\r
-import java.security.NoSuchAlgorithmException;\r
-import java.security.cert.X509Certificate;\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-import java.util.List;\r
-\r
-import org.onap.aaf.authz.cm.api.API_Cert;\r
-import org.onap.aaf.authz.cm.ca.CA;\r
-import org.onap.aaf.authz.cm.cert.BCFactory;\r
-import org.onap.aaf.authz.cm.cert.CSRMeta;\r
-import org.onap.aaf.authz.cm.data.CertDrop;\r
-import org.onap.aaf.authz.cm.data.CertRenew;\r
-import org.onap.aaf.authz.cm.data.CertReq;\r
-import org.onap.aaf.authz.cm.data.CertResp;\r
-import org.onap.aaf.authz.cm.validation.Validator;\r
-import org.onap.aaf.authz.env.AuthzTrans;\r
-import org.onap.aaf.authz.layer.Result;\r
-import org.onap.aaf.authz.org.Organization;\r
-import org.onap.aaf.authz.org.OrganizationException;\r
-import org.onap.aaf.authz.org.Organization.Identity;\r
-import org.onap.aaf.dao.CassAccess;\r
-import org.onap.aaf.dao.DAO;\r
-import org.onap.aaf.dao.aaf.cass.ArtiDAO;\r
-import org.onap.aaf.dao.aaf.cass.CacheInfoDAO;\r
-import org.onap.aaf.dao.aaf.cass.CertDAO;\r
-import org.onap.aaf.dao.aaf.cass.CredDAO;\r
-import org.onap.aaf.dao.aaf.cass.HistoryDAO;\r
-import org.onap.aaf.dao.aaf.cass.Status;\r
-import org.onap.aaf.dao.aaf.hl.Question;\r
-\r
-import org.onap.aaf.cadi.Hash;\r
-import org.onap.aaf.cadi.aaf.AAFPermission;\r
-import org.onap.aaf.cadi.aaf.v2_0.AAFCon;\r
-import org.onap.aaf.cadi.cm.Factory;\r
-import org.onap.aaf.inno.env.APIException;\r
-import org.onap.aaf.inno.env.Slot;\r
-import org.onap.aaf.inno.env.util.Chrono;\r
-import com.datastax.driver.core.Cluster;\r
-\r
-\r
-public class CMService {\r
-       // If we add more CAs, may want to parameterize\r
-       private static final int STD_RENEWAL = 30;\r
-       private static final int MAX_RENEWAL = 60;\r
-       private static final int MIN_RENEWAL = 10;\r
-       \r
-       public static final String REQUEST = "request";\r
-       public static final String RENEW = "renew";\r
-       public static final String DROP = "drop";\r
-       public static final String SANS = "san";\r
-       \r
-       private static final String[] NO_NOTES = new String[0];\r
-       private Slot sCertAuth;\r
-       private final CertDAO certDAO;\r
-       private final CredDAO credDAO;\r
-       private final ArtiDAO artiDAO;\r
-       private DAO<AuthzTrans, ?>[] daos;\r
-\r
-       @SuppressWarnings("unchecked")\r
-       public CMService(AuthzTrans trans, CertManAPI certman) throws APIException, IOException {\r
-\r
-               sCertAuth = certman.env.slot(API_Cert.CERT_AUTH);\r
-               Cluster cluster;\r
-               try {\r
-                       cluster = org.onap.aaf.dao.CassAccess.cluster(certman.env,null);\r
-               } catch (IOException e) {\r
-                       throw new APIException(e);\r
-               }\r
-\r
-               // jg 4/2015 SessionFilter unneeded... DataStax already deals with Multithreading well\r
-               \r
-               HistoryDAO hd = new HistoryDAO(trans,  cluster, CassAccess.KEYSPACE);\r
-               CacheInfoDAO cid = new CacheInfoDAO(trans, hd);\r
-               certDAO = new CertDAO(trans, hd, cid);\r
-               credDAO = new CredDAO(trans, hd, cid);\r
-               artiDAO = new ArtiDAO(trans, hd, cid);\r
-               \r
-               daos =(DAO<AuthzTrans, ?>[]) new DAO<?,?>[] {\r
-                               hd,cid,certDAO,credDAO,artiDAO\r
-               };\r
-\r
-               // Setup Shutdown Hooks for Cluster and Pooled Sessions\r
-               Runtime.getRuntime().addShutdownHook(new Thread() {\r
-                       @Override\r
-                       public void run() {\r
-                               for(DAO<AuthzTrans,?> dao : daos) {\r
-                                       dao.close(trans);\r
-                               }\r
-\r
-//                             sessionFilter.destroy();\r
-                               cluster.close();\r
-                       }\r
-               }); \r
-       }\r
-       \r
-       public Result<CertResp> requestCert(AuthzTrans trans,Result<CertReq> req) {\r
-               if(req.isOK()) {\r
-                       CA ca = trans.get(sCertAuth, null);\r
-                       if(ca==null) {\r
-                               return Result.err(Result.err(Result.ERR_BadData, "Invalid Cert Authority requested"));\r
-                       }\r
-\r
-                       // Allow only AAF CA without special permission\r
-                       if(!ca.getName().equals("aaf") && !trans.fish( new AAFPermission(ca.getPermType(), ca.getName(), REQUEST))) {\r
-                               return Result.err(Status.ERR_Denied, "'%s' does not have permission to request Certificates from Certificate Authority '%s'", \r
-                                               trans.user(),ca.getName());\r
-                       }\r
-\r
-                       List<String> notes = null;\r
-                       List<String> fqdns;\r
-                       String email = null;\r
-\r
-                       try {\r
-                               Organization org = trans.org();\r
-                               \r
-                               // Policy 1: Requests are only by Pre-Authorized Configurations\r
-                               ArtiDAO.Data add = null;\r
-                               try {\r
-                                       for(InetAddress ia : InetAddress.getAllByName(trans.ip())) {\r
-                                               Result<List<ArtiDAO.Data>> ra = artiDAO.read(trans, req.value.mechid,ia.getHostName());\r
-                                               if(ra.isOKhasData()) {\r
-                                                       add = ra.value.get(0);\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                               } catch (UnknownHostException e1) {\r
-                                       return Result.err(Result.ERR_BadData,"There is no host for %s",trans.ip());\r
-                               }\r
-                               \r
-                               if(add==null) {\r
-                                       return Result.err(Result.ERR_BadData,"There is no configuration for %s",req.value.mechid);\r
-                               }\r
-                               \r
-                               // Policy 2: If Config marked as Expired, do not create or renew\r
-                               Date now = new Date();\r
-                               if(add.expires!=null && now.after(add.expires)) {\r
-                                       return Result.err(Result.ERR_Policy,"Configuration for %s %s is expired %s",add.mechid,add.machine,Chrono.dateFmt.format(add.expires));\r
-                               }\r
-                               \r
-                               // Policy 3: MechID must be current\r
-                               Identity muser = org.getIdentity(trans, add.mechid);\r
-                               if(muser == null) {\r
-                                       return Result.err(Result.ERR_Policy,"MechID must exist in %s",org.getName());\r
-                               }\r
-                               \r
-                               // Policy 4: Sponsor must be current\r
-                               Identity ouser = muser.owner();\r
-                               if(ouser==null) {\r
-                                       return Result.err(Result.ERR_Policy,"%s does not have a current sponsor at %s",add.mechid,org.getName());\r
-                               } else if(!ouser.isFound() || !ouser.isResponsible()) {\r
-                                       return Result.err(Result.ERR_Policy,"%s reports that %s cannot be responsible for %s",org.getName(),trans.user());\r
-                               }\r
-                               \r
-                                       // Set Email from most current Sponsor\r
-                               email = ouser.email();\r
-                               \r
-                               // Policy 5: keep Artifact data current\r
-                               if(!ouser.fullID().equals(add.sponsor)) {\r
-                                       add.sponsor = ouser.fullID();\r
-                                       artiDAO.update(trans, add);\r
-                               }\r
-               \r
-                               // Policy 6: Requester must be granted Change permission in Namespace requested\r
-                               String mechNS = AAFCon.reverseDomain(req.value.mechid);\r
-                               if(mechNS==null) {\r
-                                       return Result.err(Status.ERR_Denied, "%s does not reflect a valid AAF Namespace",req.value.mechid);\r
-                               }\r
-                               \r
-                               // Policy 7: Caller must be the MechID or have specifically delegated permissions\r
-                               if(!trans.user().equals(req.value.mechid) && !trans.fish(new AAFPermission(mechNS + ".certman", ca.getName() , "request"))) {\r
-                                       return Result.err(Status.ERR_Denied, "%s must have access to modify x509 certs in NS %s",trans.user(),mechNS);\r
-                               }\r
-                               \r
-       \r
-                               // Policy 8: SANs only allowed by Exception... need permission\r
-                               fqdns = new ArrayList<String>();\r
-                               fqdns.add(add.machine);  // machine is first\r
-                               if(req.value.fqdns.size()>1 && !trans.fish(new AAFPermission(ca.getPermType(), ca.getName(), SANS))) {\r
-                                       if(notes==null) {notes = new ArrayList<String>();}\r
-                                       notes.add("Warning: Subject Alternative Names only allowed by Permission: Get CSO Exception.  This Certificate will be created, but without SANs");\r
-                               } else {\r
-                                       for(String m : req.value.fqdns) {\r
-                                               if(!add.machine.equals(m)) {\r
-                                                       fqdns.add(m);\r
-                                               }\r
-                                       }\r
-                               }\r
-                               \r
-                       } catch (Exception e) {\r
-                               trans.error().log(e);\r
-                               return Result.err(Status.ERR_Denied,"MechID Sponsorship cannot be determined at this time.  Try later");\r
-                       }\r
-                       \r
-                       CSRMeta csrMeta;\r
-                       try {\r
-                               csrMeta = BCFactory.createCSRMeta(\r
-                                               ca, \r
-                                               req.value.mechid, \r
-                                               email, \r
-                                               fqdns);\r
-                               X509Certificate x509 = ca.sign(trans, csrMeta);\r
-                               if(x509==null) {\r
-                                       return Result.err(Result.ERR_ActionNotCompleted,"x509 Certificate not signed by CA");\r
-                               }\r
-                               CertDAO.Data cdd = new CertDAO.Data();\r
-                               cdd.ca=ca.getName();\r
-                               cdd.serial=x509.getSerialNumber();\r
-                               cdd.id=req.value.mechid;\r
-                               cdd.x500=x509.getSubjectDN().getName();\r
-                               cdd.x509=Factory.toString(trans, x509);\r
-                               certDAO.create(trans, cdd);\r
-                               \r
-                               CredDAO.Data crdd = new CredDAO.Data();\r
-                               crdd.other = Question.random.nextInt();\r
-                               crdd.cred=getChallenge256SaltedHash(csrMeta.challenge(),crdd.other);\r
-                               crdd.expires = x509.getNotAfter();\r
-                               crdd.id = req.value.mechid;\r
-                               crdd.ns = Question.domain2ns(crdd.id);\r
-                               crdd.type = CredDAO.CERT_SHA256_RSA;\r
-                               credDAO.create(trans, crdd);\r
-                               \r
-                               CertResp cr = new CertResp(trans,x509,csrMeta, compileNotes(notes));\r
-                               return Result.ok(cr);\r
-                       } catch (Exception e) {\r
-                               trans.error().log(e);\r
-                               return Result.err(Result.ERR_ActionNotCompleted,e.getMessage());\r
-                       }\r
-               } else {\r
-                       return Result.err(req);\r
-               }\r
-       }\r
-\r
-    public Result<CertResp> renewCert(AuthzTrans trans, Result<CertRenew> renew) {\r
-               if(renew.isOK()) {\r
-                       return Result.err(Result.ERR_NotImplemented,"Not implemented yet");\r
-               } else {\r
-                       return Result.err(renew);\r
-               }       \r
-       }\r
-\r
-       public Result<Void> dropCert(AuthzTrans trans, Result<CertDrop> drop) {\r
-               if(drop.isOK()) {\r
-                       return Result.err(Result.ERR_NotImplemented,"Not implemented yet");\r
-               } else {\r
-                       return Result.err(drop);\r
-               }       \r
-       }\r
-\r
-       ///////////////\r
-       // Artifact\r
-       //////////////\r
-       public Result<Void> createArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {\r
-               Validator v = new Validator().artisRequired(list, 1);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-               for(ArtiDAO.Data add : list) {\r
-                       try {\r
-                               // Policy 1: MechID must exist in Org\r
-                               Identity muser = trans.org().getIdentity(trans, add.mechid);\r
-                               if(muser == null) {\r
-                                       return Result.err(Result.ERR_Denied,"%s is not valid for %s", add.mechid,trans.org().getName());\r
-                               }\r
-                               \r
-                               // Policy 2: MechID must have valid Organization Owner\r
-                               Identity ouser = muser.owner();\r
-                               if(ouser == null) {\r
-                                       return Result.err(Result.ERR_Denied,"%s is not a valid Sponsor for %s at %s",\r
-                                                       trans.user(),add.mechid,trans.org().getName());\r
-                               }\r
-                               \r
-                               // Policy 3: Calling ID must be MechID Owner\r
-                               if(!trans.user().equals(ouser.fullID())) {\r
-                                       return Result.err(Result.ERR_Denied,"%s is not the Sponsor for %s at %s",\r
-                                                       trans.user(),add.mechid,trans.org().getName());\r
-                               }\r
-\r
-                               // Policy 4: Renewal Days are between 10 and 60 (constants, may be parameterized)\r
-                               if(add.renewDays<MIN_RENEWAL) {\r
-                                       add.renewDays = STD_RENEWAL;\r
-                               } else if(add.renewDays>MAX_RENEWAL) {\r
-                                       add.renewDays = MAX_RENEWAL;\r
-                               }\r
-                               \r
-                               // Policy 5: If Notify is blank, set to Owner's Email\r
-                               if(add.notify==null || add.notify.length()==0) {\r
-                                       add.notify = "mailto:"+ouser.email();\r
-                               }\r
-\r
-                               // Set Sponsor from Golden Source\r
-                               add.sponsor = ouser.fullID();\r
-                               \r
-                               \r
-                       } catch (OrganizationException e) {\r
-                               return Result.err(e);\r
-                       }\r
-                       // Add to DB\r
-                       Result<ArtiDAO.Data> rv = artiDAO.create(trans, add);\r
-                       // TODO come up with Partial Reporting Scheme, or allow only one at a time.\r
-                       if(rv.notOK()) {\r
-                               return Result.err(rv);\r
-                       }\r
-               }\r
-               return Result.ok();\r
-       }\r
-\r
-       public Result<List<ArtiDAO.Data>> readArtifacts(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {\r
-               Validator v = new Validator().keys(add);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-               String ns = AAFCon.reverseDomain(add.mechid);\r
-               \r
-               if( trans.user().equals(add.mechid)\r
-                       || trans.fish(new AAFPermission(ns + ".access", "*", "read"))\r
-                       || (trans.org().validate(trans,Organization.Policy.OWNS_MECHID,null,add.mechid))==null) {\r
-                               return artiDAO.read(trans, add);\r
-               } else {\r
-                       return Result.err(Result.ERR_Denied,"%s is not %s, is not the sponsor, and doesn't have delegated permission.",trans.user(),add.mechid); // note: reason is set by 2nd case, if 1st case misses\r
-               }\r
-\r
-       }\r
-\r
-       public Result<List<ArtiDAO.Data>> readArtifactsByMechID(AuthzTrans trans, String mechid) throws OrganizationException {\r
-               Validator v = new Validator().nullOrBlank("mechid", mechid);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-               String ns = AAFCon.reverseDomain(mechid);\r
-               \r
-               String reason;\r
-               if(trans.fish(new AAFPermission(ns + ".access", "*", "read"))\r
-                       || (reason=trans.org().validate(trans,Organization.Policy.OWNS_MECHID,null,mechid))==null) {\r
-                       return artiDAO.readByMechID(trans, mechid);\r
-               } else {\r
-                       return Result.err(Result.ERR_Denied,reason); // note: reason is set by 2nd case, if 1st case misses\r
-               }\r
-\r
-       }\r
-\r
-       public Result<List<ArtiDAO.Data>> readArtifactsByMachine(AuthzTrans trans, String machine) {\r
-               Validator v = new Validator().nullOrBlank("machine", machine);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-               \r
-               // TODO do some checks?\r
-\r
-               Result<List<ArtiDAO.Data>> rv = artiDAO.readByMachine(trans, machine);\r
-               return rv;\r
-       }\r
-\r
-       public Result<Void> updateArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) throws OrganizationException {\r
-               Validator v = new Validator().artisRequired(list, 1);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-               \r
-               // Check if requesting User is Sponsor\r
-               //TODO - Shall we do one, or multiples?\r
-               for(ArtiDAO.Data add : list) {\r
-                       // Policy 1: MechID must exist in Org\r
-                       Identity muser = trans.org().getIdentity(trans, add.mechid);\r
-                       if(muser == null) {\r
-                               return Result.err(Result.ERR_Denied,"%s is not valid for %s", add.mechid,trans.org().getName());\r
-                       }\r
-                       \r
-                       // Policy 2: MechID must have valid Organization Owner\r
-                       Identity ouser = muser.owner();\r
-                       if(ouser == null) {\r
-                               return Result.err(Result.ERR_Denied,"%s is not a valid Sponsor for %s at %s",\r
-                                               trans.user(),add.mechid,trans.org().getName());\r
-                       }\r
-\r
-                       // Policy 3: Renewal Days are between 10 and 60 (constants, may be parameterized)\r
-                       if(add.renewDays<MIN_RENEWAL) {\r
-                               add.renewDays = STD_RENEWAL;\r
-                       } else if(add.renewDays>MAX_RENEWAL) {\r
-                               add.renewDays = MAX_RENEWAL;\r
-                       }\r
-\r
-                       // Policy 4: Data is always updated with the latest Sponsor\r
-                       // Add to Sponsor, to make sure we are always up to date.\r
-                       add.sponsor = ouser.fullID();\r
-\r
-                       // Policy 5: If Notify is blank, set to Owner's Email\r
-                       if(add.notify==null || add.notify.length()==0) {\r
-                               add.notify = "mailto:"+ouser.email();\r
-                       }\r
-\r
-                       // Policy 4: only Owner may update info\r
-                       if(trans.user().equals(add.sponsor)) {\r
-                               return artiDAO.update(trans, add);\r
-                       } else {\r
-                               return Result.err(Result.ERR_Denied,"%s may not update info for %s",trans.user(),muser.fullID());\r
-                       }\r
-                       \r
-               }\r
-               return Result.err(Result.ERR_BadData,"No Artifacts to update");\r
-       }\r
-       \r
-       public Result<Void> deleteArtifact(AuthzTrans trans, String mechid, String machine) throws OrganizationException {\r
-               Validator v = new Validator()\r
-                               .nullOrBlank("mechid", mechid)\r
-                               .nullOrBlank("machine", machine);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-\r
-               Result<List<ArtiDAO.Data>> rlad = artiDAO.read(trans, mechid, machine);\r
-               if(rlad.notOKorIsEmpty()) {\r
-                       return Result.err(Result.ERR_NotFound,"Artifact for %s %s does not exist.",mechid,machine);\r
-               }\r
-               \r
-               return deleteArtifact(trans,rlad.value.get(0));\r
-       }\r
-               \r
-       private Result<Void> deleteArtifact(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {\r
-               // Policy 1: Record should be delete able only by Existing Sponsor.  \r
-               String sponsor=null;\r
-               Identity muser = trans.org().getIdentity(trans, add.mechid);\r
-               if(muser != null) {\r
-                       Identity ouser = muser.owner();\r
-                       if(ouser!=null) {\r
-                               sponsor = ouser.fullID();\r
-                       }\r
-               }\r
-               // Policy 1.a: If Sponsorship is deleted in system of Record, then \r
-               // accept deletion by sponsor in Artifact Table\r
-               if(sponsor==null) {\r
-                       sponsor = add.sponsor;\r
-               }\r
-               \r
-               String ns = AAFCon.reverseDomain(add.mechid);\r
-\r
-               if(trans.fish(new AAFPermission(ns + ".access", "*", "write"))\r
-                               || trans.user().equals(sponsor)) {\r
-                       return artiDAO.delete(trans, add, false);\r
-               }\r
-               return null;\r
-       }\r
-\r
-       public Result<Void> deleteArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {\r
-               Validator v = new Validator().artisRequired(list, 1);\r
-               if(v.err()) {\r
-                       return Result.err(Result.ERR_BadData,v.errs());\r
-               }\r
-\r
-               try {\r
-                       boolean partial = false;\r
-                       Result<Void> result=null;\r
-                       for(ArtiDAO.Data add : list) {\r
-                               result = deleteArtifact(trans, add);\r
-                               if(result.notOK()) {\r
-                                       partial = true;\r
-                               }\r
-                       }\r
-                       if(result == null) {\r
-                               result = Result.err(Result.ERR_BadData,"No Artifacts to delete"); \r
-                       } else if(partial) {\r
-                               result.partialContent(true);\r
-                       }\r
-                       return result;\r
-               } catch(Exception e) {\r
-                       return Result.err(e);\r
-               }\r
-       }\r
-\r
-       private String[] compileNotes(List<String> notes) {\r
-               String[] rv;\r
-               if(notes==null) {\r
-                       rv = NO_NOTES;\r
-               } else {\r
-                       rv = new String[notes.size()];\r
-                       notes.toArray(rv);\r
-               }\r
-               return rv;\r
-       }\r
-\r
-       private ByteBuffer getChallenge256SaltedHash(String challenge, int salt) throws NoSuchAlgorithmException {\r
-               ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + challenge.length());\r
-               bb.putInt(salt);\r
-               bb.put(challenge.getBytes());\r
-               byte[] hash = Hash.hashSHA256(bb.array());\r
-               return ByteBuffer.wrap(hash);\r
-       }\r
-}\r