From 4a51a8f96715ffb2a42189b93b9fa91b453b8530 Mon Sep 17 00:00:00 2001 From: sg481n Date: Thu, 3 Aug 2017 17:39:12 -0400 Subject:  [AAF-21] Initial code import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ia1dd196befd061f6ba0c2be6bf4456a30ea50f97 Signed-off-by: sg481n --- aaf/src/main/java/Examples.java | 42 ++ .../main/java/com/att/cadi/aaf/AAFPermission.java | 106 +++ .../main/java/com/att/cadi/aaf/AAFTransmutate.java | 88 +++ .../java/com/att/cadi/aaf/ConnectivityTest.java | 459 +++++++++++++ aaf/src/main/java/com/att/cadi/aaf/PermEval.java | 150 +++++ .../att/cadi/aaf/cert/AAFListedCertIdentity.java | 179 ++++++ .../java/com/att/cadi/aaf/client/ErrMessage.java | 98 +++ .../java/com/att/cadi/aaf/client/Examples.java | 445 +++++++++++++ .../java/com/att/cadi/aaf/marshal/CertMarshal.java | 67 ++ .../com/att/cadi/aaf/marshal/CertsMarshal.java | 46 ++ .../main/java/com/att/cadi/aaf/v2_0/AAFAuthn.java | 207 ++++++ .../main/java/com/att/cadi/aaf/v2_0/AAFCon.java | 396 ++++++++++++ .../java/com/att/cadi/aaf/v2_0/AAFConDME2.java | 224 +++++++ .../java/com/att/cadi/aaf/v2_0/AAFConHttp.java | 187 ++++++ .../java/com/att/cadi/aaf/v2_0/AAFLurPerm.java | 221 +++++++ .../main/java/com/att/cadi/aaf/v2_0/AAFTaf.java | 168 +++++ .../com/att/cadi/aaf/v2_0/AAFTrustChecker.java | 116 ++++ .../main/java/com/att/cadi/aaf/v2_0/AbsAAFLur.java | 269 ++++++++ aaf/src/main/java/com/att/cadi/cm/ArtifactDir.java | 288 +++++++++ .../main/java/com/att/cadi/cm/CertException.java | 47 ++ aaf/src/main/java/com/att/cadi/cm/CmAgent.java | 711 +++++++++++++++++++++ aaf/src/main/java/com/att/cadi/cm/Factory.java | 449 +++++++++++++ .../main/java/com/att/cadi/cm/PlaceArtifact.java | 34 + .../java/com/att/cadi/cm/PlaceArtifactInFiles.java | 54 ++ .../com/att/cadi/cm/PlaceArtifactInKeystore.java | 130 ++++ .../com/att/cadi/cm/PlaceArtifactOnStream.java | 53 ++ .../java/com/att/cadi/cm/PlaceArtifactScripts.java | 139 ++++ aaf/src/main/java/com/att/cadi/sso/AAFSSO.java | 286 +++++++++ 28 files changed, 5659 insertions(+) create mode 100644 aaf/src/main/java/Examples.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/AAFPermission.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/AAFTransmutate.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/ConnectivityTest.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/PermEval.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/cert/AAFListedCertIdentity.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/client/ErrMessage.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/client/Examples.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/marshal/CertMarshal.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/marshal/CertsMarshal.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFAuthn.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFCon.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConDME2.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConHttp.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFLurPerm.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTaf.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTrustChecker.java create mode 100644 aaf/src/main/java/com/att/cadi/aaf/v2_0/AbsAAFLur.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/ArtifactDir.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/CertException.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/CmAgent.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/Factory.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/PlaceArtifact.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInFiles.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInKeystore.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/PlaceArtifactOnStream.java create mode 100644 aaf/src/main/java/com/att/cadi/cm/PlaceArtifactScripts.java create mode 100644 aaf/src/main/java/com/att/cadi/sso/AAFSSO.java (limited to 'aaf/src/main') diff --git a/aaf/src/main/java/Examples.java b/aaf/src/main/java/Examples.java new file mode 100644 index 0000000..91f313f --- /dev/null +++ b/aaf/src/main/java/Examples.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +import com.att.rosetta.env.RosettaEnv; + +public class Examples { + public static void main(String[] args) { + if(args.length<1) { + System.out.println("Usage: Examples [\"optional\" - will show optional fields]"); + } else { + boolean options = args.length>1&&"optional".equals(args[1]); + try { + RosettaEnv env = new RosettaEnv(); + System.out.println(com.att.cadi.aaf.client.Examples.print(env, args[0], options)); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + } + + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/AAFPermission.java b/aaf/src/main/java/com/att/cadi/aaf/AAFPermission.java new file mode 100644 index 0000000..e604053 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/AAFPermission.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf; + +import com.att.cadi.Permission; + +/** + * A Class that understands the AAF format of Permission (name/type/action) + * or String "name|type|action" + * + * + */ +public class AAFPermission implements Permission { + protected String type,instance,action,key; + + protected AAFPermission() {} + + public AAFPermission(String type, String instance, String action) { + this.type = type; + this.instance = instance; + this.action = action; + key = type + '|' + instance + '|' + action; + } + + /** + * Match a Permission + * if Permission is Fielded type "Permission", we use the fields + * otherwise, we split the Permission with '|' + * + * when the type or action starts with REGEX indicator character ( ! ), + * then it is evaluated as a regular expression. + * + * If you want a simple field comparison, it is faster without REGEX + */ + public boolean match(Permission p) { + if(p instanceof AAFPermission) { + AAFPermission ap = (AAFPermission)p; + // Note: In AAF > 1.0, Accepting "*" from name would violate multi-tenancy + // Current solution is only allow direct match on Type. + // 8/28/2014 - added REGEX ability + if(type.equals(ap.getName())) + if(PermEval.evalInstance(instance,ap.getInstance())) + if(PermEval.evalAction(action,ap.getAction())) + return true; + } else { + // Permission is concatenated together: separated by | + String[] aaf = p.getKey().split("[\\s]*\\|[\\s]*",3); + if(aaf.length>0 && type.equals(aaf[0])) + if(PermEval.evalInstance(instance,aaf.length>1?aaf[1]:"*")) + if(PermEval.evalAction(action,aaf.length>2?aaf[2]:"*")) + return true; + } + return false; + } + + public String getName() { + return type; + } + + public String getInstance() { + return instance; + } + + public String getAction() { + return action; + } + + public String getKey() { + return key; + } + + /* (non-Javadoc) + * @see com.att.cadi.Permission#permType() + */ + public String permType() { + return "AAF"; + } + + public String toString() { + return "AAFPermission:\n\tType: " + type + + "\n\tInstance: " + instance + + "\n\tAction: " + action + + "\n\tKey: " + key; + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/AAFTransmutate.java b/aaf/src/main/java/com/att/cadi/aaf/AAFTransmutate.java new file mode 100644 index 0000000..a6a9bb7 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/AAFTransmutate.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf; + +import java.security.Principal; +import java.util.regex.Pattern; + +import com.att.cadi.Transmutate; +import com.att.cadi.lur.ConfigPrincipal; +import com.att.cadi.principal.BasicPrincipal; +import com.att.cadi.principal.CSPPrincipal_T; + +/** + * AAFTransmutate + * + * Each System determines the mechanisms for which one Principal is transmutated to another, such as whether it is created + * independently, etc. + * + * For AAF, the only important thing is that these are valid ATTUID/mechIDs, to avoid unnecessary user hits + * + * attUIDs look like ab1234 or AB1234 or AZ123a + * mechids look like m12345 + * + * + */ +public final class AAFTransmutate implements Transmutate { + private Pattern pattern = Pattern.compile("[a-zA-Z]\\w\\d\\d\\d\\w"); + + public Principal mutate(Principal p) { + // Accept these three internal kinds of Principals + if(p instanceof CSPPrincipal_T + || p instanceof BasicPrincipal + || p instanceof ConfigPrincipal) { + return p; + } else { + + final String name = p.getName(); + final int idx = name.indexOf('@'); + String shortName; + if(idx>0) { // strip off any domain + shortName = name.substring(0,idx); + } else { + shortName = name; + } + + // Check for ATTUID specs before creating CSP_T + return pattern.matcher(shortName).matches()? + new CSP_T(name): // Note: use REAL name, short name for CSP_T + null; + } + } + + /** + * Essential Principal reflecting CSP Principal + * + * + */ + private final class CSP_T implements CSPPrincipal_T { + private String name; + public CSP_T(String name) { + this.name = name; + } + public String getName() { + return name; + } + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/ConnectivityTest.java b/aaf/src/main/java/com/att/cadi/aaf/ConnectivityTest.java new file mode 100644 index 0000000..070d48d --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/ConnectivityTest.java @@ -0,0 +1,459 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.Date; + +import com.att.aft.dme2.api.DME2Client; +import com.att.aft.dme2.api.DME2Manager; +import com.att.cadi.CadiException; +import com.att.cadi.Locator; +import com.att.cadi.Locator.Item; +import com.att.cadi.LocatorException; +import com.att.cadi.Lur; +import com.att.cadi.PropAccess; +import com.att.cadi.SecuritySetter; +import com.att.cadi.TrustChecker; +import com.att.cadi.aaf.v2_0.AAFCon; +import com.att.cadi.aaf.v2_0.AAFConDME2; +import com.att.cadi.client.Future; +import com.att.cadi.config.Config; +import com.att.cadi.config.SecurityInfoC; +import com.att.cadi.http.HBasicAuthSS; +import com.att.cadi.http.HClient; +import com.att.cadi.http.HX509SS; +import com.att.cadi.locator.DME2Locator; +import com.att.cadi.locator.PropertyLocator; +import com.att.inno.env.APIException; +import com.att.rosetta.env.RosettaDF; +import com.att.rosetta.env.RosettaEnv; + +import aaf.v2_0.Perms; + +public class ConnectivityTest { + private static final String PROD = "PROD"; + private static final String SRV_RESOLVE = "https://DME2RESOLVE/service=com.att.authz.AuthorizationService/version=2.0/envContext=%s/routeOffer=%s"; + private static final String GW_RESOLVE = "https://DME2RESOLVE/service=com.att.authz.authz-gw/version=2.0/envContext=%s/routeOffer=%s"; + + public static void main(String[] args) { + if(args.length<2) { + System.out.println("Usage: ConnectivityTester "); + } else { + print(true,"START OF CONNECTIVITY TESTS",new Date().toString(),System.getProperty("user.name"), + "Note: All API Calls are /authz/perms/user/"); + + final String aaf_env = args[0]; + args[1]=Config.CADI_PROP_FILES+'='+args[1]; + + PropAccess pa = new PropAccess(args); + String user = pa.getProperty(Config.AAF_MECHID); + String pass = pa.getProperty(Config.AAF_MECHPASS); + String alias = pa.getProperty(Config.CADI_ALIAS); + if(user==null) { + user=alias; + } + RosettaEnv env = new RosettaEnv(pa.getProperties()); + + try { + RosettaDF permsDF = env.newDataFactory(Perms.class); + SecurityInfoC si = new SecurityInfoC(pa); + HBasicAuthSS hbass = new HBasicAuthSS(pa,si); + if(hbass.getID()==null) { + hbass=null; // not configured with ID. + } + HX509SS hxss=null; + AAFCon aafcon; + + try { + hxss = new HX509SS(user,si); + } catch(Exception e) { + e.printStackTrace(); + print(false,"Continuing"); + } + String aafurl; + if(user==null || (pass==null && alias==null)) { + System.out.printf("ERROR: DME2 Client cannot be tested with out %s and %s properties" + , Config.AAF_MECHID, Config.AAF_MECHPASS ); + } else { + if("TEST".equals(aaf_env) || "IST".equals(aaf_env) || "PROD".equals(aaf_env)) { + DME2Manager dm = null; + print(false,"Attempt DME2Manager Load"); + if(Class.forName("com.att.aft.dme2.api.DME2Manager")==null) { + print(true,"DME2 jar is not available: Skipping DME2 Tests"); + } else { // DME2 Client Tests + pass=pa.decrypt(pass,false); + // Out of the box DME2 + aafurl = String.format(SRV_RESOLVE, aaf_env, PROD.equals(aaf_env)?"DEFAULT":"BAU_SE"); + print(true,"TEST CADI Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + print(true,"Find and TEST Connections with DME2Locator",aafurl); + DME2Locator dl = new DME2Locator(pa,dm,aafurl); + connectTest(dl); + + dm = new DME2Manager("DME2Manager",pa.getProperties()); + + dme2RawTest(dm, aafurl,user,pass); + + // URL specific Variant + if((aafurl = specificDME2URL(dl, aafurl))!=null) { + print(true,"TEST Specific DME2 CADI Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + dme2RawTest(dm,aafurl,user,pass); + } + + print(true,"CADI Direct AAFConDME2 Object Usage",aafurl); + try { + pa.setProperty(Config.AAF_URL,aafurl); + aafcon = new AAFConDME2(pa); + test(aafcon,permsDF,user); + } catch(Throwable t) { + t.printStackTrace(); + } + + // find a direct client to code a Direct HTTP with + // + if(hbass!=null) { + print(true,"CADI Http DME2Locator Client Coding Methodology BasicAuth",aafurl); + hClientTest(dl,hbass,user); + } + if(hxss!=null) { + print(true,"CADI Http DME2Locator Client Coding Methodology X509",aafurl); + hClientTest(dl,hxss,user); + } + + // ##### PROXY CHECKS + aafurl = String.format(GW_RESOLVE, aaf_env, PROD.equals(aaf_env)?"DEFAULT":"BAU_SE"); + print(true,"TEST PROXY DME2 CADI Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + + dme2RawTest(dm, aafurl,user,pass); + + // URL specific Variant + dl = new DME2Locator(pa,dm,aafurl); + if((aafurl = specificDME2URL(dl, aafurl))!=null) { + print(true,"TEST PROXY Specific DME2 CADI Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + dme2RawTest(dm,aafurl,user,pass); + } + } + } + + // Prop Locator + PropertyLocator pl = servicePropLocator(aaf_env); + connectTest(pl); + URI uri = pl.get(pl.best()); + if(uri!=null) { + aafurl = uri.toString(); + print(true,"TEST Service PropertyLocator based Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + if(hbass!=null) { + print(true,"CADI Service Http PropLocator Client Coding Methodology Basic Auth",aafurl); + hClientTest(pl,hbass, user); + print(true,"CADI Service Http PropLocator Client Coding Methodology /authn/basicAuth",aafurl); + basicAuthTest(pl,hbass); + } + if(hxss!=null) { + print(true,"CADI Service Http PropLocator Client Coding Methodology X509",aafurl); + hClientTest(pl,hxss, user); + } + } + pl = proxyPropLocator(aaf_env); + connectTest(pl); + uri = pl.get(pl.best()); + if(uri!=null) { + aafurl = uri.toString(); + print(true,"TEST PROXY PropertyLocator based Config",aafurl); + aafcon = testConfig(pa,aafurl); + test(aafcon,permsDF,user); + + if(hbass!=null) { + print(true,"CADI PROXY Http PropLocator Client Coding Methodology Basic Auth",aafurl); + hClientTest(pl,hbass, user); + print(true,"CADI PROXY Http PropLocator Client Coding Methodology /proxy/authn/basicAuth",aafurl); + basicAuthTest(pl,hbass); + } + if(hxss!=null) { + print(true,"CADI PROXY Http PropLocator Client Coding Methodology X509",aafurl); + hClientTest(pl,hxss, user); + } + } + } + + } catch(Exception e) { + e.printStackTrace(System.err); + } finally { + print(true,"END OF TESTS"); + } + } + } + + private static void print(Boolean strong, String ... args) { + PrintStream out = System.out; + out.println(); + if(strong) { + for(int i=0;i<70;++i) { + out.print('='); + } + out.println(); + } + for(String s : args) { + out.print(strong?"== ":"------ "); + out.print(s); + if(!strong) { + out.print(" ------"); + } + out.println(); + } + if(strong) { + for(int i=0;i<70;++i) { + out.print('='); + } + } + out.println(); + } + + private static void test(AAFCon aafcon,RosettaDF permsDF,String user) { + if(aafcon==null) { + print(false,"AAFCon is null"); + } else { + try { + print(false,"Calling with AAFCon"); + Future fp = aafcon.client("2.0").read("/authz/perms/user/"+user, Perms.class, permsDF); + if(fp.get(4000)) { + System.out.printf("Found %d Permission(s)\n",fp.value.getPerm().size()); + } else { + System.out.printf("Error: %d %s\n",fp.code(),fp.body()); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + + private static AAFCon testConfig(PropAccess pa, String aafurl) { + try { + pa.setProperty(Config.AAF_URL, aafurl); + Lur lur = Config.configLur(pa); + Config.configHttpTaf(pa, TrustChecker.NOTRUST, null, lur); + if(lur != null) { + Field f = null; + try { + f = lur.getClass().getField("aaf"); + return (AAFCon)f.get(lur); + } catch (Exception nsfe) { + } + } + + } catch(Throwable t) { + t.printStackTrace(); + } + return null; + } + + private static String specificDME2URL(Locator loc, String aafurl) throws LocatorException { + Item item = loc.best(); + if(item!=null) { + URI uri = loc.get(item); + return aafurl.replace("DME2RESOLVE", String.format("%s:%d",uri.getHost(),uri.getPort())); + } + return null; + } + + private static void connectTest(Locator dl) throws LocatorException { + URI uri; + Socket socket; + print(false,"TCP/IP Connect test to all Located Services"); + for(Item li = dl.first();li!=null;li=dl.next(li)) { + if((uri = dl.get(li)) == null) { + System.out.println("Locator Item empty"); + } else { + try { + socket = new Socket(); + socket.connect(new InetSocketAddress(uri.getHost(), uri.getPort()),3000); + System.out.printf("Can Connect a Socket to %s %d\n",uri.getHost(),uri.getPort()); + try { + socket.close(); + } catch (IOException e1) { + System.out.printf("Could not close Socket Connection: %s\n",e1.getMessage()); + } + } catch (IOException e) { + System.out.printf("Cannot Connect a Socket to %s %d: %s\n",uri.getHost(),uri.getPort(),e.getMessage()); + } + } + } + } + + private static PropertyLocator servicePropLocator(String env) throws LocatorException { + String purls; + switch(env) { + case "LOCAL": + try { + purls="https://"+InetAddress.getLocalHost().getHostName()+":8100"; + } catch (UnknownHostException e) { + throw new LocatorException(e); + } + break; + case "DEV": + purls="https://aaf.dev.att.com:8100,https://aaf.dev.att.com:8101"; + break; + case "TEST": + purls="https://aaftest.test.att.com:8100,https://aaftest.test.att.com:8101"; + break; + case "IST": + purls="https://aafist.test.att.com:8100,https://aafist.test.att.com:8101"; + break; + case PROD: + purls="https://aaf.it.att.com:8100,https://aaf.it.att.com:8101"; + break; + default: + if(env.contains(".")) { + purls="https://"+env+":8100"; + } else { + throw new LocatorException(ConnectivityTest.class.getSimpleName() + ": unknown Env"); + } + } + System.out.printf("Creating a PropertyLocator for %s\n",purls); + return new PropertyLocator(purls); + } + + private static PropertyLocator proxyPropLocator(String env) throws LocatorException { + String purls; + switch(env) { + case "LOCAL": + try { + purls="https://"+InetAddress.getLocalHost().getHostAddress()+":8100"; + } catch (UnknownHostException e) { + throw new LocatorException(e); + } + break; + case "DEV": + purls="https://aaf.dev.att.com:8095/proxy"; + break; + case "TEST": + purls="https://aaftest.test.att.com:8095/proxy"; + break; + case "IST": + purls="https://aafist.test.att.com:8095/proxy"; + break; + case PROD: + purls="https://aaf.it.att.com:8095/proxy"; + break; + default: + if(env.contains(".")) { + purls="https://"+env+":8095/proxy"; + } else { + throw new LocatorException(ConnectivityTest.class.getSimpleName() + ": unknown Env"); + } + + } + System.out.printf("Creating a PropertyLocator for %s\n",purls); + return new PropertyLocator(purls); + } + + + + + private static void hClientTest(Locator dl, SecuritySetter ss, String user) { + try { + URI uri = dl.get(dl.best()); + System.out.println("Resolved to: " + uri); + HClient client = new HClient(ss, uri, 3000); + client.setMethod("GET"); + client.setPathInfo("/authz/perms/user/"+user); + client.send(); + Future future = client.futureReadString(); + if(future.get(7000)) { + System.out.println(future.body()); + } else { + System.out.println(future.code() + ":" + future.body()); + } + } catch (CadiException | LocatorException | APIException e) { + e.printStackTrace(); + } + } + + + private static void basicAuthTest(PropertyLocator dl, SecuritySetter ss) { + try { + URI uri = dl.get(dl.best()); + System.out.println("Resolved to: " + uri); + HClient client = new HClient(ss, uri, 3000); + client.setMethod("GET"); + client.setPathInfo("/authn/basicAuth"); + client.addHeader("Accept", "text/plain"); + client.send(); + + + Future future = client.futureReadString(); + if(future.get(7000)) { + System.out.println("BasicAuth Validated"); + } else { + System.out.println("Failure " + future.code() + ":" + future.body()); + } + } catch (CadiException | LocatorException | APIException e) { + e.printStackTrace(); + } + } + + // Regular DME2Client Coding Style + private static void dme2RawTest(DME2Manager dm, String aafurl, String user, String pass) { + try { + if(dm==null) { + return; + } + URI uri = new URI(aafurl); + print(true,"DME2 Direct Client Coding Methodology",uri.toString()); + DME2Client client = dm.newClient( uri, 3000); + client.setMethod("GET"); // FYI, DME2 defaults to "POST" + client.setContext("/authz/perms/user/"+user); // DME2 direct requires separate setting of Context from URI + if(pass!=null) { // rely on Cert if no pass + client.setCredentials(user, pass); + } + client.setPayload(""); // DME2 will not send without something + String resp = client.sendAndWait(7000); + System.out.println(resp); + } catch(Throwable e) { + e.printStackTrace(); + } + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/PermEval.java b/aaf/src/main/java/com/att/cadi/aaf/PermEval.java new file mode 100644 index 0000000..b661df4 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/PermEval.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf; + +import com.att.inno.env.util.Split; + + +public class PermEval { + public static final char START_REGEX_CHAR = '!'; + public static final char START_INST_KEY_CHAR=':'; + public static final char ALT_START_INST_KEY_CHAR='/'; + + public static final char LIST_SEP = ','; + public static final String INST_KEY_REGEX = new StringBuilder().append(START_INST_KEY_CHAR).toString(); + public static final String ASTERIX = "*"; + + /** + * Evaluate Instance + * + * Instance can be more complex. It can be a string, a Regular Expression, or a ":" separated Key + * who's parts can also be a String, Regular Expression. + * + * sInst = Server's Instance + * In order to prevent false matches, keys must be the same length to count as equal + * Changing this will break existing users, like Cassandra. 9-4-2015 + */ + public static boolean evalInstance(String sInst, String pInst) { + if(sInst==null || pInst == null) { + return false; + } + if(ASTERIX.equals(sInst)) { + return true; // If Server's String is "*", then it accepts every Instance + } + char firstChar = pInst.charAt(0); + char startChar = firstChar==ALT_START_INST_KEY_CHAR?ALT_START_INST_KEY_CHAR:START_INST_KEY_CHAR; + switch(pInst.charAt(0)) { // First char + case START_REGEX_CHAR: // Evaluate as Regular Expression + String pItem = pInst.substring(1); + for(String sItem : Split.split(LIST_SEP,sInst)) { // allow for "," definition in Action + return sItem.matches(pItem); + } + + case START_INST_KEY_CHAR: // Evaluate a special Key field, i.e.:xyz:*:!df.* + case ALT_START_INST_KEY_CHAR: // Also allow '/' as special Key Field, i.e. /xyz/*/!.* + if(sInst.charAt(0)==startChar) { // To compare key-to-key, both strings must be keys + String[] skeys=Split.split(startChar,sInst); + String[] pkeys=Split.split(startChar,pInst); + if(skeys.length!=pkeys.length) return false; + + boolean pass = true; + for(int i=1;pass && i certs = null; + + // Did this to add other Trust Mechanisms + // Trust mechanism set by Property: + private static final String[] authMechanisms = new String[] {"tguard","basicAuth","csp"}; + private static String[] certIDs; + + private static Map> trusted =null; + + public AAFListedCertIdentity(Access access, AAFCon aafcon) throws APIException { + synchronized(AAFListedCertIdentity.class) { + if(certIDs==null) { + String cip = access.getProperty(Config.AAF_CERT_IDS, null); + if(cip!=null) { + certIDs = Split.split(',',cip); + } + } + if(certIDs!=null && certs==null) { + TimerTask cu = new CertUpdate(aafcon); + cu.run(); // want this to run in this thread first... + new Timer("AAF Identity Refresh Timer",true).scheduleAtFixedRate(cu, EIGHT_HOURS,EIGHT_HOURS); + } + } + } + + public static Set trusted(String authMech) { + return trusted.get(authMech); + } + + public Principal identity(HttpServletRequest req, X509Certificate cert, byte[] certBytes) throws CertificateException { + if(cert==null && certBytes==null)return null; + if(certBytes==null)certBytes = cert.getEncoded(); + byte[] fingerprint = X509Taf.getFingerPrint(certBytes); + String id = certs.get(new ByteArrayHolder(fingerprint)); + if(id!=null) { // Caller is Validated + return new X509Principal(id,cert,certBytes); + } + return null; + } + + private static class ByteArrayHolder implements Comparable { + private byte[] ba; + public ByteArrayHolder(byte[] ba) { + this.ba = ba; + } + public int compareTo(ByteArrayHolder b) { + return Hash.compareTo(ba, b.ba); + } + } + + private class CertUpdate extends TimerTask { + + private AAFCon aafcon; + public CertUpdate(AAFCon con) { + aafcon = con; + } + + @Override + public void run() { + try { + TreeMap newCertsMap = new TreeMap(); + Map> newTrustMap = new TreeMap>(); + Set userLookup = new HashSet(); + for(String s : certIDs) { + userLookup.add(s); + } + for(String authMech : authMechanisms) { + Future fusr = aafcon.client(AAF_VERSION).read("/authz/users/perm/com.att.aaf.trust/"+authMech+"/authenticate", Users.class, aafcon.usersDF); + if(fusr.get(5000)) { + List users = fusr.value.getUser(); + if(users.isEmpty()) { + aafcon.access.log(Level.WARN, "AAF Lookup-No IDs in Role com.att.aaf.trustForID <> "+authMech); + } else { + aafcon.access.log(Level.INFO,"Loading Trust Authentication Info for",authMech); + Set hsUser = new HashSet(); + for(User u : users) { + userLookup.add(u.getId()); + hsUser.add(u.getId()); + } + newTrustMap.put(authMech,hsUser); + } + } else { + aafcon.access.log(Level.WARN, "Could not get Users in Perm com.att.trust|tguard|authenticate",fusr.code(),fusr.body()); + } + + } + + for(String u : userLookup) { + Future fc = aafcon.client(AAF_VERSION).read("/authn/cert/id/"+u, Certs.class, aafcon.certsDF); + XMLGregorianCalendar now = Chrono.timeStamp(); + if(fc.get(5000)) { + List certs = fc.value.getCert(); + if(certs.isEmpty()) { + aafcon.access.log(Level.WARN, "No Cert Associations for",u); + } else { + for(Cert c : fc.value.getCert()) { + XMLGregorianCalendar then =c.getExpires(); + if(then !=null && then.compare(now)>0) { + newCertsMap.put(new ByteArrayHolder(c.getFingerprint()), c.getId()); + aafcon.access.log(Level.INIT,"Associating "+ c.getId() + " expiring " + Chrono.dateOnlyStamp(c.getExpires()) + " with " + c.getX500()); + } + } + } + } else { + aafcon.access.log(Level.WARN, "Could not get Certificates for",u); + } + } + + certs = newCertsMap; + trusted = newTrustMap; + } catch(Exception e) { + aafcon.access.log(e, "Failure to update Certificate Identities from AAF"); + } + } + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/client/ErrMessage.java b/aaf/src/main/java/com/att/cadi/aaf/client/ErrMessage.java new file mode 100644 index 0000000..7fc776a --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/client/ErrMessage.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.client; + +import java.io.PrintStream; + +import aaf.v2_0.Error; + +import com.att.cadi.client.Future; +import com.att.cadi.util.Vars; +import com.att.inno.env.APIException; +import com.att.inno.env.Data.TYPE; +import com.att.rosetta.env.RosettaDF; +import com.att.rosetta.env.RosettaEnv; + +public class ErrMessage { + private RosettaDF errDF; + + public ErrMessage(RosettaEnv env) throws APIException { + errDF = env.newDataFactory(Error.class); + } + + /** + * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with. + * + * This code will create a meaningful string from this format. + * + * @param ps + * @param df + * @param r + * @throws APIException + */ + public void printErr(PrintStream ps, String attErrJson) throws APIException { + StringBuilder sb = new StringBuilder(); + Error err = errDF.newData().in(TYPE.JSON).load(attErrJson).asObject(); + ps.println(toMsg(sb,err)); + } + + /** + * AT&T Requires a specific Error Format for RESTful Services, which AAF complies with. + * + * This code will create a meaningful string from this format. + * + * @param sb + * @param df + * @param r + * @throws APIException + */ + public StringBuilder toMsg(StringBuilder sb, String attErrJson) throws APIException { + return toMsg(sb,errDF.newData().in(TYPE.JSON).load(attErrJson).asObject()); + } + + public StringBuilder toMsg(Future future) { + return toMsg(new StringBuilder(),future); + } + + public StringBuilder toMsg(StringBuilder sb, Future future) { + try { + toMsg(sb,errDF.newData().in(TYPE.JSON).load(future.body()).asObject()); + } catch(Exception e) { + //just print what we can + sb.append(future.code()); + sb.append(": "); + sb.append(future.body()); + } + return sb; + } + + public StringBuilder toMsg(StringBuilder sb, Error err) { + sb.append(err.getMessageId()); + sb.append(' '); + String[] vars = new String[err.getVariables().size()]; + err.getVariables().toArray(vars); + Vars.convert(sb, err.getText(),vars); + return sb; + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/client/Examples.java b/aaf/src/main/java/com/att/cadi/aaf/client/Examples.java new file mode 100644 index 0000000..e8a4756 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/client/Examples.java @@ -0,0 +1,445 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.client; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.GregorianCalendar; + +import aaf.v2_0.Approval; +import aaf.v2_0.Approvals; +import aaf.v2_0.CredRequest; +import aaf.v2_0.Keys; +import aaf.v2_0.NsRequest; +import aaf.v2_0.Nss; +import aaf.v2_0.Nss.Ns; +import aaf.v2_0.Perm; +import aaf.v2_0.PermKey; +import aaf.v2_0.PermRequest; +import aaf.v2_0.Perms; +import aaf.v2_0.Pkey; +import aaf.v2_0.Request; +import aaf.v2_0.Role; +import aaf.v2_0.RoleKey; +import aaf.v2_0.RolePermRequest; +import aaf.v2_0.RoleRequest; +import aaf.v2_0.Roles; +import aaf.v2_0.UserRole; +import aaf.v2_0.UserRoleRequest; +import aaf.v2_0.UserRoles; +import aaf.v2_0.Users; +import aaf.v2_0.Users.User; + +import com.att.inno.env.APIException; +import com.att.inno.env.Data; +import com.att.inno.env.Data.TYPE; +import com.att.inno.env.util.Chrono; +import com.att.rosetta.env.RosettaDF; +import com.att.rosetta.env.RosettaEnv; + +public class Examples { + public static String print(RosettaEnv env, String nameOrContentType, boolean optional) throws APIException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { + // Discover ClassName + String className = null; + String version = null; + TYPE type = TYPE.JSON; // default + if(nameOrContentType.startsWith("application/")) { + for(String ct : nameOrContentType.split("\\s*,\\s*")) { + for(String elem : ct.split("\\s*;\\s*")) { + if(elem.endsWith("+json")) { + type = TYPE.JSON; + className = elem.substring(elem.indexOf('/')+1, elem.length()-5); + } else if(elem.endsWith("+xml")) { + type = TYPE.XML; + className = elem.substring(elem.indexOf('/')+1, elem.length()-4); + } else if(elem.startsWith("version=")) { + version = elem.substring(8); + } + } + if(className!=null && version!=null)break; + } + if(className==null) { + throw new APIException(nameOrContentType + " does not contain Class Information"); + } + } else { + className = nameOrContentType; + } + + // No Void.class in aaf.v2_0 package causing errors when trying to use a newVoidv2_0 + // method similar to others in this class. This makes it work, but is it right? + if ("Void".equals(className)) return ""; + + if("1.1".equals(version)) { + version = "v1_0"; + } else if(version!=null) { + version = "v" + version.replace('.', '_'); + } else { + version = "v2_0"; + } + + Class cls; + try { + cls = Examples.class.getClassLoader().loadClass("aaf."+version+'.'+className); + } catch (ClassNotFoundException e) { + throw new APIException(e); + } + + Method meth; + try { + meth = Examples.class.getDeclaredMethod("new"+cls.getSimpleName()+version,boolean.class); + } catch (Exception e) { + throw new APIException("ERROR: " + cls.getName() + " does not have an Example in Code. Request from AAF Developers"); + } + + RosettaDF df = env.newDataFactory(cls); + df.option(Data.PRETTY); + + Object data = meth.invoke(null,optional); + + @SuppressWarnings("unchecked") + String rv = df.newData().load((C)data).out(type).asString(); +// Object obj = df.newData().in(type).load(rv).asObject(); + return rv; + } + + /* + * Set Base Class Request (easier than coding over and over) + */ + private static void setOptional(Request req) { + GregorianCalendar gc = new GregorianCalendar(); + req.setStart(Chrono.timeStamp(gc)); + gc.add(GregorianCalendar.MONTH, 6); + req.setEnd(Chrono.timeStamp(gc)); +// req.setForce("false"); + + } + + @SuppressWarnings("unused") + private static Request newRequestv2_0(boolean optional) { + Request r = new Request(); + setOptional(r); + return r; + } + @SuppressWarnings("unused") + private static RolePermRequest newRolePermRequestv2_0(boolean optional) { + RolePermRequest rpr = new RolePermRequest(); + Pkey pkey = new Pkey(); + pkey.setType("com.att.myns.mytype"); + pkey.setInstance("myInstance"); + pkey.setAction("myAction"); + rpr.setPerm(pkey); + rpr.setRole("com.att.myns.myrole"); + if(optional)setOptional(rpr); + return rpr; + } + + @SuppressWarnings("unused") + private static Roles newRolesv2_0(boolean optional) { + Role r; + Pkey p; + Roles rs = new Roles(); + rs.getRole().add(r = new Role()); + r.setName("com.att.myns.myRole"); + r.getPerms().add(p = new Pkey()); + p.setType("com.att.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + r.getPerms().add(p = new Pkey()); + p.setType("com.att.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + + rs.getRole().add(r = new Role()); + r.setName("com.att.myns.myOtherRole"); + r.getPerms().add(p = new Pkey()); + p.setType("com.att.myns.myOtherType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + r.getPerms().add(p = new Pkey()); + p.setType("com.att.myns.myOthertype"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + + return rs; + } + + + @SuppressWarnings("unused") + private static PermRequest newPermRequestv2_0(boolean optional) { + PermRequest pr = new PermRequest(); + pr.setType("com.att.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + if(optional) { + pr.setDescription("Short and meaningful verbiage about the Permission"); + + setOptional(pr); + } + return pr; + } + + @SuppressWarnings("unused") + private static Perm newPermv2_0(boolean optional) { + Perm pr = new Perm(); + pr.setType("com.att.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + pr.getRoles().add("com.att.myns.myRole"); + pr.getRoles().add("com.att.myns.myRole2"); + pr.setDescription("This is my description, and I'm sticking with it"); + if(optional) { + pr.setDescription("Short and meaningful verbiage about the Permission"); + } + return pr; + } + + + @SuppressWarnings("unused") + private static PermKey newPermKeyv2_0(boolean optional) { + PermKey pr = new PermKey(); + pr.setType("com.att.myns.myType"); + pr.setInstance("myInstance"); + pr.setAction("myAction"); + return pr; + } + + @SuppressWarnings("unused") + private static Perms newPermsv2_0(boolean optional) { + Perms perms = new Perms(); + Perm p; + perms.getPerm().add(p=new Perm()); + p.setType("com.att.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + p.getRoles().add("com.att.myns.myRole"); + p.getRoles().add("com.att.myns.myRole2"); + + + perms.getPerm().add(p=new Perm()); + p.setType("com.att.myns.myOtherType"); + p.setInstance("myInstance"); + p.setAction("myOtherAction"); + p.getRoles().add("com.att.myns.myRole"); + p.getRoles().add("com.att.myns.myRole2"); + + return perms; + + } + + @SuppressWarnings("unused") + private static UserRoleRequest newUserRoleRequestv2_0(boolean optional) { + UserRoleRequest urr = new UserRoleRequest(); + urr.setRole("com.att.myns.myRole"); + urr.setUser("ab1234@csp.att.com"); + if(optional) setOptional(urr); + return urr; + } + + @SuppressWarnings("unused") + private static NsRequest newNsRequestv2_0(boolean optional) { + NsRequest nr = new NsRequest(); + nr.setName("com.att.myns"); + nr.getResponsible().add("ab1234@csp.att.com"); + nr.getResponsible().add("cd5678@csp.att.com"); + nr.getAdmin().add("zy9876@csp.att.com"); + nr.getAdmin().add("xw5432@csp.att.com"); + if(optional) { + nr.setDescription("This is my Namespace to set up"); + nr.setType("APP"); + setOptional(nr); + } + return nr; + } + + + @SuppressWarnings("unused") + private static Nss newNssv2_0(boolean optional) { + Ns ns; + + Nss nss = new Nss(); + nss.getNs().add(ns = new Nss.Ns()); + ns.setName("com.att.myns"); + ns.getResponsible().add("ab1234@csp.att.com"); + ns.getResponsible().add("cd5678@csp.att.com"); + ns.getAdmin().add("zy9876@csp.att.com"); + ns.getAdmin().add("xw5432@csp.att.com"); + ns.setDescription("This is my Namespace to set up"); + + nss.getNs().add(ns = new Nss.Ns()); + ns.setName("com.att.myOtherNs"); + ns.getResponsible().add("ab1234@csp.att.com"); + ns.getResponsible().add("cd5678@csp.att.com"); + ns.getAdmin().add("zy9876@csp.att.com"); + ns.getAdmin().add("xw5432@csp.att.com"); + + return nss; + } + @SuppressWarnings("unused") + private static RoleRequest newRoleRequestv2_0(boolean optional) { + RoleRequest rr = new RoleRequest(); + rr.setName("com.att.myns.myRole"); + if(optional) { + rr.setDescription("This is my Role"); + setOptional(rr); + } + return rr; + } + + @SuppressWarnings("unused") + private static CredRequest newCredRequestv2_0(boolean optional) { + CredRequest cr = new CredRequest(); + cr.setId("myID@fully.qualified.domain"); + if(optional) { + cr.setType(2); + cr.setEntry("0x125AB256344CE"); + } else { + cr.setPassword("This is my provisioned password"); + } + + return cr; + } + + @SuppressWarnings("unused") + private static Users newUsersv2_0(boolean optional) { + User user; + + Users users = new Users(); + users.getUser().add(user = new Users.User()); + user.setId("ab1234@csp.att.com"); + GregorianCalendar gc = new GregorianCalendar(); + user.setExpires(Chrono.timeStamp(gc)); + + users.getUser().add(user = new Users.User()); + user.setId("zy9876@csp.att.com"); + user.setExpires(Chrono.timeStamp(gc)); + + return users; + } + + @SuppressWarnings("unused") + private static Role newRolev2_0(boolean optional) { + Role r = new Role(); + Pkey p; + r.setName("com.att.myns.myRole"); + r.getPerms().add(p = new Pkey()); + p.setType("com.att.myns.myType"); + p.setInstance("myInstance"); + p.setAction("myAction"); + + return r; + } + + @SuppressWarnings("unused") + private static RoleKey newRoleKeyv2_0(boolean optional) { + RoleKey r = new RoleKey(); + Pkey p; + r.setName("com.att.myns.myRole"); + return r; + } + + @SuppressWarnings("unused") + private static Keys newKeysv2_0(boolean optional) { + Keys ks = new Keys(); + ks.getKey().add("Reponse 1"); + ks.getKey().add("Response 2"); + return ks; + } + + @SuppressWarnings("unused") + private static UserRoles newUserRolesv2_0(boolean optional) { + UserRoles urs = new UserRoles(); + UserRole ur = new UserRole(); + ur.setUser("xy1234"); + ur.setRole("com.test.myapp.myRole"); + ur.setExpires(Chrono.timeStamp()); + urs.getUserRole().add(ur); + + ur = new UserRole(); + ur.setUser("yx4321"); + ur.setRole("com.test.yourapp.yourRole"); + ur.setExpires(Chrono.timeStamp()); + urs.getUserRole().add(ur); + return urs; + } + + + @SuppressWarnings("unused") + private static Approvals newApprovalsv2_0(boolean optional) { + Approvals as = new Approvals(); + Approval a = new Approval(); + a.setApprover("MyApprover"); + a.setId("MyID"); + a.setMemo("My memo (and then some)"); + a.setOperation("MyOperation"); + a.setStatus("MyStatus"); + a.setTicket("MyTicket"); + a.setType("MyType"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser"); + as.getApprovals().add(a); + a = new Approval(); + a.setApprover("MyApprover2"); + a.setId("MyID2"); + a.setMemo("My memo (and then some)2"); + a.setOperation("MyOperation2"); + a.setStatus("MyStatus2"); + a.setTicket("MyTicket2"); + a.setType("MyType2"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser2"); + as.getApprovals().add(a); + return as; + } + + @SuppressWarnings("unused") + private static Approval newApprovalv2_0(boolean optional) { + Approval a = new Approval(); + a.setApprover("MyApprover"); + a.setId("MyID"); + a.setMemo("My memo (and then some)"); + a.setOperation("MyOperation"); + a.setStatus("MyStatus"); + a.setTicket("MyTicket"); + a.setType("MyType"); + a.setUpdated(Chrono.timeStamp()); + a.setUser("MyUser"); + return a; + } + + + + @SuppressWarnings("unused") + private static aaf.v2_0.Error newErrorv2_0(boolean optional) { + aaf.v2_0.Error err = new aaf.v2_0.Error(); + err.setMessageId("SVC1403"); + err.setText("MyText %s, %s: The last three digits are usually the HTTP Code"); + err.getVariables().add("Variable 1"); + err.getVariables().add("Variable 2"); + return err; + } + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/marshal/CertMarshal.java b/aaf/src/main/java/com/att/cadi/aaf/marshal/CertMarshal.java new file mode 100644 index 0000000..4193d43 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/marshal/CertMarshal.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.marshal; + +import javax.xml.datatype.XMLGregorianCalendar; + +import aaf.v2_0.Certs.Cert; + +import com.att.rosetta.marshal.FieldDateTime; +import com.att.rosetta.marshal.FieldHexBinary; +import com.att.rosetta.marshal.FieldString; +import com.att.rosetta.marshal.ObjMarshal; + +public class CertMarshal extends ObjMarshal { + public CertMarshal() { + add(new FieldHexBinary("fingerprint") { + @Override + protected byte[] data(Cert t) { + return t.getFingerprint(); + } + }); + + add(new FieldString("id") { + @Override + protected String data(Cert t) { + return t.getId(); + } + }); + + add(new FieldString("x500") { + @Override + protected String data(Cert t) { + return t.getX500(); + } + }); + + add(new FieldDateTime("expires") { + @Override + protected XMLGregorianCalendar data(Cert t) { + return t.getExpires(); + } + }); + + + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/marshal/CertsMarshal.java b/aaf/src/main/java/com/att/cadi/aaf/marshal/CertsMarshal.java new file mode 100644 index 0000000..16fc580 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/marshal/CertsMarshal.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.marshal; + +import java.util.List; + +import aaf.v2_0.Certs; +import aaf.v2_0.Certs.Cert; + +import com.att.rosetta.marshal.ObjArray; +import com.att.rosetta.marshal.ObjMarshal; + +public class CertsMarshal extends ObjMarshal { + + public CertsMarshal() { + add(new ObjArray("cert",new CertMarshal()) { + @Override + protected List data(Certs t) { + return t.getCert(); + } + }); + } + + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFAuthn.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFAuthn.java new file mode 100644 index 0000000..db60495 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFAuthn.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.io.IOException; + +import com.att.aft.dme2.api.DME2Exception; +import com.att.cadi.AbsUserCache; +import com.att.cadi.CachedPrincipal; +import com.att.cadi.CadiException; +import com.att.cadi.GetCred; +import com.att.cadi.Hash; +import com.att.cadi.User; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.client.Future; +import com.att.cadi.client.Rcli; +import com.att.cadi.config.Config; +import com.att.cadi.lur.ConfigPrincipal; +import com.att.inno.env.APIException; + +public class AAFAuthn extends AbsUserCache { + private AAFCon con; + private String realm; + + /** + * Configure with Standard AAF properties, Stand alone + * @param con + * @throws Exception + */ + // Package on purpose + AAFAuthn(AAFCon con) throws Exception { + super(con.access,con.cleanInterval,con.highCount,con.usageRefreshTriggerCount); + this.con = con; + + try { + setRealm(); + } catch (APIException e) { + if(e.getCause() instanceof DME2Exception) { + // Can't contact AAF, assume default + realm=con.access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm()); + } + } + } + + /** + * Configure with Standard AAF properties, but share the Cache (with AAF Lur) + * @param con + * @throws Exception + */ + // Package on purpose + AAFAuthn(AAFCon con, AbsUserCache cache) throws Exception { + super(cache); + this.con = con; + try { + setRealm(); + } catch (Exception e) { + if(e.getCause() instanceof DME2Exception) { + access.log(e); + // Can't contact AAF, assume default + realm=con.access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm()); + } + } + } + + private void setRealm() throws Exception { + // Make a call without security set to get the 401 response, which + // includes the Realm of the server + // This also checks on Connectivity early on. + Future fp = con.client(AAFCon.AAF_LATEST_VERSION).read("/authn/basicAuth", "text/plain"); + if(fp.get(con.timeout)) { + throw new Exception("Do not preset Basic Auth Information for AAFAuthn"); + } else { + if(fp.code()==401) { + realm = fp.header("WWW-Authenticate"); + if(realm!=null && realm.startsWith("Basic realm=\"")) { + realm = realm.substring(13, realm.length()-1); + } else { + realm = "unknown.com"; + } + } + } + } + + /** + * Return Native Realm of AAF Instance. + * + * @return + */ + public String getRealm() { + return realm; + } + + /** + * Returns null if ok, or an Error String; + * + * @param user + * @param password + * @return + * @throws IOException + * @throws CadiException + * @throws Exception + */ + public String validate(String user, String password) throws IOException, CadiException { + User usr = getUser(user); + if(password.startsWith("enc:???")) { + password = access.decrypt(password, true); + } + + byte[] bytes = password.getBytes(); + if(usr != null && usr.principal != null && usr.principal.getName().equals(user) + && usr.principal instanceof GetCred) { + + if(Hash.isEqual(((GetCred)usr.principal).getCred(),bytes)) { + return null; + } else { + remove(usr); + usr = null; + } + } + + AAFCachedPrincipal cp = new AAFCachedPrincipal(this,con.app, user, bytes, con.cleanInterval); + // Since I've relocated the Validation piece in the Principal, just revalidate, then do Switch + // Statement + switch(cp.revalidate()) { + case REVALIDATED: + if(usr!=null) { + usr.principal = cp; + } else { + addUser(new User(cp,con.timeout)); + } + return null; + case INACCESSIBLE: + return "AAF Inaccessible"; + case UNVALIDATED: + return "User/Pass combo invalid for " + user; + case DENIED: + return "AAF denies API for " + user; + default: + return "AAFAuthn doesn't handle Principal " + user; + } + } + + private class AAFCachedPrincipal extends ConfigPrincipal implements CachedPrincipal { + private long expires,timeToLive; + + public AAFCachedPrincipal(AAFAuthn aaf, String app, String name, byte[] pass, int timeToLive) { + super(name,pass); + this.timeToLive = timeToLive; + expires = timeToLive + System.currentTimeMillis(); + } + + public Resp revalidate() { + if(con.isDisabled()) { + return Resp.DENIED; + } + try { + Miss missed = missed(getName()); + if(missed==null || missed.mayContinue(getCred())) { + Rcli client = con.client(AAFCon.AAF_LATEST_VERSION).forUser(con.basicAuth(getName(), new String(getCred()))); + Future fp = client.read( + "/authn/basicAuth", + "text/plain" + ); + if(fp.get(con.timeout)) { + expires = System.currentTimeMillis() + timeToLive; + addUser(new User(this, expires)); + return Resp.REVALIDATED; + } else { + addMiss(getName(), getCred()); + return Resp.UNVALIDATED; + } + } else { + return Resp.UNVALIDATED; + } + } catch (Exception e) { + con.access.log(e); + return Resp.INACCESSIBLE; + } + } + + public long expires() { + return expires; + } + }; + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFCon.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFCon.java new file mode 100644 index 0000000..573c85e --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFCon.java @@ -0,0 +1,396 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.net.URI; +import java.security.Principal; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; + +import com.att.cadi.AbsUserCache; +import com.att.cadi.CadiException; +import com.att.cadi.CadiWrap; +import com.att.cadi.Connector; +import com.att.cadi.LocatorException; +import com.att.cadi.Lur; +import com.att.cadi.PropAccess; +import com.att.cadi.SecuritySetter; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.aaf.marshal.CertsMarshal; +import com.att.cadi.client.AbsBasicAuth; +import com.att.cadi.client.Future; +import com.att.cadi.client.Rcli; +import com.att.cadi.client.Retryable; +import com.att.cadi.config.Config; +import com.att.cadi.config.SecurityInfoC; +import com.att.cadi.lur.EpiLur; +import com.att.cadi.principal.BasicPrincipal; +import com.att.cadi.util.Vars; +import com.att.inno.env.APIException; +import com.att.inno.env.Data.TYPE; +import com.att.inno.env.util.Split; +import com.att.rosetta.env.RosettaDF; +import com.att.rosetta.env.RosettaEnv; + +import aaf.v2_0.Certs; +import aaf.v2_0.Error; +import aaf.v2_0.Perms; +import aaf.v2_0.Users; + +public abstract class AAFCon implements Connector { + public static final String AAF_LATEST_VERSION = "2.0"; + + final public PropAccess access; + // Package access + final public int timeout, cleanInterval, connTimeout; + final public int highCount, userExpires, usageRefreshTriggerCount; + private Map> clients = new ConcurrentHashMap>(); + final public RosettaDF permsDF; + final public RosettaDF certsDF; + final public RosettaDF usersDF; + final public RosettaDF errDF; + private String realm; + public final String app; + protected SecuritySetter ss; + protected SecurityInfoC si; + + private DisableCheck disableCheck; + + private AAFLurPerm lur; + + private RosettaEnv env; + protected abstract URI initURI(); + protected abstract void setInitURI(String uriString) throws CadiException; + + /** + * Use this call to get the appropriate client based on configuration (DME2, HTTP, future) + * + * @param apiVersion + * @return + * @throws CadiException + */ + public Rcli client(String apiVersion) throws CadiException { + Rcli client = clients.get(apiVersion); + if(client==null) { + client = rclient(initURI(),ss); + client.apiVersion(apiVersion) + .readTimeout(connTimeout); + clients.put(apiVersion, client); + } + return client; + } + + /** + * Use this API when you have permission to have your call act as the end client's ID. + * + * Your calls will get 403 errors if you do not have this permission. it is a special setup, rarely given. + * + * @param apiVersion + * @param req + * @return + * @throws CadiException + */ + public Rcli clientAs(String apiVersion, ServletRequest req) throws CadiException { + Rcli cl = client(apiVersion); + return cl.forUser(transferSS(((HttpServletRequest)req).getUserPrincipal())); + } + + protected AAFCon(AAFCon copy) { + access = copy.access; + timeout = copy.timeout; + cleanInterval = copy.cleanInterval; + connTimeout = copy.connTimeout; + highCount = copy.highCount; + userExpires = copy.userExpires; + usageRefreshTriggerCount = copy.usageRefreshTriggerCount; + permsDF = copy.permsDF; + certsDF = copy.certsDF; + usersDF = copy.usersDF; + errDF = copy.errDF; + app = copy.app; + ss = copy.ss; + si = copy.si; + env = copy.env; + disableCheck = copy.disableCheck; + realm = copy.realm; + } + + protected AAFCon(PropAccess access, String tag, SecurityInfoC si) throws CadiException{ + if(tag==null) { + throw new CadiException("AAFCon cannot be constructed with a tag=null"); + } + try { + this.access = access; + this.si = si; + this.ss = si.defSS; + if(ss==null) { + String mechid = access.getProperty(Config.AAF_MECHID, null); + String encpass = access.getProperty(Config.AAF_MECHPASS, null); + if(encpass==null) { + String alias = access.getProperty(Config.CADI_ALIAS, mechid); + if(alias==null) { + throw new CadiException(Config.CADI_ALIAS + " or " + Config.AAF_MECHID + " required."); + } + set(si.defSS=x509Alias(alias)); + } else { + if(mechid!=null && encpass !=null) { + set(si.defSS=basicAuth(mechid, encpass)); + } else { + set(si.defSS=new SecuritySetter() { + + @Override + public String getID() { + return ""; + } + + @Override + public void setSecurity(CLIENT client) throws CadiException { + throw new CadiException("AAFCon has not been initialized with Credentials (SecuritySetter)"); + } + + @Override + public int setLastResponse(int respCode) { + return 0; + } + }); + } + } + } + + timeout = Integer.parseInt(access.getProperty(Config.AAF_READ_TIMEOUT, Config.AAF_READ_TIMEOUT_DEF)); + cleanInterval = Integer.parseInt(access.getProperty(Config.AAF_CLEAN_INTERVAL, Config.AAF_CLEAN_INTERVAL_DEF)); + highCount = Integer.parseInt(access.getProperty(Config.AAF_HIGH_COUNT, Config.AAF_HIGH_COUNT_DEF).trim()); + connTimeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF).trim()); + userExpires = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim()); + usageRefreshTriggerCount = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim())-1; // zero based + + String str = access.getProperty(tag,null); + if(str==null) { + throw new CadiException(tag + " property is required."); + } + setInitURI(str); + + app=reverseDomain(ss.getID()); + realm="openecomp.org"; + + env = new RosettaEnv(); + permsDF = env.newDataFactory(Perms.class); + usersDF = env.newDataFactory(Users.class); + certsDF = env.newDataFactory(Certs.class); + certsDF.rootMarshal(new CertsMarshal()); // Speedier Marshaling + errDF = env.newDataFactory(Error.class); + } catch (APIException e) { + throw new CadiException("AAFCon cannot be configured",e); + } + } + + public RosettaEnv env() { + return env; + } + + /** + * Return the backing AAFCon, if there is a Lur Setup that is AAF. + * + * If there is no AAFLur setup, it will return "null" + * @param servletRequest + * @return + */ + public static final AAFCon obtain(Object servletRequest) { + if(servletRequest instanceof CadiWrap) { + Lur lur = ((CadiWrap)servletRequest).getLur(); + if(lur != null) { + if(lur instanceof EpiLur) { + AbsAAFLur aal = (AbsAAFLur) ((EpiLur)lur).subLur(AbsAAFLur.class); + if(aal!=null) { + return aal.aaf; + } + } else { + if(lur instanceof AbsAAFLur) { + return ((AbsAAFLur)lur).aaf; + } + } + } + } + return null; + } + + public abstract AAFCon clone(String url) throws CadiException; + + public AAFAuthn newAuthn() throws APIException { + try { + return new AAFAuthn(this); + } catch (APIException e) { + throw e; + } catch (Exception e) { + throw new APIException(e); + } + } + + public AAFAuthn newAuthn(AbsUserCache c) throws APIException { + try { + return new AAFAuthn(this,c); + } catch (APIException e) { + throw e; + } catch (Exception e) { + throw new APIException(e); + } + } + + public AAFLurPerm newLur() throws CadiException { + try { + if(lur==null) { + return new AAFLurPerm(this); + } else { + return new AAFLurPerm(this,lur); + } + } catch (CadiException e) { + throw e; + } catch (Exception e) { + throw new CadiException(e); + } + } + + public AAFLurPerm newLur(AbsUserCache c) throws APIException { + try { + return new AAFLurPerm(this,c); + } catch (APIException e) { + throw e; + } catch (Exception e) { + throw new APIException(e); + } + } + + /** + * Take a Fully Qualified User, and get a Namespace from it. + * @param user + * @return + */ + public static String reverseDomain(String user) { + StringBuilder sb = null; + String[] split = Split.split('.',user); + int at; + for(int i=split.length-1;i>=0;--i) { + if(sb == null) { + sb = new StringBuilder(); + } else { + sb.append('.'); + } + + if((at = split[i].indexOf('@'))>0) { + sb.append(split[i].subSequence(at+1, split[i].length())); + } else { + sb.append(split[i]); + } + } + + return sb==null?"":sb.toString(); + } + + protected abstract Rcli rclient(URI uri, SecuritySetter ss) throws CadiException; + + public abstract RET best(Retryable retryable) throws LocatorException, CadiException, APIException; + + + public abstract SecuritySetter basicAuth(String user, String password) throws CadiException; + + public abstract SecuritySetter transferSS(Principal principal) throws CadiException; + + public abstract SecuritySetter basicAuthSS(BasicPrincipal principal) throws CadiException; + + public abstract SecuritySetter x509Alias(String alias) throws APIException, CadiException; + + + public String getRealm() { + return realm; + + } + + public SecuritySetter set(final SecuritySetter ss) { + this.ss = ss; + if(ss instanceof AbsBasicAuth) { + disableCheck = (ss instanceof AbsBasicAuth)? + new DisableCheck() { + AbsBasicAuth aba = (AbsBasicAuth)ss; + @Override + public boolean isDisabled() { + return aba.isDenied(); + } + }: + new DisableCheck() { + @Override + public boolean isDisabled() { + return this.isDisabled(); + } + }; + } + for(Rcli client : clients.values()) { + client.setSecuritySetter(ss); + } + return ss; + } + + public SecurityInfoC securityInfo() { + return si; + } + + public String defID() { + if(ss!=null) { + return ss.getID(); + } + return "unknown"; + } + + public void invalidate() throws CadiException { + for(Rcli client : clients.values()) { + client.invalidate(); + clients.remove(client); + } + } + + public String readableErrMsg(Future f) { + String text = f.body(); + if(text==null || text.length()==0) { + text = f.code() + ": **No Message**"; + } else if(text.contains("%")) { + try { + Error err = errDF.newData().in(TYPE.JSON).load(f.body()).asObject(); + return Vars.convert(err.getText(),err.getVariables()); + } catch (APIException e){ + // just return the body below + } + } + return text; + } + + private interface DisableCheck { + public boolean isDisabled(); + }; + + public boolean isDisabled() { + return disableCheck.isDisabled(); + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConDME2.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConDME2.java new file mode 100644 index 0000000..7092613 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConDME2.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.util.Properties; + +import com.att.aft.dme2.api.DME2Client; +import com.att.aft.dme2.api.DME2Exception; +import com.att.aft.dme2.api.DME2Manager; +import com.att.cadi.CadiException; +import com.att.cadi.LocatorException; +import com.att.cadi.PropAccess; +import com.att.cadi.SecuritySetter; +import com.att.cadi.client.Rcli; +import com.att.cadi.client.Retryable; +import com.att.cadi.config.Config; +import com.att.cadi.config.SecurityInfoC; +import com.att.cadi.dme2.DME2BasicAuth; +import com.att.cadi.dme2.DME2TransferSS; +import com.att.cadi.dme2.DME2x509SS; +import com.att.cadi.dme2.DRcli; +import com.att.cadi.principal.BasicPrincipal; +import com.att.inno.env.APIException; + +public class AAFConDME2 extends AAFCon{ + private DME2Manager manager; + private boolean isProxy; + private URI initURI; + + public AAFConDME2(PropAccess access) throws CadiException, GeneralSecurityException, IOException{ + super(access,Config.AAF_URL,new SecurityInfoC (access)); + manager = newManager(access); + setIsProxy(); + } + + public AAFConDME2(PropAccess access, String url) throws CadiException, GeneralSecurityException, IOException{ + super(access,url,new SecurityInfoC (access)); + manager = newManager(access); + setIsProxy(); + } + + public AAFConDME2(PropAccess access, SecurityInfoC si) throws CadiException { + super(access,Config.AAF_URL,si); + manager = newManager(access); + setIsProxy(); + } + + public AAFConDME2(PropAccess access, String url, SecurityInfoC si) throws CadiException { + super(access,url,si); + manager = newManager(access); + setIsProxy(); + } + + /** + * Construct a Connector based on the AAF one. This is for remote access to OTHER than AAF, + * but using Credentials, etc + */ + private AAFConDME2(AAFCon aafcon, String url) throws CadiException { + super(aafcon); + try { + initURI = new URI(url); + } catch (URISyntaxException e) { + throw new CadiException(e); + } + manager = newManager(access); + } + + /** + * Create a Connector based on the AAF one. This is for remote access to OTHER than AAF, + * but using Credentials, etc + */ + public AAFCon clone(String url) throws CadiException { + return new AAFConDME2(this,url); + } + + private void setIsProxy() { + String str; + if((str=access.getProperty(Config.AAF_URL, null))!=null) { + isProxy = str.contains("service=com.att.authz.authz-gw/version="); + } + } + + private DME2Manager newManager(PropAccess access) throws CadiException { + Properties props = access.getDME2Properties(); + // Critical that TLS Settings not ignored + try { + return new DME2Manager("AAFCon",props); + } catch (DME2Exception e) { + throw new CadiException(e); + } + } + + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#basicAuth(java.lang.String, java.lang.String) + */ + @Override + public SecuritySetter basicAuth(String user, String password) throws CadiException { + if(password.startsWith("enc:???")) { + try { + password = access.decrypt(password, true); + } catch (IOException e) { + throw new CadiException("Error Decrypting Password",e); + } + } + + try { + return set(new DME2BasicAuth(user,password,si)); + } catch (IOException e) { + throw new CadiException("Error setting up DME2BasicAuth",e); + } + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#rclient(java.net.URI, com.att.cadi.SecuritySetter) + */ + @Override + protected Rcli rclient(URI uri, SecuritySetter ss) { + DRcli dc = new DRcli(uri, ss); + dc.setProxy(isProxy); + dc.setManager(manager); + return dc; + } + + @Override + public SecuritySetter transferSS(Principal principal) throws CadiException { + try { + return principal==null?ss:new DME2TransferSS(principal, app, si); + } catch (IOException e) { + throw new CadiException("Error creating DME2TransferSS",e); + } + } + + @Override + public SecuritySetter basicAuthSS(BasicPrincipal principal) throws CadiException { + try { + return new DME2BasicAuth(principal,si); + } catch (IOException e) { + throw new CadiException("Error creating DME2BasicAuth",e); + } + + } + + @Override + public SecuritySetter x509Alias(String alias) throws CadiException { + try { + presetProps(access, alias); + return new DME2x509SS(alias,si); + } catch (Exception e) { + throw new CadiException("Error creating DME2x509SS",e); + } + } + + @Override + public RET best(Retryable retryable) throws LocatorException, CadiException, APIException { + // NOTE: DME2 had Retry Logic embedded lower. + try { + return (retryable.code(rclient(initURI,ss))); + } catch (ConnectException e) { + // DME2 should catch + try { + manager.refresh(); + } catch (Exception e1) { + throw new CadiException(e1); + } + throw new CadiException(e); + } + } + + public static void presetProps(PropAccess access, String alias) throws IOException { + System.setProperty(Config.AFT_DME2_CLIENT_SSL_CERT_ALIAS, alias); + if(System.getProperty(Config.AFT_DME2_CLIENT_IGNORE_SSL_CONFIG)==null) { + access.getDME2Properties(); + } + + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#initURI() + */ + @Override + protected URI initURI() { + return initURI; + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#setInitURI(java.lang.String) + */ + @Override + protected void setInitURI(String uriString) throws CadiException { + try { + initURI = new URI(uriString); + } catch (URISyntaxException e) { + throw new CadiException(e); + } + } +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConHttp.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConHttp.java new file mode 100644 index 0000000..5454773 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFConHttp.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.security.GeneralSecurityException; +import java.security.Principal; + +import com.att.cadi.CadiException; +import com.att.cadi.Locator; +import com.att.cadi.Locator.Item; +import com.att.cadi.LocatorException; +import com.att.cadi.PropAccess; +import com.att.cadi.SecuritySetter; +import com.att.cadi.client.AbsTransferSS; +import com.att.cadi.client.Rcli; +import com.att.cadi.client.Retryable; +import com.att.cadi.config.Config; +import com.att.cadi.config.SecurityInfoC; +import com.att.cadi.http.HBasicAuthSS; +import com.att.cadi.http.HMangr; +import com.att.cadi.http.HRcli; +import com.att.cadi.http.HTransferSS; +import com.att.cadi.http.HX509SS; +import com.att.cadi.principal.BasicPrincipal; +import com.att.inno.env.APIException; + +public class AAFConHttp extends AAFCon { + private final HMangr hman; + + public AAFConHttp(PropAccess access) throws CadiException, GeneralSecurityException, IOException { + super(access,Config.AAF_URL,new SecurityInfoC(access)); + hman = new HMangr(access,Config.loadLocator(access, access.getProperty(Config.AAF_URL,null))); + } + + public AAFConHttp(PropAccess access, String tag) throws CadiException, GeneralSecurityException, IOException { + super(access,tag,new SecurityInfoC(access)); + hman = new HMangr(access,Config.loadLocator(access, access.getProperty(tag,null))); + } + + public AAFConHttp(PropAccess access, String urlTag, SecurityInfoC si) throws CadiException { + super(access,urlTag,si); + hman = new HMangr(access,Config.loadLocator(access, access.getProperty(urlTag,null))); + } + + public AAFConHttp(PropAccess access, Locator locator) throws CadiException, GeneralSecurityException, IOException { + super(access,Config.AAF_URL,new SecurityInfoC(access)); + hman = new HMangr(access,locator); + } + + public AAFConHttp(PropAccess access, Locator locator, SecurityInfoC si) throws CadiException { + super(access,Config.AAF_URL,si); + hman = new HMangr(access,locator); + } + + public AAFConHttp(PropAccess access, Locator locator, SecurityInfoC si, String tag) throws CadiException { + super(access,tag,si); + hman = new HMangr(access, locator); + } + + private AAFConHttp(AAFCon aafcon, String url) { + super(aafcon); + hman = new HMangr(aafcon.access,Config.loadLocator(access, url)); + } + + @Override + public AAFCon clone(String url) { + return new AAFConHttp(this,url); + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#basicAuth(java.lang.String, java.lang.String) + */ + @Override + public SecuritySetter basicAuth(String user, String password) throws CadiException { + if(password.startsWith("enc:???")) { + try { + password = access.decrypt(password, true); + } catch (IOException e) { + throw new CadiException("Error decrypting password",e); + } + } + try { + return new HBasicAuthSS(user,password,si); + } catch (IOException e) { + throw new CadiException("Error creating HBasicAuthSS",e); + } + } + + public SecuritySetter x509Alias(String alias) throws APIException, CadiException { + try { + return set(new HX509SS(alias,si)); + } catch (Exception e) { + throw new CadiException("Error creating X509SS",e); + } + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#rclient(java.net.URI, com.att.cadi.SecuritySetter) + */ + @Override + protected Rcli rclient(URI ignoredURI, SecuritySetter ss) throws CadiException { + if(hman.loc==null) { + throw new CadiException("No Locator set in AAFConHttp"); + } + try { + return new HRcli(hman, hman.loc.best() ,ss); + } catch (Exception e) { + throw new CadiException(e); + } + } + + @Override + public AbsTransferSS transferSS(Principal principal) throws CadiException { + return new HTransferSS(principal, app,si); + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#basicAuthSS(java.security.Principal) + */ + @Override + public SecuritySetter basicAuthSS(BasicPrincipal principal) throws CadiException { + try { + return new HBasicAuthSS(principal,si); + } catch (IOException e) { + throw new CadiException("Error creating HBasicAuthSS",e); + } + } + + public HMangr hman() { + return hman; + } + + @Override + public RET best(Retryable retryable) throws LocatorException, CadiException, APIException { + return hman.best(ss, (Retryable)retryable); + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#initURI() + */ + @Override + protected URI initURI() { + try { + Item item = hman.loc.best(); + if(item!=null) { + return hman.loc.get(item); + } + } catch (LocatorException e) { + access.log(e, "Error in AAFConHttp obtaining initial URI"); + } + return null; + } + + /* (non-Javadoc) + * @see com.att.cadi.aaf.v2_0.AAFCon#setInitURI(java.lang.String) + */ + @Override + protected void setInitURI(String uriString) throws CadiException { + // TODO Auto-generated method stub + + } + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFLurPerm.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFLurPerm.java new file mode 100644 index 0000000..db03347 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFLurPerm.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.net.ConnectException; +import java.net.URISyntaxException; +import java.security.Principal; +import java.util.Map; + +import com.att.aft.dme2.api.DME2Exception; +import com.att.cadi.AbsUserCache; +import com.att.cadi.Access; +import com.att.cadi.Access.Level; +import com.att.cadi.CachedPrincipal.Resp; +import com.att.cadi.CadiException; +import com.att.cadi.Permission; +import com.att.cadi.User; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.client.Future; +import com.att.cadi.client.Rcli; +import com.att.cadi.client.Retryable; +import com.att.cadi.lur.LocalPermission; +import com.att.inno.env.APIException; +import com.att.inno.env.util.Split; + +import aaf.v2_0.Perm; +import aaf.v2_0.Perms; + +/** + * Use AAF Service as Permission Service. + * + * This Lur goes after AAF Permissions, which are elements of Roles, not the Roles themselves. + * + * If you want a simple Role Lur, use AAFRoleLur + * + * + */ +public class AAFLurPerm extends AbsAAFLur { + /** + * Need to be able to transmutate a Principal into either ATTUID or MechID, which are the only ones accepted at this + * point by AAF. There is no "domain", aka, no "@att.com" in "ab1234@att.com". + * + * The only thing that matters here for AAF is that we don't waste calls with IDs that obviously aren't valid. + * Thus, we validate that the ID portion follows the rules before we waste time accessing AAF remotely + * @throws APIException + * @throws URISyntaxException + * @throws DME2Exception + */ + // Package on purpose + AAFLurPerm(AAFCon con) throws CadiException, DME2Exception, URISyntaxException, APIException { + super(con); + } + + // Package on purpose + AAFLurPerm(AAFCon con, AbsUserCache auc) throws DME2Exception, URISyntaxException, APIException { + super(con,auc); + } + + protected User loadUser(Principal p) { + // Note: The rules for AAF is that it only stores permissions for ATTUID and MechIDs, which don't + // have domains. We are going to make the Transitive Class (see this.transmutative) to convert + Principal principal = transmutate.mutate(p); + if(principal==null)return null; // if not a valid Transmutated credential, don't bother calling... + return loadUser(p, p.getName()); + } + + protected User loadUser(String name) { + return loadUser((Principal)null, name); + } + + private User loadUser(final Principal prin, final String name) { + + //TODO Create a dynamic way to declare domains supported. + final long start = System.nanoTime(); + final boolean[] success = new boolean[]{false}; + +// new Exception("loadUser").printStackTrace(); + try { + return aaf.best(new Retryable>() { + @Override + public User code(Rcli client) throws CadiException, ConnectException, APIException { + Future fp = client.read("/authz/perms/user/"+name,aaf.permsDF); + + // In the meantime, lookup User, create if necessary + User user = getUser(name); + Principal p; + if(prin == null) { + p = new Principal() {// Create a holder for lookups + private String n = name; + public String getName() { + return n; + } + }; + } else { + p = prin; + } + + if(user==null) { + addUser(user = new User(p,aaf.userExpires)); // no password + } + + // OK, done all we can, now get content + if(fp.get(aaf.timeout)) { + success[0]=true; + Map newMap = user.newMap(); + boolean willLog = aaf.access.willLog(Level.DEBUG); + for(Perm perm : fp.value.getPerm()) { + user.add(newMap,new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction())); + if(willLog) { + aaf.access.log(Level.DEBUG, name,"has '",perm.getType(),'|',perm.getInstance(),'|',perm.getAction(),'\''); + } + } + user.setMap(newMap); + user.renewPerm(); + } else { + int code; + switch(code=fp.code()) { + case 401: + aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls"); + break; + default: + aaf.access.log(Access.Level.ERROR, code, fp.body()); + } + } + + return user; + } + }); + } catch (Exception e) { + aaf.access.log(e,"Calling","/authz/perms/user/"+name); + success[0]=false; + return null; + } finally { + float time = (System.nanoTime()-start)/1000000f; + aaf.access.log(Level.INFO, success[0]?"Loaded":"Load Failure",name,"from AAF in",time,"ms"); + } + } + + public Resp reload(User user) { + final String name = user.principal.getName(); + long start = System.nanoTime(); + boolean success = false; + try { + Future fp = aaf.client(AAFCon.AAF_LATEST_VERSION).read( + "/authz/perms/user/"+name, + aaf.permsDF + ); + + // OK, done all we can, now get content + if(fp.get(aaf.timeout)) { + success = true; + Map newMap = user.newMap(); + boolean willLog = aaf.access.willLog(Level.DEBUG); + for(Perm perm : fp.value.getPerm()) { + user.add(newMap, new AAFPermission(perm.getType(),perm.getInstance(),perm.getAction())); + if(willLog) { + aaf.access.log(Level.DEBUG, name,"has",perm.getType(),perm.getInstance(),perm.getAction()); + } + } + user.renewPerm(); + return Resp.REVALIDATED; + } else { + int code; + switch(code=fp.code()) { + case 401: + aaf.access.log(Access.Level.ERROR, code, "Unauthorized to make AAF calls"); + break; + default: + aaf.access.log(Access.Level.ERROR, code, fp.body()); + } + return Resp.UNVALIDATED; + } + } catch (Exception e) { + aaf.access.log(e,"Calling","/authz/perms/user/"+name); + return Resp.INACCESSIBLE; + } finally { + float time = (System.nanoTime()-start)/1000000f; + aaf.access.log(Level.AUDIT, success?"Reloaded":"Reload Failure",name,"from AAF in",time,"ms"); + } + } + + @Override + protected boolean isCorrectPermType(Permission pond) { + return pond instanceof AAFPermission; + } + + /* (non-Javadoc) + * @see com.att.cadi.Lur#createPerm(java.lang.String) + */ + @Override + public Permission createPerm(String p) { + String[] params = Split.split('|', p); + if(params.length==3) { + return new AAFPermission(params[0],params[1],params[2]); + } else { + return new LocalPermission(p); + } + } + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTaf.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTaf.java new file mode 100644 index 0000000..35df38f --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTaf.java @@ -0,0 +1,168 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.att.cadi.AbsUserCache; +import com.att.cadi.Access.Level; +import com.att.cadi.CachedPrincipal; +import com.att.cadi.CachedPrincipal.Resp; +import com.att.cadi.GetCred; +import com.att.cadi.Hash; +import com.att.cadi.Taf.LifeForm; +import com.att.cadi.User; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.client.Future; +import com.att.cadi.client.Rcli; +import com.att.cadi.principal.BasicPrincipal; +import com.att.cadi.principal.CachedBasicPrincipal; +import com.att.cadi.taf.HttpTaf; +import com.att.cadi.taf.TafResp; +import com.att.cadi.taf.TafResp.RESP; +import com.att.cadi.taf.basic.BasicHttpTafResp; + +public class AAFTaf extends AbsUserCache implements HttpTaf { +// private static final String INVALID_AUTH_TOKEN = "Invalid Auth Token"; +// private static final String AUTHENTICATING_SERVICE_UNAVAILABLE = "Authenticating Service unavailable"; + private AAFCon aaf; + private boolean warn; + + public AAFTaf(AAFCon con, boolean turnOnWarning) { + super(con.access,con.cleanInterval,con.highCount, con.usageRefreshTriggerCount); + aaf = con; + warn = turnOnWarning; + } + + public AAFTaf(AAFCon con, boolean turnOnWarning, AbsUserCache other) { + super(other); + aaf = con; + warn = turnOnWarning; + } + + public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) { + //TODO Do we allow just anybody to validate? + + // Note: Either Carbon or Silicon based LifeForms ok + String authz = req.getHeader("Authorization"); + if(authz != null && authz.startsWith("Basic ")) { + if(warn&&!req.isSecure())aaf.access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel"); + try { + CachedBasicPrincipal bp; + if(req.getUserPrincipal() instanceof CachedBasicPrincipal) { + bp = (CachedBasicPrincipal)req.getUserPrincipal(); + } else { + bp = new CachedBasicPrincipal(this,authz,aaf.getRealm(),aaf.userExpires); + } + // First try Cache + User usr = getUser(bp); + if(usr != null && usr.principal != null) { + if(usr.principal instanceof GetCred) { + if(Hash.isEqual(bp.getCred(),((GetCred)usr.principal).getCred())) { + return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by cached AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false); + } + } + } + + Miss miss = missed(bp.getName()); + if(miss!=null && !miss.mayContinue(bp.getCred())) { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "User/Pass Retry limit exceeded"), + RESP.FAIL,resp,aaf.getRealm(),true); + } + + Rcli userAAF = aaf.client(AAFCon.AAF_LATEST_VERSION).forUser(aaf.basicAuthSS(bp)); + Future fp = userAAF.read("/authn/basicAuth", "text/plain"); + if(fp.get(aaf.timeout)) { + if(usr!=null) { + usr.principal = bp; + } else { + addUser(new User(bp,aaf.userExpires)); + } + return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false); + } else { + // Note: AddMiss checks for miss==null, and is part of logic + boolean rv= addMiss(bp.getName(),bp.getCred()); + if(rv) { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "User/Pass combo invalid via AAF"), + RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true); + } else { + return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req, + "User/Pass combo invalid via AAF - Retry limit exceeded"), + RESP.FAIL,resp,aaf.getRealm(),true); + } + } + } catch (IOException e) { + String msg = buildMsg(null,req,"Invalid Auth Token"); + aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')'); + return new BasicHttpTafResp(aaf.access,null,msg, RESP.TRY_AUTHENTICATING, resp, aaf.getRealm(),true); + } catch (Exception e) { + String msg = buildMsg(null,req,"Authenticating Service unavailable"); + aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')'); + return new BasicHttpTafResp(aaf.access,null,msg, RESP.FAIL, resp, aaf.getRealm(),false); + } + } + return new BasicHttpTafResp(aaf.access,null,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),false); + } + + private String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) { + StringBuilder sb = new StringBuilder(); + for(Object s : msg) { + sb.append(s.toString()); + } + if(pr!=null) { + sb.append(" for "); + sb.append(pr.getName()); + } + sb.append(" from "); + sb.append(req.getRemoteAddr()); + sb.append(':'); + sb.append(req.getRemotePort()); + return sb.toString(); + } + + + + public Resp revalidate(CachedPrincipal prin) { + // !!!! TEST THIS.. Things may not be revalidated, if not BasicPrincipal + if(prin instanceof BasicPrincipal) { + Future fp; + try { + Rcli userAAF = aaf.client(AAFCon.AAF_LATEST_VERSION).forUser(aaf.transferSS(prin)); + fp = userAAF.read("/authn/basicAuth", "text/plain"); + return fp.get(aaf.timeout)?Resp.REVALIDATED:Resp.UNVALIDATED; + } catch (Exception e) { + aaf.access.log(e, "Cannot Revalidate",prin.getName()); + return Resp.INACCESSIBLE; + } + } + return Resp.NOT_MINE; + } + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTrustChecker.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTrustChecker.java new file mode 100644 index 0000000..3bc4faf --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AAFTrustChecker.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import javax.servlet.http.HttpServletRequest ; + +import com.att.cadi.Access; +import com.att.cadi.Lur; +import com.att.cadi.TrustChecker; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.config.Config; +import com.att.cadi.principal.TrustPrincipal; +import com.att.cadi.taf.TafResp; +import com.att.cadi.taf.TrustNotTafResp; +import com.att.cadi.taf.TrustTafResp; +import com.att.inno.env.Env; +import com.att.inno.env.util.Split; + +public class AAFTrustChecker implements TrustChecker { + private final String tag, id; + private final AAFPermission perm; + private Lur lur; + + /** + * + * Instance will be replaced by Identity + * @param lur + * + * @param tag + * @param perm + */ + public AAFTrustChecker(final Env env) { + tag = env.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN); + id = env.getProperty(Config.CADI_ALIAS,env.getProperty(Config.AAF_MECHID)); // share between components + String str = env.getProperty(Config.CADI_TRUST_PERM); + AAFPermission temp=null; + if(str!=null) { + String[] sp = Split.splitTrim('|', str); + if(sp.length==3) { + temp = new AAFPermission(sp[0],sp[1],sp[2]); + } + } + perm=temp; + } + + public AAFTrustChecker(final Access access) { + tag = access.getProperty(Config.CADI_USER_CHAIN_TAG, Config.CADI_USER_CHAIN); + id = access.getProperty(Config.CADI_ALIAS,access.getProperty(Config.AAF_MECHID,null)); // share between components + String str = access.getProperty(Config.CADI_TRUST_PERM,null); + AAFPermission temp=null; + if(str!=null) { + String[] sp = Split.splitTrim('|', str); + if(sp.length==3) { + temp = new AAFPermission(sp[0],sp[1],sp[2]); + } + } + perm=temp; + } + + /* (non-Javadoc) + * @see com.att.cadi.TrustChecker#setLur(com.att.cadi.Lur) + */ + @Override + public void setLur(Lur lur) { + this.lur = lur; + } + + @Override + public TafResp mayTrust(TafResp tresp, HttpServletRequest req) { + String user_info = req.getHeader(tag); + if(user_info !=null ) { + String[] info = Split.split(',', user_info); + if(info.length>0) { + String[] flds = Split.splitTrim(':',info[0]); + if(flds.length>3 && "AS".equals(flds[3])) { // is it set for "AS" + String pn = tresp.getPrincipal().getName(); + if(pn.equals(id) // We do trust our own App Components: if a trust entry is made with self, always accept + || lur.fish(tresp.getPrincipal(), perm)) { // Have Perm set by Config.CADI_TRUST_PERM + return new TrustTafResp(tresp, + new TrustPrincipal(tresp.getPrincipal(), flds[0]), + " " + flds[0] + " validated using " + flds[2] + " by " + flds[1] + ',' + ); + } else if(pn.equals(flds[0])) { // Ignore if same identity + return tresp; + } else { + return new TrustNotTafResp(tresp, tresp.getPrincipal().getName() + " requested trust as " + + flds[0] + ", but does not have Authorization"); + } + } + } + } + return tresp; + } + +} diff --git a/aaf/src/main/java/com/att/cadi/aaf/v2_0/AbsAAFLur.java b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AbsAAFLur.java new file mode 100644 index 0000000..8626588 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/aaf/v2_0/AbsAAFLur.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.aaf.v2_0; + +import java.net.URISyntaxException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.att.aft.dme2.api.DME2Exception; +import com.att.cadi.AbsUserCache; +import com.att.cadi.Access.Level; +import com.att.cadi.CachingLur; +import com.att.cadi.Permission; +import com.att.cadi.StrLur; +import com.att.cadi.Transmutate; +import com.att.cadi.User; +import com.att.cadi.config.Config; +import com.att.cadi.aaf.AAFPermission; +import com.att.cadi.aaf.AAFTransmutate; +import com.att.inno.env.APIException; +import com.att.inno.env.util.Split; + +public abstract class AbsAAFLur extends AbsUserCache implements StrLur, CachingLur { + protected static final byte[] BLANK_PASSWORD = new byte[0]; + protected static final Transmutate transmutate = new AAFTransmutate(); + private String[] debug = null; + public AAFCon aaf; + private String[] supports; + + public AbsAAFLur(AAFCon con) throws DME2Exception, URISyntaxException, APIException { + super(con.access, con.cleanInterval, con.highCount, con.usageRefreshTriggerCount); + aaf = con; + setLur(this); + supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*"); + } + + public AbsAAFLur(AAFCon con, AbsUserCache auc) throws DME2Exception, URISyntaxException, APIException { + super(auc); + aaf = con; + setLur(this); + supports = con.access.getProperty(Config.AAF_DOMAIN_SUPPORT, Config.AAF_DOMAIN_SUPPORT_DEF).split("\\s*:\\s*"); + } + + @Override + public void setDebug(String ids) { + this.debug = ids==null?null:Split.split(',', ids); + } + + protected abstract User loadUser(Principal bait); + protected abstract User loadUser(String name); + public final boolean supports(String userName) { + if(userName!=null) { + for(String s : supports) { + if(userName.endsWith(s)) + return true; + } + } + return false; + } + + protected abstract boolean isCorrectPermType(Permission pond); + + // This is where you build AAF CLient Code. Answer the question "Is principal "bait" in the "pond" + public boolean fish(Principal bait, Permission pond) { + return fish(bait.getName(), pond); + } + + public void fishAll(Principal bait, List perms) { + fishAll(bait.getName(),perms); + } + + // This is where you build AAF CLient Code. Answer the question "Is principal "bait" in the "pond" + public boolean fish(String bait, Permission pond) { + if(isDebug(bait)) { + boolean rv = false; + StringBuilder sb = new StringBuilder("Log for "); + sb.append(bait); + if(supports(bait)) { + User user = getUser(bait); + if(user==null) { + sb.append("\n\tUser is not in Cache"); + } else { + if(user.noPerms())sb.append("\n\tUser has no Perms"); + if(user.permExpired()) { + sb.append("\n\tUser's perm expired ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } else { + sb.append("\n\tUser's perm expires ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } + } + if(user==null || (user.noPerms() && user.permExpired())) { + user = loadUser(bait); + sb.append("\n\tloadUser called"); + } + if(user==null) { + sb.append("\n\tUser was not Loaded"); + } else if(user.contains(pond)) { + sb.append("\n\tUser contains "); + sb.append(pond.getKey()); + rv = true; + } else { + sb.append("\n\tUser does not contain "); + sb.append(pond.getKey()); + List perms = new ArrayList(); + user.copyPermsTo(perms); + for(Permission p : perms) { + sb.append("\n\t\t"); + sb.append(p.getKey()); + } + } + } else { + sb.append("AAF Lur does not support ["); + sb.append(bait); + sb.append("]"); + } + aaf.access.log(Level.INFO, sb); + return rv; + } else { + if(supports(bait)) { + User user = getUser(bait); + if(user==null || (user.noPerms() && user.permExpired())) { + user = loadUser(bait); + } + return user==null?false:user.contains(pond); + } + return false; + } + } + + public void fishAll(String bait, List perms) { + if(isDebug(bait)) { + StringBuilder sb = new StringBuilder("Log for "); + sb.append(bait); + if(supports(bait)) { + User user = getUser(bait); + if(user==null) { + sb.append("\n\tUser is not in Cache"); + } else { + if(user.noPerms())sb.append("\n\tUser has no Perms"); + if(user.permExpired()) { + sb.append("\n\tUser's perm expired ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } else { + sb.append("\n\tUser's perm expires ["); + sb.append(new Date(user.permExpires())); + sb.append(']'); + } + } + if(user==null || (user.noPerms() && user.permExpired())) { + user = loadUser(bait); + sb.append("\n\tloadUser called"); + } + if(user==null) { + sb.append("\n\tUser was not Loaded"); + } else { + sb.append("\n\tCopying Perms "); + user.copyPermsTo(perms); + for(Permission p : perms) { + sb.append("\n\t\t"); + sb.append(p.getKey()); + } + } + } else { + sb.append("AAF Lur does not support ["); + sb.append(bait); + sb.append("]"); + } + aaf.access.log(Level.INFO, sb); + } else { + if(supports(bait)) { + User user = getUser(bait); + if(user==null || (user.noPerms() && user.permExpired())) user = loadUser(bait); + if(user!=null) { + user.copyPermsTo(perms); + } + } + } + } + + @Override + public void remove(String user) { + super.remove(user); + } + + private boolean isDebug(String bait) { + if(debug!=null) { + if(debug.length==1 && "all".equals(debug[0]))return true; + for(String s : debug) { + if(s.equals(bait))return true; + } + } + return false; + } + /** + * This special case minimizes loops, avoids multiple Set hits, and calls all the appropriate Actions found. + * + * @param bait + * @param obj + * @param type + * @param instance + * @param actions + */ + public void fishOneOf(String bait, A obj, String type, String instance, List> actions) { + User user = getUser(bait); + if(user==null || (user.noPerms() && user.permExpired()))user = loadUser(bait); +// return user==null?false:user.contains(pond); + if(user!=null) { + ReuseAAFPermission perm = new ReuseAAFPermission(type,instance); + for(Action action : actions) { + perm.setAction(action.getName()); + if(user.contains(perm)) { + if(action.exec(obj))return; + } + } + } + } + + public static interface Action { + public String getName(); + /** + * Return false to continue, True to end now + * @return + */ + public boolean exec(A a); + } + + private class ReuseAAFPermission extends AAFPermission { + public ReuseAAFPermission(String type, String instance) { + super(type,instance,null); + } + + public void setAction(String s) { + action = s; + } + + /** + * This function understands that AAF Keys are hierarchical, :A:B:C, + * Cassandra follows a similar method, so we'll short circuit and do it more efficiently when there isn't a first hit + * @return + */ + } +} diff --git a/aaf/src/main/java/com/att/cadi/cm/ArtifactDir.java b/aaf/src/main/java/com/att/cadi/cm/ArtifactDir.java new file mode 100644 index 0000000..39854d6 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/ArtifactDir.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.att.cadi.CadiException; +import com.att.cadi.Symm; +import com.att.cadi.config.Config; +import com.att.cadi.util.Chmod; +import com.att.inno.env.Trans; +import com.att.inno.env.util.Chrono; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public abstract class ArtifactDir implements PlaceArtifact { + + protected static final String C_R = "\n"; + protected File dir; + private List encodeds = new ArrayList(); + + private Symm symm; + // This checks for multiple passes of Dir on the same objects. Run clear after done. + protected static Map processed = new HashMap(); + + + /** + * Note: Derived Classes should ALWAYS call "super.place(cert,arti)" first, and + * then "placeProperties(arti)" just after they implement + */ + @Override + public final boolean place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + validate(arti); + + try { + // Obtain/setup directory as required + dir = new File(arti.getDir()); + if(processed.get("dir")==null) { + if(!dir.exists()) { + Chmod.to755.chmod(dir); + if(!dir.mkdirs()) { + throw new CadiException("Could not create " + dir); + } + } + + // Also place cm_url and Host Name + addProperty(Config.CM_URL,trans.getProperty(Config.CM_URL)); + addProperty(Config.HOSTNAME,arti.getMachine()); + //addProperty(Config.AAF_ENV,certInfo.getEnv()); + // Obtain Issuers + boolean first = true; + StringBuilder issuers = new StringBuilder(); +// for(String dn : certInfo.getCaIssuerDNs()) { +// if(first) { +// first=false; +// } else { +// issuers.append(':'); +// } +// issuers.append(dn); +// } + addProperty(Config.CADI_X509_ISSUERS,issuers.toString()); + } + symm = (Symm)processed.get("symm"); + if(symm==null) { + // CADI Key Gen + File f = new File(dir,arti.getAppName() + ".keyfile"); + if(!f.exists()) { + write(f,Chmod.to400,Symm.baseCrypt().keygen()); + } + symm = Symm.obtain(f); + + addEncProperty("ChallengePassword", certInfo.getChallenge()); + + processed.put("symm",symm); + } + + _place(trans, certInfo,arti); + + placeProperties(arti); + + processed.put("dir",dir); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } + + /** + * Derived Classes implement this instead, so Dir can process first, and write any Properties last + * @param cert + * @param arti + * @return + * @throws CadiException + */ + protected abstract boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException; + + protected void addProperty(String tag, String value) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append(tag); + sb.append('='); + sb.append(value); + encodeds.add(sb.toString()); + } + + protected void addEncProperty(String tag, String value) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append(tag); + sb.append('='); + sb.append("enc:???"); + sb.append(symm.enpass(value)); + encodeds.add(sb.toString()); + } + + protected void write(File f, Chmod c, String ... data) throws IOException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + PrintStream ps = new PrintStream(fos); + try { + for(String s : data) { + ps.print(s); + } + } finally { + ps.close(); + c.chmod(f); + } + } + + protected void write(File f, Chmod c, byte[] bytes) throws IOException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + try { + fos.write(bytes); + } finally { + fos.close(); + c.chmod(f); + } + } + + protected void write(File f, Chmod c, KeyStore ks, char[] pass ) throws IOException, CadiException { + f.setWritable(true,true); + + FileOutputStream fos = new FileOutputStream(f); + try { + ks.store(fos, pass); + } catch (Exception e) { + throw new CadiException(e); + } finally { + fos.close(); + c.chmod(f); + } + } + + + private void validate(Artifact a) throws CadiException { + StringBuilder sb = new StringBuilder(); + if(a.getDir()==null) { + sb.append("File Artifacts require a path"); + } + + if(a.getAppName()==null) { + if(sb.length()>0) { + sb.append('\n'); + } + sb.append("File Artifacts require an AAF Namespace"); + } + + if(sb.length()>0) { + throw new CadiException(sb.toString()); + } + } + + private boolean placeProperties(Artifact arti) throws CadiException { + if(encodeds.size()==0) { + return true; + } + boolean first=processed.get("dir")==null; + try { + File f = new File(dir,arti.getAppName()+".props"); + if(f.exists()) { + if(first) { + f.delete(); + } else { + f.setWritable(true); + } + } + // Append if not first + PrintWriter pw = new PrintWriter(new FileWriter(f,!first)); + + // Write a Header + if(first) { + for(int i=0;i<60;++i) { + pw.print('#'); + } + pw.println(); + pw.println("# Properties Generated by AT&T Certificate Manager"); + pw.print("# by "); + pw.println(System.getProperty("user.name")); + pw.print("# on "); + pw.println(Chrono.dateStamp()); + pw.println("# @copyright 2016, AT&T"); + for(int i=0;i<60;++i) { + pw.print('#'); + } + pw.println(); + for(String prop : encodeds) { + if( prop.startsWith("cm_") + || prop.startsWith(Config.HOSTNAME) + || prop.startsWith(Config.AAF_ENV)) { + pw.println(prop); + } + } + } + + try { + for(String prop : encodeds) { + if(prop.startsWith("cadi")) { + pw.println(prop); + } + } + } finally { + pw.close(); + } + Chmod.to644.chmod(f); + + if(first) { + // Challenge + f = new File(dir,arti.getAppName()+".chal"); + if(f.exists()) { + f.delete(); + } + pw = new PrintWriter(new FileWriter(f)); + try { + for(String prop : encodeds) { + if(prop.startsWith("Challenge")) { + pw.println(prop); + } + } + } finally { + pw.close(); + } + Chmod.to400.chmod(f); + } + } catch(Exception e) { + throw new CadiException(e); + } + return true; + } + + public static void clear() { + processed.clear(); + } + +} diff --git a/aaf/src/main/java/com/att/cadi/cm/CertException.java b/aaf/src/main/java/com/att/cadi/cm/CertException.java new file mode 100644 index 0000000..a2694ce --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/CertException.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +public class CertException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1373028409048516401L; + + public CertException() { + } + + public CertException(String message) { + super(message); + } + + public CertException(Throwable cause) { + super(cause); + } + + public CertException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/aaf/src/main/java/com/att/cadi/cm/CmAgent.java b/aaf/src/main/java/com/att/cadi/cm/CmAgent.java new file mode 100644 index 0000000..424ef83 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/CmAgent.java @@ -0,0 +1,711 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import com.att.cadi.PropAccess; +import com.att.cadi.Symm; +import com.att.cadi.aaf.client.ErrMessage; +import com.att.cadi.aaf.v2_0.AAFCon; +import com.att.cadi.aaf.v2_0.AAFConHttp; +import com.att.cadi.client.Future; +import com.att.cadi.config.Config; +import com.att.cadi.http.HBasicAuthSS; +import com.att.cadi.sso.AAFSSO; +import com.att.inno.env.Data.TYPE; +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; +import com.att.inno.env.util.Chrono; +import com.att.inno.env.util.Split; +import com.att.rosetta.env.RosettaDF; +import com.att.rosetta.env.RosettaEnv; + +import certman.v1_0.Artifacts; +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; +import certman.v1_0.CertificateRequest; + +public class CmAgent { + private static final String PRINT = "print"; + private static final String FILE = "file"; + private static final String PKCS12 = "pkcs12"; + private static final String JKS = "jks"; + private static final String SCRIPT="script"; + + private static final String CM_VER = "1.0"; + public static final int PASS_SIZE = 24; + private static int TIMEOUT; + + private static RosettaDF reqDF; + private static RosettaDF certDF; + private static RosettaDF artifactsDF; + private static ErrMessage errMsg; + private static Map placeArtifact; + private static RosettaEnv env; + + public static void main(String[] args) { + int exitCode = 0; + try { + AAFSSO aafsso = new AAFSSO(args); + if(aafsso.loginOnly()) { + aafsso.setLogDefault(); + aafsso.writeFiles(); + System.out.println("AAF SSO information created in ~/.aaf"); + } else { + PropAccess access = aafsso.access(); + env = new RosettaEnv(access.getProperties()); + Deque cmds = new ArrayDeque(); + for(String p : args) { + if(p.indexOf('=')<0) { + cmds.add(p); + } + } + + if(cmds.size()==0) { + aafsso.setLogDefault(); + System.out.println("Usage: java -jar cmd []*"); + System.out.println(" create []"); + System.out.println(" read []"); + System.out.println(" update []"); + System.out.println(" delete []"); + System.out.println(" copy [,]*"); + System.out.println(" place []"); + System.out.println(" showpass []"); + System.out.println(" check []"); + System.exit(1); + } + + TIMEOUT = Integer.parseInt(env.getProperty(Config.AAF_CONN_TIMEOUT, "5000")); + + reqDF = env.newDataFactory(CertificateRequest.class); + artifactsDF = env.newDataFactory(Artifacts.class); + certDF = env.newDataFactory(CertInfo.class); + errMsg = new ErrMessage(env); + + placeArtifact = new HashMap(); + placeArtifact.put(JKS, new PlaceArtifactInKeystore(JKS)); + placeArtifact.put(PKCS12, new PlaceArtifactInKeystore(PKCS12)); + placeArtifact.put(FILE, new PlaceArtifactInFiles()); + placeArtifact.put(PRINT, new PlaceArtifactOnStream(System.out)); + placeArtifact.put(SCRIPT, new PlaceArtifactScripts()); + + Trans trans = env.newTrans(); + try { + // show Std out again + aafsso.setLogDefault(); + aafsso.setStdErrDefault(); + + // if CM_URL can be obtained, add to sso.props, if written + String cm_url = getProperty(access,env,false, Config.CM_URL,Config.CM_URL+": "); + if(cm_url!=null) { + aafsso.addProp(Config.CM_URL, cm_url); + } + aafsso.writeFiles(); + + AAFCon aafcon = new AAFConHttp(access,Config.CM_URL); + + String cmd = cmds.removeFirst(); + if("place".equals(cmd)) { + placeCerts(trans,aafcon,cmds); + } else if("create".equals(cmd)) { + createArtifact(trans, aafcon,cmds); + } else if("read".equals(cmd)) { + readArtifact(trans, aafcon, cmds); + } else if("copy".equals(cmd)) { + copyArtifact(trans, aafcon, cmds); + } else if("update".equals(cmd)) { + updateArtifact(trans, aafcon, cmds); + } else if("delete".equals(cmd)) { + deleteArtifact(trans, aafcon, cmds); + } else if("showpass".equals(cmd)) { + showPass(trans,aafcon,cmds); + } else if("check".equals(cmd)) { + try { + exitCode = check(trans,aafcon,cmds); + } catch (Exception e) { + exitCode = 1; + throw e; + } + } else { + AAFSSO.cons.printf("Unknown command \"%s\"\n", cmd); + } + } finally { + StringBuilder sb = new StringBuilder(); + trans.auditTrail(4, sb, Trans.REMOTE); + if(sb.length()>0) { + trans.info().log("Trans Info\n",sb); + } + } + aafsso.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + if(exitCode!=0) { + System.exit(exitCode); + } + } + + private static String getProperty(PropAccess pa, Env env, boolean secure, String tag, String prompt, Object ... def) { + String value; + if((value=pa.getProperty(tag))==null) { + if(secure) { + value = new String(AAFSSO.cons.readPassword(prompt, def)); + } else { + value = AAFSSO.cons.readLine(prompt,def).trim(); + } + if(value!=null) { + if(value.length()>0) { + pa.setProperty(tag,value); + env.setProperty(tag,value); + } else if(def.length==1) { + value=def[0].toString(); + pa.setProperty(tag,value); + env.setProperty(tag,value); + } + } + } + return value; + } + + private static String mechID(Deque cmds) { + if(cmds.size()<1) { + String alias = env.getProperty(Config.CADI_ALIAS); + return alias!=null?alias:AAFSSO.cons.readLine("MechID: "); + } + return cmds.removeFirst(); + } + + private static String machine(Deque cmds) throws UnknownHostException { + if(cmds.size()>0) { + return cmds.removeFirst(); + } else { + String mach = env.getProperty(Config.HOSTNAME); + return mach!=null?mach:InetAddress.getLocalHost().getHostName(); + } + } + + private static String[] machines(Deque cmds) { + String machines; + if(cmds.size()>0) { + machines = cmds.removeFirst(); + } else { + machines = AAFSSO.cons.readLine("Machines (sep by ','): "); + } + return Split.split(',', machines); + } + + private static void createArtifact(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + Artifacts artifacts = new Artifacts(); + Artifact arti = new Artifact(); + artifacts.getArtifact().add(arti); + arti.setMechid(mechID!=null?mechID:AAFSSO.cons.readLine("MechID: ")); + arti.setMachine(machine!=null?machine:AAFSSO.cons.readLine("Machine (%s): ",InetAddress.getLocalHost().getHostName())); + arti.setCa(AAFSSO.cons.readLine("CA: (%s): ","aaf")); + + String resp = AAFSSO.cons.readLine("Types [file,jks,script] (%s): ", "jks"); + for(String s : Split.splitTrim(',', resp)) { + arti.getType().add(s); + } + // Always do Script + if(!resp.contains(SCRIPT)) { + arti.getType().add(SCRIPT); + } + + // Note: Sponsor is set on Creation by CM + String configRootName = AAFCon.reverseDomain(arti.getMechid()); + arti.setAppName(AAFSSO.cons.readLine("Namespace (%s): ",configRootName)); + arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", System.getProperty("user.dir"))); + arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", System.getProperty("user.name"))); + arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renewal Days (%s):", "30"))); + arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (mailto owner):", ""))); + + TimeTaken tt = trans.start("Create Artifact", Env.REMOTE); + try { + Future future = aafcon.client(CM_VER).create("/cert/artifacts", artifactsDF, artifacts); + if(future.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",arti.getMechid(), arti.getMachine()); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + + private static String toNotification(String notification) { + if(notification==null) { + notification=""; + } else if(notification.length()>0) { + if(notification.indexOf(':')<0) { + notification = "mailto:" + notification; + } + } + return notification; + } + + + private static void readArtifact(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Read Artifact", Env.SUB); + try { + Future future = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + + if(future.get(TIMEOUT)) { + boolean printed = false; + for(Artifact a : future.value.getArtifact()) { + AAFSSO.cons.printf("MechID: %s\n",a.getMechid()); + AAFSSO.cons.printf(" Sponsor: %s\n",a.getSponsor()); + AAFSSO.cons.printf("Machine: %s\n",a.getMachine()); + AAFSSO.cons.printf("CA: %s\n",a.getCa()); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String t : a.getType()) { + if(first) {first=false;} + else{sb.append(',');} + sb.append(t); + } + AAFSSO.cons.printf("Types: %s\n",sb); + AAFSSO.cons.printf("Namespace: %s\n",a.getAppName()); + AAFSSO.cons.printf("Directory: %s\n",a.getDir()); + AAFSSO.cons.printf("O/S User: %s\n",a.getOsUser()); + AAFSSO.cons.printf("Renew Days: %d\n",a.getRenewDays()); + AAFSSO.cons.printf("Notification %s\n",a.getNotification()); + printed = true; + } + if(!printed) { + AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine); + } + } else { + trans.error().log(errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + + private static void copyArtifact(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + String[] newmachs = machines(cmds); + if(newmachs==null || newmachs == null) { + trans.error().log("No machines listed to copy to"); + } else { + TimeTaken tt = trans.start("Copy Artifact", Env.REMOTE); + try { + Future future = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + + if(future.get(TIMEOUT)) { + boolean printed = false; + for(Artifact a : future.value.getArtifact()) { + for(String m : newmachs) { + a.setMachine(m); + Future fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, future.value); + if(fup.get(TIMEOUT)) { + trans.info().printf("Copy of %s %s successful to %s",mechID,machine,m); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(fup)); + } + + printed = true; + } + } + if(!printed) { + AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine); + } + } else { + trans.error().log(errMsg.toMsg(future)); + } + } finally { + tt.done(); + } + } + } + + private static void updateArtifact(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Update Artifact", Env.REMOTE); + try { + Future fread = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + + if(fread.get(TIMEOUT)) { + Artifacts artifacts = new Artifacts(); + for(Artifact a : fread.value.getArtifact()) { + Artifact arti = new Artifact(); + artifacts.getArtifact().add(arti); + + AAFSSO.cons.printf("For %s on %s\n", a.getMechid(),a.getMachine()); + arti.setMechid(a.getMechid()); + arti.setMachine(a.getMachine()); + arti.setCa(AAFSSO.cons.readLine("CA: (%s): ",a.getCa())); + StringBuilder sb = new StringBuilder(); + boolean first = true; + for(String t : a.getType()) { + if(first) {first=false;} + else{sb.append(',');} + sb.append(t); + } + + String resp = AAFSSO.cons.readLine("Types [file,jks,pkcs12] (%s): ", sb); + for(String s : Split.splitTrim(',', resp)) { + arti.getType().add(s); + } + // Always do Script + if(!resp.contains(SCRIPT)) { + arti.getType().add(SCRIPT); + } + + // Note: Sponsor is set on Creation by CM + arti.setAppName(AAFSSO.cons.readLine("Namespace (%s): ",a.getAppName())); + arti.setDir(AAFSSO.cons.readLine("Directory (%s): ", a.getDir())); + arti.setOsUser(AAFSSO.cons.readLine("OS User (%s): ", a.getOsUser())); + arti.setRenewDays(Integer.parseInt(AAFSSO.cons.readLine("Renew Days (%s):", a.getRenewDays()))); + arti.setNotification(toNotification(AAFSSO.cons.readLine("Notification (%s):", a.getNotification()))); + + } + if(artifacts.getArtifact().size()==0) { + AAFSSO.cons.printf("Artifact for %s %s does not exist", mechID, machine); + } else { + Future fup = aafcon.client(CM_VER).update("/cert/artifacts", artifactsDF, artifacts); + if(fup.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",mechID,machine); + } else { + trans.error().printf("Call to AAF Certman failed, %s", + errMsg.toMsg(fup)); + } + } + } else { + trans.error().printf("Call to AAF Certman failed, %s %s, %s", + errMsg.toMsg(fread),mechID,machine); + } + } finally { + tt.done(); + } + } + + private static void deleteArtifact(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechid = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Delete Artifact", Env.REMOTE); + try { + Future future = aafcon.client(CM_VER) + .delete("/cert/artifacts/"+mechid+"/"+machine,"application/json" ); + + if(future.get(TIMEOUT)) { + trans.info().printf("Call to AAF Certman successful %s, %s",mechid,machine); + } else { + trans.error().printf("Call to AAF Certman failed, %s %s, %s", + errMsg.toMsg(future),mechid,machine); + } + } finally { + tt.done(); + } + } + + + + private static boolean placeCerts(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + boolean rv = false; + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Place Artifact", Env.REMOTE); + try { + Future acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + if(acf.get(TIMEOUT)) { + // Have to wait for JDK 1.7 source... + //switch(artifact.getType()) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("===> There are no artifacts for %s %s", mechID, machine); + } else { + for(Artifact a : acf.value.getArtifact()) { + String osID = System.getProperty("user.name"); + if(a.getOsUser().equals(osID)) { + CertificateRequest cr = new CertificateRequest(); + cr.setMechid(a.getMechid()); + cr.setSponsor(a.getSponsor()); + cr.getFqdns().add(a.getMachine()); + Future f = aafcon.client(CM_VER) + .setQueryParams("withTrust") + .updateRespondString("/cert/" + a.getCa(),reqDF, cr); + if(f.get(TIMEOUT)) { + CertInfo capi = certDF.newData().in(TYPE.JSON).load(f.body()).asObject(); + for(String type : a.getType()) { + PlaceArtifact pa = placeArtifact.get(type); + if(pa!=null) { + if(rv = pa.place(trans, capi, a)) { + notifyPlaced(a,rv); + } + } + } + // Cover for the above multiple pass possibilities with some static Data, then clear per Artifact + } else { + trans.error().log(errMsg.toMsg(f)); + } + } else { + trans.error().log("You must be OS User \"" + a.getOsUser() +"\" to place Certificates on this box"); + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + } + } finally { + tt.done(); + } + return rv; + } + + private static void notifyPlaced(Artifact a, boolean rv) { + + + } + + private static void showPass(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Show Password", Env.REMOTE); + try { + Future acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + if(acf.get(TIMEOUT)) { + // Have to wait for JDK 1.7 source... + //switch(artifact.getType()) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine); + } else { + String id = aafcon.defID(); + boolean allowed; + for(Artifact a : acf.value.getArtifact()) { + allowed = id!=null && (id.equals(a.getSponsor()) || + (id.equals(a.getMechid()) + && aafcon.securityInfo().defSS.getClass().isAssignableFrom(HBasicAuthSS.class))); + if(!allowed) { + Future pf = aafcon.client(CM_VER).read("/cert/may/" + + a.getAppName() + ".certman|"+a.getCa()+"|showpass","*/*"); + if(pf.get(TIMEOUT)) { + allowed = true; + } else { + trans.error().log(errMsg.toMsg(pf)); + } + } + if(allowed) { + File dir = new File(a.getDir()); + Properties props = new Properties(); + FileInputStream fis = new FileInputStream(new File(dir,a.getAppName()+".props")); + try { + props.load(fis); + fis.close(); + fis = new FileInputStream(new File(dir,a.getAppName()+".chal")); + props.load(fis); + } finally { + fis.close(); + } + + File f = new File(dir,a.getAppName()+".keyfile"); + if(f.exists()) { + Symm symm = Symm.obtain(f); + + for(Iterator> iter = props.entrySet().iterator(); iter.hasNext();) { + Entry en = iter.next(); + if(en.getValue().toString().startsWith("enc:???")) { + System.out.printf("%s=%s\n", en.getKey(), symm.depass(en.getValue().toString())); + } + } + } else { + trans.error().printf("%s.keyfile must exist to read passwords for %s on %s", + f.getAbsolutePath(),a.getMechid(), a.getMachine()); + } + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + } + } finally { + tt.done(); + } + + } + + + /** + * Check returns Error Codes, so that Scripts can know what to do + * + * 0 - Check Complete, nothing to do + * 1 - General Error + * 2 - Error for specific Artifact - read check.msg + * 10 - Certificate Updated - check.msg is email content + * + * @param trans + * @param aafcon + * @param cmds + * @return + * @throws Exception + */ + private static int check(Trans trans, AAFCon aafcon, Deque cmds) throws Exception { + int exitCode=1; + String mechID = mechID(cmds); + String machine = machine(cmds); + + TimeTaken tt = trans.start("Check Certificate", Env.REMOTE); + try { + + Future acf = aafcon.client(CM_VER) + .read("/cert/artifacts/"+mechID+'/'+machine, artifactsDF); + if(acf.get(TIMEOUT)) { + // Have to wait for JDK 1.7 source... + //switch(artifact.getType()) { + if(acf.value.getArtifact()==null || acf.value.getArtifact().isEmpty()) { + AAFSSO.cons.printf("No Artifacts found for %s on %s", mechID, machine); + } else { + String id = aafcon.defID(); + GregorianCalendar now = new GregorianCalendar(); + for(Artifact a : acf.value.getArtifact()) { + if(id.equals(a.getMechid())) { + File dir = new File(a.getDir()); + Properties props = new Properties(); + FileInputStream fis = new FileInputStream(new File(dir,a.getAppName()+".props")); + try { + props.load(fis); + } finally { + fis.close(); + } + + String prop; + File f; + + if((prop=props.getProperty(Config.CADI_KEYFILE))==null || + !(f=new File(prop)).exists()) { + trans.error().printf("Keyfile must exist to check Certificates for %s on %s", + a.getMechid(), a.getMachine()); + } else { + String ksf = props.getProperty(Config.CADI_KEYSTORE); + String ksps = props.getProperty(Config.CADI_KEYSTORE_PASSWORD); + if(ksf==null || ksps == null) { + trans.error().printf("Properties %s and %s must exist to check Certificates for %s on %s", + Config.CADI_KEYSTORE, Config.CADI_KEYSTORE_PASSWORD,a.getMechid(), a.getMachine()); + } else { + KeyStore ks = KeyStore.getInstance("JKS"); + Symm symm = Symm.obtain(f); + + fis = new FileInputStream(ksf); + try { + ks.load(fis,symm.depass(ksps).toCharArray()); + } finally { + fis.close(); + } + X509Certificate cert = (X509Certificate)ks.getCertificate(mechID); + String msg = null; + + if(cert==null) { + msg = String.format("X509Certificate does not exist for %s on %s in %s", + a.getMechid(), a.getMachine(), ksf); + trans.error().log(msg); + exitCode = 2; + } else { + GregorianCalendar renew = new GregorianCalendar(); + renew.setTime(cert.getNotAfter()); + renew.add(GregorianCalendar.DAY_OF_MONTH,-1*a.getRenewDays()); + if(renew.after(now)) { + msg = String.format("X509Certificate for %s on %s has been checked on %s. It expires on %s; it will not be renewed until %s.\n", + a.getMechid(), a.getMachine(),Chrono.dateOnlyStamp(now),cert.getNotAfter(),Chrono.dateOnlyStamp(renew)); + trans.info().log(msg); + exitCode = 0; // OK + } else { + trans.info().printf("X509Certificate for %s on %s expiration, %s, needs Renewal.\n", + a.getMechid(), a.getMachine(),cert.getNotAfter()); + cmds.offerLast(mechID); + cmds.offerLast(machine); + if(placeCerts(trans,aafcon,cmds)) { + msg = String.format("X509Certificate for %s on %s has been renewed. Ensure services using are refreshed.\n", + a.getMechid(), a.getMachine()); + exitCode = 10; // Refreshed + } else { + msg = String.format("X509Certificate for %s on %s attempted renewal, but failed. Immediate Investigation is required!\n", + a.getMechid(), a.getMachine()); + exitCode = 1; // Error Renewing + } + } + } + if(msg!=null) { + FileOutputStream fos = new FileOutputStream(a.getDir()+'/'+a.getAppName()+".msg"); + try { + fos.write(msg.getBytes()); + } finally { + fos.close(); + } + } + } + + } + } + } + } + } else { + trans.error().log(errMsg.toMsg(acf)); + exitCode=1; + } + } finally { + tt.done(); + } + return exitCode; + } + +} + + + + diff --git a/aaf/src/main/java/com/att/cadi/cm/Factory.java b/aaf/src/main/java/com/att/cadi/cm/Factory.java new file mode 100644 index 0000000..178cc81 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/Factory.java @@ -0,0 +1,449 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Collection; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +import com.att.cadi.Symm; +import com.att.inno.env.Env; +import com.att.inno.env.TimeTaken; +import com.att.inno.env.Trans; + +public class Factory { + private static final String PRIVATE_KEY_HEADER = "PRIVATE KEY"; + public static final String KEY_ALGO = "RSA"; + public static final String SIG_ALGO = "SHA256withRSA"; + + public static final int KEY_LENGTH = 2048; + private static final KeyPairGenerator keygen; + private static final KeyFactory keyFactory; + private static final CertificateFactory certificateFactory; + private static final SecureRandom random; + + + private static final Symm base64 = Symm.base64.copy(64); + + static { + random = new SecureRandom(); + KeyPairGenerator tempKeygen; + try { + tempKeygen = KeyPairGenerator.getInstance(KEY_ALGO);//,"BC"); + tempKeygen.initialize(KEY_LENGTH, random); + } catch (NoSuchAlgorithmException e) { + tempKeygen = null; + e.printStackTrace(System.err); + } + keygen = tempKeygen; + + KeyFactory tempKeyFactory; + try { + tempKeyFactory=KeyFactory.getInstance(KEY_ALGO);//,"BC" + } catch (NoSuchAlgorithmException e) { + tempKeyFactory = null; + e.printStackTrace(System.err); + }; + keyFactory = tempKeyFactory; + + CertificateFactory tempCertificateFactory; + try { + tempCertificateFactory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + tempCertificateFactory = null; + e.printStackTrace(System.err); + } + certificateFactory = tempCertificateFactory; + + + } + + + public static KeyPair generateKeyPair(Trans trans) { + TimeTaken tt; + if(trans!=null) { + tt = trans.start("Generate KeyPair", Env.SUB); + } else { + tt = null; + } + try { + return keygen.generateKeyPair(); + } finally { + if(tt!=null) { + tt.done(); + } + } + } + + private static final String LINE_END = "-----\n"; + + protected static String textBuilder(String kind, byte[] bytes) throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("-----BEGIN "); + sb.append(kind); + sb.append(LINE_END); + + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + base64.encode(bais, baos); + sb.append(new String(baos.toByteArray())); + + if(sb.charAt(sb.length()-1)!='\n') { + sb.append('\n'); + } + sb.append("-----END "); + sb.append(kind); + sb.append(LINE_END); + return sb.toString(); + } + + public static PrivateKey toPrivateKey(Trans trans, String pk) throws IOException, CertException { + byte[] bytes = decode(new StringReader(pk)); + return toPrivateKey(trans, bytes); + } + + public static PrivateKey toPrivateKey(Trans trans, byte[] bytes) throws IOException, CertException { + TimeTaken tt=trans.start("Reconstitute Private Key", Env.SUB); + try { + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); + } catch (InvalidKeySpecException e) { + throw new CertException("Translating Private Key from PKCS8 KeySpec",e); + } finally { + tt.done(); + } + } + + public static PrivateKey toPrivateKey(Trans trans, File file) throws IOException, CertException { + TimeTaken tt = trans.start("Decode Private Key File", Env.SUB); + try { + return toPrivateKey(trans,decode(file)); + }finally { + tt.done(); + } + } + + public static String toString(Trans trans, PrivateKey pk) throws IOException { +// PKCS8EncodedKeySpec pemContents = new PKCS8EncodedKeySpec(pk.getEncoded()); + trans.debug().log("Private Key to String"); + return textBuilder(PRIVATE_KEY_HEADER,pk.getEncoded()); + } + + public static PublicKey toPublicKey(Trans trans, String pk) throws IOException { + TimeTaken tt = trans.start("Reconstitute Public Key", Env.SUB); + try { + ByteArrayInputStream bais = new ByteArrayInputStream(pk.getBytes()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Symm.base64noSplit.decode(bais, baos); + + return keyFactory.generatePublic(new X509EncodedKeySpec(baos.toByteArray())); + } catch (InvalidKeySpecException e) { + trans.error().log(e,"Translating Public Key from X509 KeySpec"); + return null; + } finally { + tt.done(); + } + } + + public static String toString(Trans trans, PublicKey pk) throws IOException { + trans.debug().log("Public Key to String"); + return textBuilder("PUBLIC KEY",pk.getEncoded()); + } + + public static Collection toX509Certificate(String x509) throws CertificateException { + return toX509Certificate(x509.getBytes()); + } + + public static Collection toX509Certificate(List x509s) throws CertificateException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + for(String x509 : x509s) { + baos.write(x509.getBytes()); + } + } catch (IOException e) { + throw new CertificateException(e); + } + return toX509Certificate(new ByteArrayInputStream(baos.toByteArray())); + } + + public static Collection toX509Certificate(byte[] x509) throws CertificateException { + return certificateFactory.generateCertificates(new ByteArrayInputStream(x509)); + } + + public static Collection toX509Certificate(Trans trans, File file) throws CertificateException, FileNotFoundException { + FileInputStream fis = new FileInputStream(file); + try { + return toX509Certificate(fis); + } finally { + try { + fis.close(); + } catch (IOException e) { + throw new CertificateException(e); + } + } + } + + public static Collection toX509Certificate(InputStream is) throws CertificateException { + return certificateFactory.generateCertificates(is); + } + + public static String toString(Trans trans, Certificate cert) throws IOException, CertException { + if(trans.debug().isLoggable()) { + StringBuilder sb = new StringBuilder("Certificate to String"); + if(cert instanceof X509Certificate) { + sb.append(" - "); + sb.append(((X509Certificate)cert).getSubjectDN()); + } + trans.debug().log(sb); + } + try { + if(cert==null) { + throw new CertException("Certificate not built"); + } + return textBuilder("CERTIFICATE",cert.getEncoded()); + } catch (CertificateEncodingException e) { + throw new CertException(e); + } + } + + public static Cipher pkCipher() throws NoSuchAlgorithmException, NoSuchPaddingException { + return Cipher.getInstance(KEY_ALGO); + } + + public static Cipher pkCipher(Key key, boolean encrypt) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance(KEY_ALGO); + cipher.init(encrypt?Cipher.ENCRYPT_MODE:Cipher.DECRYPT_MODE,key); + return cipher; + } + + public static byte[] strip(Reader rdr) throws IOException { + BufferedReader br = new BufferedReader(rdr); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String line; + while((line=br.readLine())!=null) { + if(line.length()>0 && + !line.startsWith("-----") && + line.indexOf(':')<0) { // Header elements + baos.write(line.getBytes()); + } + } + return baos.toByteArray(); + } + + public static class StripperInputStream extends InputStream { + private Reader created; + private BufferedReader br; + private int idx; + private String line; + + public StripperInputStream(Reader rdr) { + if(rdr instanceof BufferedReader) { + br = (BufferedReader)rdr; + } else { + br = new BufferedReader(rdr); + } + created = null; + } + + public StripperInputStream(File file) throws FileNotFoundException { + this(new FileReader(file)); + created = br; + } + + public StripperInputStream(InputStream is) throws FileNotFoundException { + this(new InputStreamReader(is)); + created = br; + } + + @Override + public int read() throws IOException { + if(line==null || idx>=line.length()) { + while((line=br.readLine())!=null) { + if(line.length()>0 && + !line.startsWith("-----") && + line.indexOf(':')<0) { // Header elements + break; + } + } + + if(line==null) { + return -1; + } + idx = 0; + } + return line.charAt(idx++); + } + + /* (non-Javadoc) + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + if(created!=null) { + created.close(); + } + } + } + + public static class Base64InputStream extends InputStream { + private InputStream created; + private InputStream is; + private byte trio[]; + private byte duo[]; + private int idx; + + + public Base64InputStream(File file) throws FileNotFoundException { + this(new FileInputStream(file)); + created = is; + } + + public Base64InputStream(InputStream is) throws FileNotFoundException { + this.is = is; + trio = new byte[3]; + idx = 4; + } + + @Override + public int read() throws IOException { + if(duo==null || idx>=duo.length) { + int read = is.read(trio); + if(read==-1) { + return -1; + } + duo = Symm.base64.decode(trio); + if(duo==null || duo.length==0) { + return -1; + } + idx=0; + } + + return duo[idx++]; + } + + /* (non-Javadoc) + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException { + if(created!=null) { + created.close(); + } + } + } + + public static byte[] decode(byte[] bytes) throws IOException { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Symm.base64.decode(bais, baos); + return baos.toByteArray(); + } + + public static byte[] decode(File f) throws IOException { + FileReader fr = new FileReader(f); + try { + return Factory.decode(fr); + } finally { + fr.close(); + } + + } + public static byte[] decode(Reader rdr) throws IOException { + return decode(strip(rdr)); + } + + + public static byte[] binary(File file) throws IOException { + DataInputStream dis = new DataInputStream(new FileInputStream(file)); + try { + byte[] bytes = new byte[(int)file.length()]; + dis.readFully(bytes); + return bytes; + } finally { + dis.close(); + } + } + + + public static byte[] sign(Trans trans, byte[] bytes, PrivateKey pk) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException { + TimeTaken tt = trans.start("Sign Data", Env.SUB); + try { + Signature sig = Signature.getInstance(SIG_ALGO); + sig.initSign(pk, random); + sig.update(bytes); + return sig.sign(); + } finally { + tt.done(); + } + } + + public static String toSignatureString(byte[] signed) throws IOException { + return textBuilder("SIGNATURE", signed); + } + + public static boolean verify(Trans trans, byte[] bytes, byte[] signature, PublicKey pk) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + TimeTaken tt = trans.start("Verify Data", Env.SUB); + try { + Signature sig = Signature.getInstance(SIG_ALGO); + sig.initVerify(pk); + sig.update(bytes); + return sig.verify(signature); + } finally { + tt.done(); + } + } +} diff --git a/aaf/src/main/java/com/att/cadi/cm/PlaceArtifact.java b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifact.java new file mode 100644 index 0000000..b5a3fb0 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifact.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +import com.att.cadi.CadiException; +import com.att.inno.env.Trans; + +public interface PlaceArtifact { + public boolean place(Trans trans, CertInfo cert, Artifact arti) throws CadiException; +} diff --git a/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInFiles.java b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInFiles.java new file mode 100644 index 0000000..219eb4a --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInFiles.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.File; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +import com.att.cadi.CadiException; +import com.att.cadi.util.Chmod; +import com.att.inno.env.Trans; + +public class PlaceArtifactInFiles extends ArtifactDir { + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + try { + // Setup Public Cert + File f = new File(dir,arti.getAppName()+".crt"); + write(f,Chmod.to644,certInfo.getCerts().get(0),C_R); + + // Setup Private Key + f = new File(dir,arti.getAppName()+".key"); + write(f,Chmod.to400,certInfo.getPrivatekey(),C_R); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } +} + + diff --git a/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInKeystore.java b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInKeystore.java new file mode 100644 index 0000000..6859ba5 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactInKeystore.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.File; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Collection; + +import com.att.cadi.CadiException; +import com.att.cadi.Symm; +import com.att.cadi.config.Config; +import com.att.cadi.util.Chmod; +import com.att.inno.env.Trans; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactInKeystore extends ArtifactDir { + private String kst; + //TODO get ROOT DNs or Trusted DNs from Certificate Manager. +// private static String[] rootDNs = new String[]{ +// "CN=ATT CADI Root CA - Test, O=ATT, OU=CSO, C=US", // Lab. delete eventually +// "CN=ATT AAF CADI TEST CA, OU=CSO, O=ATT, C=US", +// "CN=ATT AAF CADI CA, OU=CSO, O=ATT, C=US" +// }; + + public PlaceArtifactInKeystore(String kst) { + this.kst = kst; + } + + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + File fks = new File(dir,arti.getAppName()+'.'+kst); + try { + KeyStore jks = KeyStore.getInstance(kst); + if(fks.exists()) { + fks.delete(); + } + + // Get the Cert(s)... Might include Trust store + Collection certColl = Factory.toX509Certificate(certInfo.getCerts()); + X509Certificate[] certs = new X509Certificate[certColl.size()]; + certColl.toArray(certs); + + + // Add CADI Keyfile Entry to Properties + addProperty(Config.CADI_KEYFILE,arti.getDir()+'/'+arti.getAppName() + ".keyfile"); + // Set Keystore Password + addProperty(Config.CADI_KEYSTORE,fks.getAbsolutePath()); + String keystorePass = Symm.randomGen(CmAgent.PASS_SIZE); + addEncProperty(Config.CADI_KEYSTORE_PASSWORD,keystorePass); + char[] keystorePassArray = keystorePass.toCharArray(); + jks.load(null,keystorePassArray); // load in + + // Add Private Key/Cert Entry for App + // Note: Java SSL security classes, while having a separate key from keystore, + // is documented to not actually work. + // java.security.UnrecoverableKeyException: Cannot recover key + // You can create a custom Key Manager to make it work, but Practicality + // dictates that you live with the default, meaning, they are the same + String keyPass = keystorePass; //Symm.randomGen(CmAgent.PASS_SIZE); + PrivateKey pk = Factory.toPrivateKey(trans, certInfo.getPrivatekey()); + addEncProperty(Config.CADI_KEY_PASSWORD, keyPass); + addProperty(Config.CADI_ALIAS, arti.getMechid()); +// Set attribs = new HashSet(); +// if(kst.equals("pkcs12")) { +// // Friendly Name +// attribs.add(new PKCS12Attribute("1.2.840.113549.1.9.20", arti.getAppName())); +// } +// + KeyStore.ProtectionParameter protParam = + new KeyStore.PasswordProtection(keyPass.toCharArray()); + + KeyStore.PrivateKeyEntry pkEntry = + new KeyStore.PrivateKeyEntry(pk, new Certificate[] {certs[0]}); + jks.setEntry(arti.getMechid(), + pkEntry, protParam); + + // Write out + write(fks,Chmod.to400,jks,keystorePassArray); + + // Change out to TrustStore + fks = new File(dir,arti.getAppName()+".trust."+kst); + jks = KeyStore.getInstance(kst); + + // Set Truststore Password + addProperty(Config.CADI_TRUSTSTORE,fks.getAbsolutePath()); + String trustStorePass = Symm.randomGen(CmAgent.PASS_SIZE); + addEncProperty(Config.CADI_TRUSTSTORE_PASSWORD,trustStorePass); + char[] truststorePassArray = trustStorePass.toCharArray(); + jks.load(null,truststorePassArray); // load in + + // Add Trusted Certificates + for(int i=1; i0) { + trans.info().printf("Warning: %s\n",capi.getNotes()); + } + out.printf("Challenge: %s\n",capi.getChallenge()); + out.printf("PrivateKey:\n%s\n",capi.getPrivatekey()); + out.println("Certificate Chain:"); + for(String c : capi.getCerts()) { + out.println(c); + } + return true; + } +} diff --git a/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactScripts.java b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactScripts.java new file mode 100644 index 0000000..ab77ca2 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/cm/PlaceArtifactScripts.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.cm; + +import java.io.File; + +import com.att.cadi.CadiException; +import com.att.cadi.util.Chmod; +import com.att.inno.env.Trans; +import com.att.inno.env.util.Chrono; +import com.att.inno.env.util.Split; + +import certman.v1_0.Artifacts.Artifact; +import certman.v1_0.CertInfo; + +public class PlaceArtifactScripts extends ArtifactDir { + @Override + public boolean _place(Trans trans, CertInfo certInfo, Artifact arti) throws CadiException { + try { + // Setup check.sh script + String filename = arti.getAppName()+".check.sh"; + File f1 = new File(dir,filename); + String email = arti.getNotification() + '\n'; + if(email.startsWith("mailto:")) { + email=email.substring(7); + } else { + email=arti.getOsUser() + '\n'; + } + + StringBuilder classpath = new StringBuilder(); + boolean first = true; + for(String pth : Split.split(File.pathSeparatorChar, System.getProperty("java.class.path"))) { + if(first) { + first=false; + } else { + classpath.append(File.pathSeparatorChar); + } + File f = new File(pth); + classpath.append(f.getCanonicalPath().replaceAll("[0-9]+\\.[0-9]+\\.[0-9]+","*")); + } + + write(f1,Chmod.to644, + "#!/bin/bash " + f1.getCanonicalPath()+'\n', + "# Certificate Manager Check Script\n", + "# Check on Certificate, and renew if needed.\n", + "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n', + "DIR="+arti.getDir()+'\n', + "APP="+arti.getAppName()+'\n', + "EMAIL="+email, + "CP=\""+classpath.toString()+"\"\n", + checkScript + ); + + // Setup check.sh script + File f2 = new File(dir,arti.getAppName()+".crontab.sh"); + write(f2,Chmod.to644, + "#!/bin/bash " + f1.getCanonicalPath()+'\n', + "# Certificate Manager Crontab Loading Script\n", + "# Add/Update a Crontab entry, that adds a check on Certificate Manager generated Certificate nightly.\n", + "# Generated by Certificate Manager " + Chrono.timeStamp()+'\n', + "TFILE=\"/tmp/cmcron$$.temp\"\n", + "DIR=\""+arti.getDir()+"\"\n", + "CF=\""+arti.getAppName()+" Certificate Check Script\"\n", + "SCRIPT=\""+f1.getCanonicalPath()+"\"\n", + cronScript + ); + + } catch (Exception e) { + throw new CadiException(e); + } + return true; + } + + private final static String checkScript = + "> $DIR/$APP.msg\n\n" + + "function mailit {\n" + + " printf \"$*\" | /bin/mail -s \"AAF Certman Notification for `uname -n`\" $EMAIL\n"+ + "}\n\n" + + System.getProperty("java.home") + "/bin/" +"java -cp $CP " + + CmAgent.class.getName() + + " cadi_prop_files=$DIR/$APP.props check 2> $DIR/$APP.STDERR > $DIR/$APP.STDOUT\n" + + "case \"$?\" in\n" + + " 0)\n" + + " # Note: Validation will be mailed only the first day after any modification\n" + + " if [ \"`find $DIR -mtime 0 -name $APP.check.sh`\" != \"\" ] ; then\n" + + " mailit `echo \"Certficate Validated:\\n\\n\" | cat - $DIR/$APP.msg`\n" + + " else\n" + + " cat $DIR/$APP.msg\n" + + " fi\n" + + " ;;\n" + + " 1) mailit \"Error with Certificate Check:\\\\n\\\\nCheck logs $DIR/$APP.STDOUT and $DIR/$APP.STDERR on `uname -n`\"\n" + + " ;;\n" + + " 2) mailit `echo \"Certificate Check Error\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " ;;\n" + + " 10) mailit `echo \"Certificate Replaced\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " if [ -e $DIR/$APP.restart.sh ]; then\n" + + " # Note: it is THIS SCRIPT'S RESPONSIBILITY to notify upon success or failure as necessary!!\n" + + " /bin/sh $DIR/$APP.restart.sh\n" + + " fi\n" + + " ;;\n" + + " *) mailit `echo \"Unknown Error code for CM Agent\\\\n\\\\n\" | cat - $DIR/$APP.msg`\n" + + " ;;\n" + + " esac\n\n" + + " # Note: make sure to cover this sripts' exit Code\n"; + + private final static String cronScript = + "crontab -l | sed -n \"/#### BEGIN $CF/,/END $CF ####/!p\" > $TFILE\n" + + "# Note: Randomize Minutes (0-60) and hours (1-4)\n" + + "echo \"#### BEGIN $CF ####\" >> $TFILE\n" + + "echo \"$(( $RANDOM % 60)) $(( $(( $RANDOM % 3 )) + 1 )) * * * /bin/bash $SCRIPT " + + ">> $DIR/cronlog 2>&1 \" >> $TFILE\n" + + "echo \"#### END $CF ####\" >> $TFILE\n" + + "crontab $TFILE\n" + + "rm $TFILE\n"; +} + + + diff --git a/aaf/src/main/java/com/att/cadi/sso/AAFSSO.java b/aaf/src/main/java/com/att/cadi/sso/AAFSSO.java new file mode 100644 index 0000000..5d0d342 --- /dev/null +++ b/aaf/src/main/java/com/att/cadi/sso/AAFSSO.java @@ -0,0 +1,286 @@ +/******************************************************************************* + * ============LICENSE_START==================================================== + * * org.onap.aai + * * =========================================================================== + * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * * Copyright © 2017 Amdocs + * * =========================================================================== + * * 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==================================================== + * * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * * + ******************************************************************************/ +package com.att.cadi.sso; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import com.att.cadi.Access.Level; +import com.att.cadi.CadiException; +import com.att.cadi.PropAccess; +import com.att.cadi.Symm; +import com.att.cadi.config.Config; +import com.att.cadi.util.MyConsole; +import com.att.cadi.util.SubStandardConsole; +import com.att.cadi.util.TheConsole; + + +public class AAFSSO { + public static final MyConsole cons = TheConsole.implemented()?new TheConsole():new SubStandardConsole(); + + private Properties diskprops = null; // use for temp storing User/Password on disk + private File dot_aaf = null, sso=null; // instantiated, if ever, with diskprops + + boolean removeSSO=false; + boolean loginOnly = false; + private PropAccess access; + private StringBuilder err; + private String user,encrypted_pass; + private boolean use_X509; + + private PrintStream os, stdout=null,stderr=null; + + private Method close; + + public AAFSSO(String[] args) throws IOException, CadiException { + List larg = new ArrayList(args.length); + + // Cover for bash's need to escape *... (\\*) + // also, remove SSO if required + for (int i = 0; i < args.length; ++i) { + if ("\\*".equals(args[i])) { + args[i] = "*"; + } + + if("-logout".equalsIgnoreCase(args[i])) { + removeSSO=true; + } else if("-login".equalsIgnoreCase(args[i])) { + loginOnly = true; + } else { + larg.add(args[i]); + } + } + + String[] nargs = new String[larg.size()]; + larg.toArray(nargs); + + dot_aaf = new File(System.getProperty("user.home")+"/.aaf"); + if(!dot_aaf.exists()) { + dot_aaf.mkdirs(); + } + File f = new File(dot_aaf,"sso.out"); + os = new PrintStream(new FileOutputStream(f,true)); + stdout = System.out; + stderr = System.err; + System.setOut(os); + System.setErr(os); + + access = new PropAccess(os,nargs); + Config.setDefaultRealm(access); + + user = access.getProperty(Config.AAF_MECHID); + encrypted_pass = access.getProperty(Config.AAF_MECHPASS); + + File dot_aaf_kf = new File(dot_aaf,"keyfile"); + + sso = new File(dot_aaf,"sso.props"); + if(removeSSO) { + if(dot_aaf_kf.exists()) { + dot_aaf_kf.setWritable(true,true); + dot_aaf_kf.delete(); + } + if(sso.exists()) { + sso.delete(); + } + System.out.println("AAF SSO information removed"); + System.exit(0); + } + + if(!dot_aaf_kf.exists()) { + FileOutputStream fos = new FileOutputStream(dot_aaf_kf); + try { + fos.write(Symm.encrypt.keygen()); + dot_aaf_kf.setExecutable(false,false); + dot_aaf_kf.setWritable(false,false); + dot_aaf_kf.setReadable(false,false); + dot_aaf_kf.setReadable(true, true); + } finally { + fos.close(); + } + } + + String keyfile = access.getProperty(Config.CADI_KEYFILE); // in case it's CertificateMan props + if(keyfile==null) { + access.setProperty(Config.CADI_KEYFILE, dot_aaf_kf.getAbsolutePath()); + } + + String alias = access.getProperty(Config.CADI_ALIAS); + if(user==null && alias!=null && access.getProperty(Config.CADI_KEYSTORE_PASSWORD)!=null) { + user = alias; + access.setProperty(Config.AAF_MECHID, user); + use_X509 = true; + } else { + use_X509 = false; + Symm decryptor = Symm.obtain(dot_aaf_kf); + if (user==null) { + if(sso.exists() && sso.lastModified()>System.currentTimeMillis()-(8*60*60*1000 /* 8 hours */)) { + String cm_url = access.getProperty(Config.CM_URL); // SSO might overwrite... + FileInputStream fos = new FileInputStream(sso); + try { + access.load(fos); + user = access.getProperty(Config.AAF_MECHID); + encrypted_pass = access.getProperty(Config.AAF_MECHPASS); + // decrypt with .aaf, and re-encrypt with regular Keyfile + access.setProperty(Config.AAF_MECHPASS, + access.encrypt(decryptor.depass(encrypted_pass))); + if(cm_url!=null) { //Command line CM_URL Overwrites ssofile. + access.setProperty(Config.CM_URL, cm_url); + } + } finally { + fos.close(); + } + } else { + diskprops = new Properties(); + String realm = Config.getDefaultRealm(); + // Turn on Console Sysout + System.setOut(stdout); + user=cons.readLine("aaf_id(%s@%s): ",System.getProperty("user.name"),realm); + if(user==null) { + user = System.getProperty("user.name")+'@'+realm; + } else if(user.length()==0) { // + user = System.getProperty("user.name")+'@' + realm; + } else if(user.indexOf('@')<0 && realm!=null) { + user = user+'@'+realm; + } + access.setProperty(Config.AAF_MECHID,user); + diskprops.setProperty(Config.AAF_MECHID,user); + encrypted_pass = new String(cons.readPassword("aaf_password: ")); + System.setOut(os); + encrypted_pass = Symm.ENC+decryptor.enpass(encrypted_pass); + access.setProperty(Config.AAF_MECHPASS,encrypted_pass); + diskprops.setProperty(Config.AAF_MECHPASS,encrypted_pass); + diskprops.setProperty(Config.CADI_KEYFILE, access.getProperty(Config.CADI_KEYFILE)); + } + } + } + if (user == null) { + err = new StringBuilder("Add -D" + Config.AAF_MECHID + "= "); + } + + if (encrypted_pass == null && alias==null) { + if (err == null) { + err = new StringBuilder(); + } else { + err.append("and "); + } + err.append("-D" + Config.AAF_MECHPASS + "= "); + } + } + + public void setLogDefault() { + access.setLogLevel(PropAccess.DEFAULT); + if(stdout!=null) { + System.setOut(stdout); + } + } + + public void setStdErrDefault() { + access.setLogLevel(PropAccess.DEFAULT); + if(stderr!=null) { + System.setErr(stderr); + } + } + + public void setLogDefault(Level level) { + access.setLogLevel(level); + if(stdout!=null) { + System.setOut(stdout); + } + } + + public boolean loginOnly() { + return loginOnly; + } + + public void addProp(String key, String value) { + if(diskprops!=null) { + diskprops.setProperty(key, value); + } + } + + public void writeFiles() throws IOException { + // Store Creds, if they work + if(diskprops!=null) { + if(!dot_aaf.exists()) { + dot_aaf.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(sso); + try { + diskprops.store(fos, "AAF Single Signon"); + } finally { + fos.close(); + sso.setWritable(false,false); + sso.setExecutable(false,false); + sso.setReadable(false,false); + sso.setReadable(true,true); + } + } + if(sso!=null) { + sso.setReadable(false,false); + sso.setWritable(false,false); + sso.setExecutable(false,false); + sso.setReadable(true,true); + sso.setWritable(true,true); + } + } + + public PropAccess access() { + return access; + } + + public StringBuilder err() { + return err; + } + + public String user() { + return user; + } + + public String enc_pass() { + return encrypted_pass; + } + + public boolean useX509() { + return use_X509; + } + + public void close() { + if(close!=null) { + try { + close.invoke(null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // nothing to do here. + } + close = null; + } + } +} -- cgit 1.2.3-korg