summaryrefslogtreecommitdiffstats
path: root/cadi/core/src/main/java/org
diff options
context:
space:
mode:
authorInstrumental <jonathan.gathman@att.com>2018-10-12 12:55:37 -0500
committerInstrumental <jonathan.gathman@att.com>2018-10-12 12:55:40 -0500
commit7e5ccdd25e377cfa2dd5850ac3c2c1428c40b078 (patch)
tree9e7a737c934e39a8809339d2efedca1dd27ce3b4 /cadi/core/src/main/java/org
parentbdaf445e08978aadeca4232030fdbf6d39797dc8 (diff)
CADI ID Translate
Issue-ID: AAF-556 Change-Id: Ifd6c42012a90b369b41ad5ae8e724fb5950df958 Signed-off-by: Instrumental <jonathan.gathman@att.com>
Diffstat (limited to 'cadi/core/src/main/java/org')
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java1
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapBathConverter.java170
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java18
-rw-r--r--cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java190
4 files changed, 379 insertions, 0 deletions
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java
index 088227ed..2479a058 100644
--- a/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java
+++ b/cadi/core/src/main/java/org/onap/aaf/cadi/config/Config.java
@@ -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
index 00000000..7a138e97
--- /dev/null
+++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/MapBathConverter.java
@@ -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;
+ }
+}
diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java
index d5f6b032..3466a8d8 100644
--- a/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java
+++ b/cadi/core/src/main/java/org/onap/aaf/cadi/taf/basic/BasicHttpTaf.java
@@ -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
index 00000000..4ae68310
--- /dev/null
+++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java
@@ -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();
+ }
+
+}