CADI ID Translate 76/70376/1
authorInstrumental <jonathan.gathman@att.com>
Fri, 12 Oct 2018 17:55:37 +0000 (12:55 -0500)
committerInstrumental <jonathan.gathman@att.com>
Fri, 12 Oct 2018 17:55:40 +0000 (12:55 -0500)
Issue-ID: AAF-556
Change-Id: Ifd6c42012a90b369b41ad5ae8e724fb5950df958
Signed-off-by: Instrumental <jonathan.gathman@att.com>
37 files changed:
auth-client/pom.xml
auth/auth-batch/pom.xml
auth/auth-cass/pom.xml
auth/auth-certman/pom.xml
auth/auth-cmd/pom.xml
auth/auth-core/pom.xml
auth/auth-deforg/pom.xml
auth/auth-fs/pom.xml
auth/auth-gui/pom.xml
auth/auth-hello/pom.xml
auth/auth-locate/pom.xml
auth/auth-oauth/pom.xml
auth/auth-service/pom.xml
auth/csit/agent.sh
auth/csit/d.props.init
auth/docker/agent.sh
auth/docker/d.props.init
auth/pom.xml
cadi/aaf/pom.xml
cadi/client/pom.xml
cadi/core/pom.xml
cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java
cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapBathConverter.java [new file with mode: 0644]
cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java
cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MapBathConverter.java [new file with mode: 0644]
cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_CSV.java [new file with mode: 0644]
cadi/oauth-enduser/pom.xml
cadi/pom.xml
cadi/servlet-sample/pom.xml
misc/env/pom.xml
misc/log4j/pom.xml
misc/pom.xml
misc/rosetta/pom.xml
misc/xgen/pom.xml
pom.xml
version.properties

index 0866c7c..0646a4e 100644 (file)
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.onap.aaf.authz</groupId>
         <artifactId>parent</artifactId>
-        <version>2.1.3-SNAPSHOT</version>
+        <version>2.1.4-SNAPSHOT</version>
     </parent>
        
        <artifactId>aaf-auth-client</artifactId>
index 73235bb..6ecb552 100644 (file)
@@ -25,7 +25,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index fec5358..871e901 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index eb0e35f..60aef39 100644 (file)
@@ -19,7 +19,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index 2f1101e..da83463 100644 (file)
@@ -18,7 +18,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index c384cb6..093de2f 100644 (file)
@@ -25,7 +25,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index 1dfff06..697aa8f 100644 (file)
@@ -26,7 +26,7 @@
                <artifactId>authparent</artifactId>
                <relativePath>../pom.xml</relativePath>
                <groupId>org.onap.aaf.authz</groupId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
        </parent>
 
        <artifactId>aaf-auth-deforg</artifactId>
index ac1a82b..acf0ef6 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index e2324b9..fb7f415 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index ca329cc..169a679 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index 03881a3..2b9932c 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index eb770f4..2bb267e 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index b36598a..5181394 100644 (file)
@@ -17,7 +17,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>authparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>../pom.xml</relativePath>
        </parent>
 
index 55a4517..34b1dcf 100644 (file)
@@ -9,7 +9,7 @@ fi
 . ./aaf.props
 
 DOCKER=${DOCKER:=docker}
-CADI_VERSION=${CADI_VERSION:=2.1.3-SNAPSHOT}
+CADI_VERSION=${CADI_VERSION:=2.1.4-SNAPSHOT}
 
 for V in VERSION DOCKER_REPOSITORY AAF_FQDN AAF_FQDN_IP DEPLOY_FQI APP_FQDN APP_FQI VOLUME DRIVER LATITUDE LONGITUDE; do
    if [ "$(grep $V ./aaf.props)" = "" ]; then
index 14728dd..bde88c8 100644 (file)
@@ -2,7 +2,7 @@
 ORG=onap
 PROJECT=aaf
 DOCKER_REPOSITORY=nexus3.onap.org:10003
-VERSION=2.1.3-SNAPSHOT
+VERSION=2.1.4-SNAPSHOT
 CONF_ROOT_DIR=/opt/app/osaaf
 # For local builds, set PREFIX=   
 PREFIX="$DOCKER_REPOSITORY/"
index 55a4517..34b1dcf 100644 (file)
@@ -9,7 +9,7 @@ fi
 . ./aaf.props
 
 DOCKER=${DOCKER:=docker}
-CADI_VERSION=${CADI_VERSION:=2.1.3-SNAPSHOT}
+CADI_VERSION=${CADI_VERSION:=2.1.4-SNAPSHOT}
 
 for V in VERSION DOCKER_REPOSITORY AAF_FQDN AAF_FQDN_IP DEPLOY_FQI APP_FQDN APP_FQI VOLUME DRIVER LATITUDE LONGITUDE; do
    if [ "$(grep $V ./aaf.props)" = "" ]; then
index 14728dd..bde88c8 100644 (file)
@@ -2,7 +2,7 @@
 ORG=onap
 PROJECT=aaf
 DOCKER_REPOSITORY=nexus3.onap.org:10003
-VERSION=2.1.3-SNAPSHOT
+VERSION=2.1.4-SNAPSHOT
 CONF_ROOT_DIR=/opt/app/osaaf
 # For local builds, set PREFIX=   
 PREFIX="$DOCKER_REPOSITORY/"
index d35703f..3212902 100644 (file)
@@ -26,7 +26,7 @@
        <parent>
         <groupId>org.onap.aaf.authz</groupId>
         <artifactId>parent</artifactId>
-        <version>2.1.3-SNAPSHOT</version>
+        <version>2.1.4-SNAPSHOT</version>
     </parent>
        <artifactId>authparent</artifactId>
        <name>AAF Auth Parent</name>
index 8b9692f..5d75916 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>cadiparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
index 698eb80..2007a43 100644 (file)
@@ -22,7 +22,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>cadiparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
index 9afde0c..32b6315 100644 (file)
@@ -16,7 +16,7 @@
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>cadiparent</artifactId>
                <relativePath>..</relativePath>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
        </parent>
 
        <modelVersion>4.0.0</modelVersion>
index 088227e..2479a05 100644 (file)
@@ -103,6 +103,7 @@ public class Config {
     public static final String CADI_PROTOCOLS = "cadi_protocols";
     public static final String CADI_NOAUTHN = "cadi_noauthn";
     public static final String CADI_LOC_LIST = "cadi_loc_list";
+    public static final String CADI_BATH_CONVERT = "cadi_bath_convert";
     
     public static final String CADI_USER_CHAIN_TAG = "cadi_user_chain";
     public static final String CADI_USER_CHAIN = "USER_CHAIN";
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapBathConverter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapBathConverter.java
new file mode 100644 (file)
index 0000000..7a138e9
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ *
+ */
+
+package org.onap.aaf.cadi.filter;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.xml.ws.Holder;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.Access.Level;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.util.CSV;
+import org.onap.aaf.cadi.util.CSV.Visitor;
+
+/**
+ * This Filter is designed to help MIGRATE users from systems that don't match the FQI style.  
+ *  
+ * Style 1, where just the ID is translated, i.e. OLD => new@something.onap.org, that is acceptable
+ * longer term, because it does not store Creds locally.  The passwords are in appropriate systems, but
+ * it's still painful operationally, though it does ease migration.
+ *
+ * Style 3, however, which is Direct match of Authorization Header to replacement, is only there
+ * because some passwords are simply not acceptable for AAF, (too easy, for instance), and it is
+ * not feasible to break Organization Password rules for a Migration.  Therefore, this method 
+ * should not considered something that is in any way a permanent
+ * 
+
+ * 
+ * It goes without saying that any file with the password conversion should be protected by "400", etc.
+ * 
+ * @author Instrumental (Jonathan)
+ *
+ */
+public class MapBathConverter {
+    private static final String BASIC = "Basic ";
+       private final Map<String,String> map;
+
+    /**
+     * Create with colon separated name value pairs
+     *  Enter the entire "Basic dXNlcjpwYXNz" "Authorization" header, where "dXNlcjpwYXNz" is 
+     *  base64 encoded, which can be created with "cadi" tool (in jar)
+     *  
+     *  The replacement should also be an exact replacement of what you want.  Recognize that 
+     *  this should be TEMPORARY as you are storing credentials outside the users control. 
+     *  
+     * @param value
+     * @throws IOException 
+     * @throws CadiException 
+     */
+    public MapBathConverter(final Access access, final CSV csv) throws IOException, CadiException {
+        map = new TreeMap<>();
+        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        final Date now = new Date();
+        csv.visit(new Visitor() {
+                       @Override
+                       public void visit(List<String> row) throws CadiException {
+                               if(row.size()<3) {
+                                       throw new CadiException("CSV file " + csv + " must have at least 2 Basic Auth columns and an Expiration Date(YYYYMMDD) in each row");
+                               }
+                               try {
+                                       Date date = sdf.parse(row.get(2));
+                                       String oldID = row.get(0);
+                                       String newID = row.get(1);
+                                       if(date.after(now)) {
+                                               if(!oldID.startsWith(BASIC) && newID.startsWith(BASIC)) {
+                                                       throw new CadiException("CSV file " + csv + ": Uncredentialed ID " + idFromBasic(oldID,null) +
+                                                                                                       " may not transfer to credentialed ID " + idFromBasic(newID,null));
+                                               } else {
+                                                       map.put(oldID,newID);
+                                                       access.printf(Level.INIT, "ID Conversion from %s to %s enabled",
+                                                                       idFromBasic(oldID,null),
+                                                                       idFromBasic(newID,null));
+                                               }
+                                       } else {
+                                               access.printf(Level.INIT, "ID Conversion from %s to %s has expired.",
+                                                               idFromBasic(oldID,null),
+                                                               idFromBasic(newID,null));
+                                       }
+                               } catch (ParseException e) {
+                                       throw new CadiException("Cannot Parse Date: " + row.get(2));
+                               } catch (IOException e) {
+                                       throw new CadiException(e);
+                               }
+                       }
+               });
+    }
+    
+    private static String idFromBasic(String bath, Holder<String> hpass) throws IOException, CadiException {
+       if(bath.startsWith(BASIC)) {
+                       String cred = Symm.base64noSplit.decode(bath.substring(6));
+                       int colon = cred.indexOf(':');
+                       if(colon<0) {
+                               throw new CadiException("Invalid Authentication Credential for " + cred);
+                       }
+                       if(hpass!=null) {
+                               hpass.value = cred.substring(colon+1);
+                       }
+                       return cred.substring(0, colon);
+       } else {
+               return bath;
+       }
+    }
+
+    /**
+     * use to instantiate entries 
+     * 
+     * @return
+     */
+    public Map<String,String> map() {
+        return map;
+    }
+
+    public String convert(Access access, final String bath) {
+       String rv = map.get(bath);
+               String cred=null;
+               Holder<String> hpass=null;
+               try {
+                       if(rv==null || !rv.startsWith(BASIC)) {
+                       if(bath.startsWith(BASIC)) {
+                               cred = idFromBasic(bath,(hpass=new Holder<String>()));
+                       }
+               }
+
+               if(cred!=null) {
+                       if(rv==null) {
+                               rv = map.get(cred);
+                       }
+                       // for SAFETY REASONS, we WILL NOT allow a non validated cred to 
+                               // pass a password from file. Should be caught from Instation, but...
+                       if(rv!=null) {
+                                       if(rv.startsWith(BASIC)) {
+                                               return bath;
+                                       } else {
+                                               rv = BASIC + Symm.base64noSplit.encode(rv+':'+hpass.value);
+                                       }
+                       }
+                       }
+               } catch (IOException | CadiException e) {
+                       access.log(e,"Invalid Authorization");
+               }
+
+       return rv;
+    }
+}
index d5f6b03..3466a8d 100644 (file)
@@ -34,16 +34,20 @@ import org.onap.aaf.cadi.Access.Level;
 import org.onap.aaf.cadi.BasicCred;
 import org.onap.aaf.cadi.CachedPrincipal;
 import org.onap.aaf.cadi.CachedPrincipal.Resp;
+import org.onap.aaf.cadi.CadiException;
 import org.onap.aaf.cadi.CredVal;
 import org.onap.aaf.cadi.CredVal.Type;
 import org.onap.aaf.cadi.CredValDomain;
 import org.onap.aaf.cadi.Taf;
+import org.onap.aaf.cadi.config.Config;
+import org.onap.aaf.cadi.filter.MapBathConverter;
 import org.onap.aaf.cadi.principal.BasicPrincipal;
 import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
 import org.onap.aaf.cadi.taf.HttpTaf;
 import org.onap.aaf.cadi.taf.TafResp;
 import org.onap.aaf.cadi.taf.TafResp.RESP;
 import org.onap.aaf.cadi.taf.dos.DenialOfServiceTaf;
+import org.onap.aaf.cadi.util.CSV;
 
 /**
  * BasicHttpTaf
@@ -66,6 +70,7 @@ public class BasicHttpTaf implements HttpTaf {
     private Map<String,CredVal> rbacs = new TreeMap<>();
     private boolean warn;
     private long timeToLive;
+       private MapBathConverter mapIds;
     
     public BasicHttpTaf(Access access, CredVal rbac, String realm, long timeToLive, boolean turnOnWarning) {
         this.access = access;
@@ -73,6 +78,16 @@ public class BasicHttpTaf implements HttpTaf {
         this.rbac = rbac;
         this.warn = turnOnWarning;
         this.timeToLive = timeToLive;
+        String csvFile = access.getProperty(Config.CADI_BATH_CONVERT, null);
+        if(csvFile==null) {
+               mapIds=null;
+        } else {
+               try {
+                               mapIds = new MapBathConverter(access, new CSV(csvFile));
+                       } catch (IOException | CadiException e) {
+                               access.log(e,"Bath Map Conversion is not initialzed (non fatal)");
+                       }
+        }
     }
 
     public void add(final CredValDomain cvd) {
@@ -116,6 +131,9 @@ public class BasicHttpTaf implements HttpTaf {
             if (warn&&!req.isSecure()) {
                 access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
             }
+            if(mapIds != null) {
+               authz = mapIds.convert(access, authz);
+            }
             try {
                 CachedBasicPrincipal ba = new CachedBasicPrincipal(this,authz,realm,timeToLive);
                 if (DenialOfServiceTaf.isDeniedID(ba.getName())!=null) {
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java
new file mode 100644 (file)
index 0000000..4ae6831
--- /dev/null
@@ -0,0 +1,190 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.aaf.cadi.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.CadiException;
+
+/**
+ * Read CSV file for various purposes
+ * 
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class CSV {
+       private File csv;
+       
+       public CSV(File file) {
+               csv = file;
+       }
+       
+       public CSV(String csvOfProperties) {
+               csv = new File(csvOfProperties);
+       }
+       
+
+       /**
+        * Create your code to accept the List<String> row.
+        * 
+        * Your code may keep the List... CSV does not hold onto it.
+        * 
+        * @author Instrumental(Jonathan)
+        *
+        */
+       public interface Visitor {
+               void visit(List<String> row) throws IOException, CadiException;
+       }
+       
+       public void visit(Visitor visitor) throws IOException, CadiException {
+               BufferedReader br = new BufferedReader(new FileReader(csv));
+               try {
+                       String line;
+                       StringBuilder sb = new StringBuilder();
+                       while((line = br.readLine())!=null) {
+                               line=line.trim();
+                               if(!line.startsWith("#") && line.length()>0) {
+//                                     System.out.println(line);  uncomment to debug
+                                       List<String> row = new ArrayList<>();
+                                       boolean quotes=false;
+                                       boolean escape=false;
+                                       char c;
+                                       for(int i=0;i<line.length();++i) {
+                                               switch(c=line.charAt(i)) {
+                                                       case '"':
+                                                               if(quotes) {
+                                                                       if(i<line.length()-1) { // may look ahead
+                                                                               if('"' == line.charAt(i+1)) {
+                                                                                       sb.append(c);
+                                                                                       ++i;
+                                                                               } else {
+                                                                                       quotes = false;
+                                                                               }
+                                                                       } else {
+                                                                               quotes=false;
+                                                                       }
+                                                               } else {
+                                                                       quotes=true;
+                                                               }
+                                                               break;
+                                                       case '\\':
+                                                               if(escape) {
+                                                                       sb.append(c);
+                                                                       escape = false;
+                                                               } else {
+                                                                       escape = true;
+                                                               }
+                                                               break;
+                                                       case ',':
+                                                               if(quotes) {
+                                                                       sb.append(c);
+                                                               } else {
+                                                                       row.add(sb.toString());
+                                                                       sb.setLength(0);
+                                                               }
+                                                               break;
+                                                       default:
+                                                               sb.append(c);
+                                               }
+                                       }
+                                       if(sb.length()>0) {
+                                               row.add(sb.toString());
+                                               sb.setLength(0);
+                                       }
+                                       visitor.visit(row);
+                               }
+                       }
+               } finally {
+                       br.close();
+               }
+       }
+       
+       public Writer writer() throws FileNotFoundException {
+               return new Writer();
+       }
+       
+       public class Writer {
+               private PrintStream ps;
+               private Writer() throws FileNotFoundException {
+                       ps = new PrintStream(new FileOutputStream(csv));
+               }
+               public void row(Object ... strings) {
+                       if(strings.length>0) {
+                               boolean first = true;
+                               boolean quote;
+                               String s;
+                               for(Object o : strings) {
+                                       if(first) {
+                                               first = false;
+                                       } else {
+                                               ps.append(',');
+                                       }
+                                       s = o.toString();
+                                       quote = s.matches(".*[,|\"].*");
+                                       if(quote) {
+                                               ps.append('"');
+                                               ps.print(s.replace("\"", "\"\"")
+                                                                 .replace("'", "''")
+                                                                 .replace("\\", "\\\\"));
+                                               ps.append('"');
+                                       } else {
+                                               ps.append(s);
+                                       }
+                               }
+                               ps.println();
+                       }
+               }
+               
+               /**
+                * Note: CSV files do not actually support Comments as a standard, but it is useful
+                * @param comment
+                */
+               public void comment(String comment) {
+                       ps.print("# ");
+                       ps.println(comment);
+               }
+               
+               public void flush() {
+                       ps.flush();
+               }
+               
+               public void close() {
+                       ps.close();
+               }
+       }
+
+       public void delete() {
+               csv.delete();
+       }
+       
+       public String toString() {
+               return csv.getAbsolutePath();
+       }
+
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MapBathConverter.java b/cadi/core/src/test/java/org/onap/aaf/cadi/config/test/JU_MapBathConverter.java
new file mode 100644 (file)
index 0000000..0bfa94c
--- /dev/null
@@ -0,0 +1,217 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.aaf.cadi.config.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.Symm;
+import org.onap.aaf.cadi.filter.MapBathConverter;
+import org.onap.aaf.cadi.util.CSV;
+import org.onap.aaf.cadi.util.CSV.Visitor;
+import org.onap.aaf.cadi.util.CSV.Writer;
+
+import junit.framework.Assert;
+
+/**
+ * Test a simple Migration conversion tool for CADI
+ * 
+ * @author Instrumental(Jonathan)
+ *
+ */
+public class JU_MapBathConverter {
+       private static final String NEW_USER_SOMETHING_ORG = "NEW_USER@Something.org";
+       private static final String OLD_ID = "OLD_ID";
+       private static final String SHARED_PASS = "SHARED_PASS";
+       private static CSV csv;
+       private static ArrayList<String> expected;
+       private static final Access access = new PropAccess();
+    private final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+       @BeforeClass
+       public static void createFile() throws IOException {
+               // Note, you cate a "MapBathConverter" by access to a File.
+               // We will create that file now.  Local is fine.
+               csv = new CSV("JU_MapBathConverter.csv"); 
+       }
+       
+       @BeforeClass
+       public static void beforeClass() {
+               expected = new ArrayList<>();
+       }
+       
+       @Before
+       public void before() {
+               expected.clear();
+       }
+       
+       @Test
+       public void test() throws IOException, CadiException {
+               CSV.Writer cw = csv.writer();
+               GregorianCalendar gc = new GregorianCalendar();
+               gc.add(GregorianCalendar.MONTH, 6);
+               try {
+                       try {
+                               // CSV can simply be OLD ID and NEW,  no passwords
+                               cw.row(exp(OLD_ID), exp(NEW_USER_SOMETHING_ORG),sdf.format(gc.getTime()));
+
+                               // Style 1 - Incoming ID/pass, create new cred with NweID and same Pass
+                               cw.row(exp(bath(OLD_ID,SHARED_PASS)), exp(NEW_USER_SOMETHING_ORG),sdf.format(gc.getTime()));
+                               // the response should be Basic with NEW_ID and OLD_PASS
+                               
+                               // Style 2
+                               cw.row(exp(bath(OLD_ID,"OLD_PASS")), exp(bath(NEW_USER_SOMETHING_ORG,"NEW_PASS")),sdf.format(gc.getTime()));
+                               
+                       } finally {
+                               cw.close();
+                       }
+                       
+                       final Iterator<String> exp = expected.iterator();
+                       csv.visit(new Visitor() {
+                               @Override
+                               public void visit(List<String> row) {
+                                       int i=0;
+                                       for(String s : row) {
+                                               switch(i++) {
+                                                       case 0:
+                                                       case 1:
+                                                               Assert.assertEquals(exp.next(), s);
+                                                               break;
+                                                       case 2:
+                                                               System.out.println(s);
+                                                               break;
+                                                       default:
+                                                               Assert.fail("There should only be 3 columns in this test case.");
+                                               }
+                                       }
+                               }
+                       });
+                       
+                       MapBathConverter mbc = new MapBathConverter(access, csv);
+
+                       // Check no lookup just returns the same
+                       Assert.assertEquals("NonKey", "NonKey"); // if not in map, expect same value
+
+                       Iterator<String> exp1 = expected.iterator();
+                       // there's no passwords in CSV
+                       String old = exp1.next(); 
+                       String nw = exp1.next();
+                       Assert.assertEquals(nw, mbc.convert(access,old));
+                       
+                       Assert.assertEquals(bath(NEW_USER_SOMETHING_ORG,SHARED_PASS), mbc.convert(access,bath(OLD_ID,SHARED_PASS)));
+                       
+                       // Style 1 (new cred, old password)
+                       old = exp1.next();
+                       nw = bath(exp1.next(),SHARED_PASS);
+                       Assert.assertEquals(nw, mbc.convert(access,old));
+
+                       // Style 2
+                       old = exp1.next();
+                       nw = exp1.next();
+                       Assert.assertEquals(nw, mbc.convert(access,old));
+
+               } finally {
+                       csv.delete();
+               }
+       }
+
+       @Test
+       public void testTooFewColumns() throws IOException, CadiException {
+               CSV.Writer cw = csv.writer();
+               try {
+                       try {
+                               cw.row(exp(bath(OLD_ID,"OLD_PASS")), exp(bath(NEW_USER_SOMETHING_ORG,"NEW_PASS")));
+                       } finally {
+                               cw.close();
+                       }
+                       
+                       try {
+                               new MapBathConverter(access, csv);
+                               Assert.fail("file with too few rows should throw exception");
+                       } catch(CadiException | IOException e) {
+                               Assert.assertTrue("Correctly thrown Exception",true);
+                       }
+               } finally {
+                       csv.delete();
+               }
+       }
+
+       @Test
+       public void testNoFile() {
+               try {
+                       new MapBathConverter(access, new CSV("Bogus"));
+                       Assert.fail("Non Existent File should throw exception");
+               } catch(CadiException | IOException e) {
+                       Assert.assertTrue("Correctly thrown Exception",true);
+               }
+       }
+       
+       @Test
+       public void testBadRows() throws IOException {
+               try {
+                       Writer cw = csv.writer();
+                       try {
+                               cw.row("Single Column");
+                       } finally {
+                               cw.close();
+                       }
+                       
+                       try {
+                               new MapBathConverter(access,csv);
+                               Assert.fail("Non Existent File should throw exception");
+                       } catch(CadiException | IOException e) {
+                               Assert.assertTrue("Correctly thrown Exception",true);
+                       }
+               } finally {
+                       csv.delete();
+               }
+               
+               // Check for deletion 
+               Assert.assertFalse(csv.toString() + "should have been deleted",new File(csv.toString()).exists());
+       }
+       
+       private String bath(String user, String password) throws IOException {
+               StringBuilder sb = new StringBuilder(user);
+               sb.append(':');
+               sb.append(password);
+               byte[] encoded = Symm.base64noSplit.encode(sb.toString().getBytes());
+               sb.setLength(0);
+               sb.append("Basic ");
+               sb.append(new String(encoded));
+               return sb.toString();
+       }
+
+       private String exp(String s) {
+               expected.add(s);
+               return s;
+       }
+}
diff --git a/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_CSV.java b/cadi/core/src/test/java/org/onap/aaf/cadi/util/test/JU_CSV.java
new file mode 100644 (file)
index 0000000..54c48da
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * ============LICENSE_START====================================================
+ * org.onap.aaf
+ * ===========================================================================
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END====================================================
+ */
+
+package org.onap.aaf.cadi.util.test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.ws.Holder;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.util.CSV;
+import org.onap.aaf.cadi.util.CSV.Visitor;
+import org.onap.aaf.cadi.util.CSV.Writer;
+
+public class JU_CSV {
+
+       private String filename;
+       private File file;
+       private static ArrayList<Object> expected;
+
+       @Before
+       public void start() {
+               filename = "Sample.csv";
+               file = new File(filename);
+       }
+       
+       @After
+       public void end() {
+               if(file!=null) {
+                       file.delete();
+               }
+       }
+
+       @BeforeClass
+       public static void before() {
+               expected = new ArrayList<>();
+       }
+       
+       @Test
+       public void test() throws IOException, CadiException {
+               CSV csv = new CSV(file);
+               // Can't visit for file that doesn't exist
+               try {
+                       csv.visit(new Visitor() {
+                               @Override
+                               public void visit(List<String> row) {
+                               }});
+               } catch(IOException e) {
+                       Assert.assertTrue("CSV correctly created exception",true);
+               }
+               
+               Writer writer = csv.writer();
+               try {
+                       writer.row(add("\"hello\""));
+                       writer.comment("Ignore Comments");
+                       writer.row(add("dXNlcjpwYXNzd29yZA=="),add("dXNlckBzb21ldGhpbmcub3JnOm90aGVyUGFzc3dvcmQ="));
+                       writer.row(); // no output
+                       writer.row(add("There is, but one thing to say"), add(" and that is"), add("\"All the best\""));
+               } finally {
+                       writer.close();
+               }
+               
+               PrintStream garbage = new PrintStream(new FileOutputStream(file, true));
+               try {
+                       garbage.println("# Ignore empty spaces, etc");
+                       garbage.println("   ");
+                       garbage.println("# Ignore blank lines");
+                       garbage.println();
+               } finally {
+                       garbage.close();
+               }
+
+       
+       //////////// 
+       // Tests
+       ////////////
+               final Holder<Integer> hi = new Holder<>(0);
+               csv.visit(new CSV.Visitor() {
+                       @Override
+                       public void visit(List<String> row) {
+                               for(String s: row) {
+//                                     System.out.println(hi.value + ") " + s);
+                                       Assert.assertEquals(expected.get(hi.value++),s);
+                               }
+                       }
+               });
+
+       }
+
+       private String add(String s) {
+               expected.add(s);
+               return s;
+       }
+
+}
index b0b0a95..d49e301 100644 (file)
@@ -25,7 +25,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>cadiparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
        
index 5b49bff..234e3e1 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
            <groupId>org.onap.aaf.authz</groupId>
            <artifactId>parent</artifactId>
-           <version>2.1.3-SNAPSHOT</version>
+           <version>2.1.4-SNAPSHOT</version>
     </parent>
        <artifactId>cadiparent</artifactId>
        <name>AAF CADI Parent (Code, Access, Data, Identity)</name>
index ad42fa7..0eb2965 100644 (file)
@@ -4,7 +4,7 @@
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>cadiparent</artifactId>
                <relativePath>..</relativePath>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
        <name>CADI Servlet Sample (Test Only)</name>
index 7dedc6f..0dc81ed 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>miscparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
index c51ad7b..45b55d3 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>miscparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
index dd5474d..18f501a 100644 (file)
@@ -25,7 +25,7 @@
        <parent>
        <groupId>org.onap.aaf.authz</groupId>
        <artifactId>parent</artifactId>
-       <version>2.1.3-SNAPSHOT</version>
+       <version>2.1.4-SNAPSHOT</version>
     </parent>
        <artifactId>miscparent</artifactId>
        <name>AAF Misc Parent</name>
index 62e7577..b4a44b4 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>miscparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
index df9d741..87dd242 100644 (file)
@@ -24,7 +24,7 @@
        <parent>
                <groupId>org.onap.aaf.authz</groupId>
                <artifactId>miscparent</artifactId>
-               <version>2.1.3-SNAPSHOT</version>
+               <version>2.1.4-SNAPSHOT</version>
                <relativePath>..</relativePath>
        </parent>
 
diff --git a/pom.xml b/pom.xml
index 19cdd1b..c450d37 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.onap.aaf.authz</groupId>
     <artifactId>parent</artifactId>
-    <version>2.1.3-SNAPSHOT</version>
+    <version>2.1.4-SNAPSHOT</version>
     <name>aaf-authz</name>
     <packaging>pom</packaging>
 
index 2ffb7ed..635c4b9 100644 (file)
@@ -27,7 +27,7 @@
 
 major=2
 minor=1
-patch=3
+patch=4
 
 base_version=${major}.${minor}.${patch}