Improved multi Proxy DNSLocator based
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / hl / Question.java
index a513da0..c7ee593 100644 (file)
@@ -26,14 +26,13 @@ import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 
 import org.onap.aaf.auth.common.Define;
@@ -46,6 +45,7 @@ import org.onap.aaf.auth.dao.cached.CachedNSDAO;
 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
+import org.onap.aaf.auth.dao.cached.FileGetter;
 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
 import org.onap.aaf.auth.dao.cass.CacheInfoDAO;
 import org.onap.aaf.auth.dao.cass.CertDAO;
@@ -62,6 +62,7 @@ import org.onap.aaf.auth.dao.cass.PermDAO;
 import org.onap.aaf.auth.dao.cass.RoleDAO;
 import org.onap.aaf.auth.dao.cass.Status;
 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
+import org.onap.aaf.auth.env.AuthzEnv;
 import org.onap.aaf.auth.env.AuthzTrans;
 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
 import org.onap.aaf.auth.env.AuthzTransFilter;
@@ -89,7 +90,7 @@ import com.datastax.driver.core.Cluster;
 public class Question {
 
     // DON'T CHANGE FROM lower Case!!!
-    public static enum Type {
+    public enum Type {
         ns, role, perm, cred
     };
 
@@ -101,7 +102,7 @@ public class Question {
 
     static final String ASTERIX = "*";
 
-    public static enum Access {
+    public enum Access {
         read, write, create
     };
 
@@ -124,28 +125,73 @@ public class Question {
     static Slot PERMS;
 
     private static Set<String> specialLog = null;
-    public static final Random random = new SecureRandom();
+    public static final SecureRandom random = new SecureRandom();
     private static long traceID = random.nextLong();
     private static Slot specialLogSlot = null;
     private static Slot transIDSlot = null;
 
 
-    public final HistoryDAO historyDAO;
-    public final CachedNSDAO nsDAO;
-    public  CachedRoleDAO roleDAO;
-    public final CachedPermDAO permDAO;
-    public CachedUserRoleDAO userRoleDAO;
-    public final CachedCredDAO credDAO;
-    public final CachedCertDAO certDAO;
-    public final DelegateDAO delegateDAO;
-    public final FutureDAO futureDAO;
-    public final ApprovalDAO approvalDAO;
-    private final CacheInfoDAO cacheInfoDAO;
+    private final HistoryDAO historyDAO;
+    public HistoryDAO historyDAO() {
+        return historyDAO;
+    }
+    
+    private final CachedNSDAO nsDAO;
+    public CachedNSDAO nsDAO() {
+        return nsDAO;
+    }
+    
+    private final CachedRoleDAO roleDAO;
+    public CachedRoleDAO roleDAO() {
+        return roleDAO;
+    }
+    
+    private final CachedPermDAO permDAO;
+    public CachedPermDAO permDAO() {
+        return permDAO;
+    }
+    
+    private final CachedUserRoleDAO userRoleDAO;
+    public CachedUserRoleDAO userRoleDAO() {
+        return userRoleDAO;
+    }
+    
+    private final CachedCredDAO credDAO;
+    public CachedCredDAO credDAO() {
+        return credDAO;
+    }
+    
+    private final CachedCertDAO certDAO;
+    public CachedCertDAO certDAO() {
+        return certDAO;
+    }
+    
+    private final DelegateDAO delegateDAO;
+    public DelegateDAO delegateDAO() {
+        return delegateDAO;
+    }
+    
+    private final FutureDAO futureDAO;
+    public FutureDAO futureDAO() {
+        return futureDAO;
+    }
+    
+    private final ApprovalDAO approvalDAO;
+    public ApprovalDAO approvalDAO() {
+        return approvalDAO;
+    }
+    
     public final LocateDAO locateDAO;
+    public LocateDAO locateDAO() {
+        return locateDAO;
+    }
+    
+    private final CacheInfoDAO cacheInfoDAO;
+    private final int cldays;
+    private final boolean alwaysSpecial;
 
-    public Question(AuthzTrans trans, Cluster cluster, String keyspace, boolean startClean) throws APIException, IOException {
+    public Question(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
         PERMS = trans.slot("USER_PERMS");
-        System.out.println(trans.init());
         trans.init().log("Instantiating DAOs");
         long expiresIn = Long.parseLong(trans.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF));
         historyDAO = new HistoryDAO(trans, cluster, keyspace);
@@ -157,6 +203,8 @@ public class Question {
         permDAO = new CachedPermDAO(new PermDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
         roleDAO = new CachedRoleDAO(new RoleDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
         userRoleDAO = new CachedUserRoleDAO(new UserRoleDAO(trans, historyDAO,cacheInfoDAO), cacheInfoDAO, expiresIn);
+        // Create if aaf_file_cred exists with file
+        FileGetter.singleton(trans.env().access());
         credDAO = new CachedCredDAO(new CredDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
         certDAO = new CachedCertDAO(new CertDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
 
@@ -165,14 +213,6 @@ public class Question {
         delegateDAO = new DelegateDAO(trans, historyDAO);
         approvalDAO = new ApprovalDAO(trans, historyDAO);
 
-        // Only want to aggressively cleanse User related Caches... The others,
-        // just normal refresh
-        if (startClean) {
-            CachedDAO.startCleansing(trans.env(), credDAO, userRoleDAO);
-            CachedDAO.startRefresh(trans.env(), cacheInfoDAO);
-        }
-        // Set a Timer to Check Caches to send messages for Caching changes
-        
         if (specialLogSlot==null) {
             specialLogSlot = trans.slot(AuthzTransFilter.SPECIAL_LOG_SLOT);
         }
@@ -182,9 +222,44 @@ public class Question {
         }
         
         AbsCassDAO.primePSIs(trans);
+        
+        cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
+        
+        alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
+    }
+    
+    /**
+     * Note: This Constructor created for JUNIT Purposes.  Do not use otherwise.
+     */
+    public Question(AuthzTrans trans, HistoryDAO historyDAO, CacheInfoDAO cacheInfoDAO,
+            CachedNSDAO nsDAO, CachedPermDAO permDAO, CachedRoleDAO roleDAO,
+            CachedUserRoleDAO userRoleDAO, CachedCredDAO credDAO, CachedCertDAO certDAO,
+            LocateDAO locateDAO,FutureDAO futureDAO, DelegateDAO delegateDAO,
+            ApprovalDAO approvalDAO ) {
+        this.historyDAO = historyDAO;
+        this.cacheInfoDAO = cacheInfoDAO;
+        this.nsDAO = nsDAO;
+        this.permDAO = permDAO;
+        this.roleDAO = roleDAO;
+        this.userRoleDAO = userRoleDAO;
+        this.credDAO = credDAO;
+        this.certDAO = certDAO;
+        this.locateDAO = locateDAO;
+        this.futureDAO = futureDAO;
+        this.delegateDAO = delegateDAO;
+        this.approvalDAO = approvalDAO;
+
+        cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
+        alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
     }
 
-
+    public void startTimers(AuthzEnv env) {
+        // Only want to aggressively cleanse User related Caches... The others,
+        // just normal refresh
+        CachedDAO.startCleansing(env, credDAO, userRoleDAO);
+        CachedDAO.startRefresh(env, cacheInfoDAO);
+    }
+    
     public void close(AuthzTrans trans) {
         historyDAO.close(trans);
         cacheInfoDAO.close(trans);
@@ -199,14 +274,28 @@ public class Question {
         approvalDAO.close(trans);
     }
 
-    public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type,
-            String instance, String action) {
-        Result<NsDAO.Data> rnd = deriveNs(trans, type);
-        if (rnd.isOK()) {
-            return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
-                    instance, action));
+    public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type, String instance, String action) {
+        if(type.indexOf('@') >= 0) {
+            int colon = type.indexOf(':');
+            if(colon>=0) {
+                PermDAO.Data pdd = new PermDAO.Data();
+                pdd.ns = type.substring(0, colon);
+                pdd.type = type.substring(colon+1);
+                pdd.instance = instance;
+                pdd.action = action;
+            
+                return Result.ok(pdd);
+            } else {
+                return Result.err(Result.ERR_BadData,"Could not extract ns and type from " + type);
+            }
         } else {
-            return Result.err(rnd);
+            Result<NsDAO.Data> rnd = deriveNs(trans, type);
+            if (rnd.isOK()) {
+                return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
+                        instance, action));
+            } else {
+                return Result.err(rnd);
+            }
         }
     }
 
@@ -270,21 +359,39 @@ public class Question {
         return Result.ok(rlpUser); 
     }
 
-    public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String perm) {
-        Result<NsSplit> nss = deriveNsSplit(trans, perm);
-        if (nss.notOK()) {
-            return Result.err(nss);
+    public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String type) {
+        if(type.indexOf('@') >= 0) {
+            int colon = type.indexOf(':');
+            if(colon>=0) {
+                return permDAO.readByType(trans, type.substring(0, colon),type.substring(colon+1));
+            } else {
+                return Result.err(Result.ERR_BadData, "%s is malformed",type);
+            }
+        } else {
+            Result<NsSplit> nss = deriveNsSplit(trans, type);
+            if (nss.notOK()) {
+                return Result.err(nss);
+            }
+            return permDAO.readByType(trans, nss.value.ns, nss.value.name);
         }
-        return permDAO.readByType(trans, nss.value.ns, nss.value.name);
     }
 
-    public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans,
-            String type, String instance, String action) {
-        Result<NsSplit> nss = deriveNsSplit(trans, type);
-        if (nss.notOK()) {
-            return Result.err(nss);
+    public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
+        if(type.indexOf('@') >= 0) {
+            int colon = type.indexOf(':');
+            if(colon>=0) {
+                return permDAO.read(trans, type.substring(0, colon),type.substring(colon+1), instance,action);
+            } else {
+                return Result.err(Result.ERR_BadData, "%s is malformed",type);
+            }
+        } else {
+            Result<NsSplit> nss = deriveNsSplit(trans, type);
+            if (nss.notOK()) {
+                return Result.err(nss);
+            }
+            
+            return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
         }
-        return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
     }
 
     public Result<List<PermDAO.Data>> getPermsByRole(AuthzTrans trans, String role, boolean lookup) {
@@ -330,8 +437,14 @@ public class Question {
         return Result.ok(perms);
     }
 
-    public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans,
-            String role) {
+    public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans, String role) {
+        if(role.startsWith(trans.user()) ) {
+            if(role.endsWith(":user")) {
+                return roleDAO.read(trans,trans.user(), "user");
+            } else {
+                return Result.err(Result.ERR_BadData,"%s is a badly formatted role",role);
+            }
+        }
         Result<NsSplit> nss = deriveNsSplit(trans, role);
         if (nss.notOK()) {
             return Result.err(nss);
@@ -368,12 +481,7 @@ public class Question {
         if (r.isOKhasData()) {
             return Result.ok(r.value.get(0));
         } else {
-            int dot;
-            if (child==null) {
-                return Result.err(Status.ERR_NsNotFound, "No Namespace");
-            } else {
-                dot = child.lastIndexOf('.');
-            }
+            int dot = child.lastIndexOf('.');
             if (dot < 0) {
                 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", child);
             } else {
@@ -490,30 +598,41 @@ public class Question {
     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
         // <ns>.access|:role:<role name>|<read|write>
         String ns = ndd.name;
+        boolean isRoot = ns.startsWith(Define.ROOT_NS());
         int last;
         do {
             if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
                 return Result.ok(ndd);
             }
+            if(isRoot) {
+                break;
+            }
             if ((last = ns.lastIndexOf('.')) >= 0) {
                 ns = ns.substring(0, last);
             }
         } while (last >= 0);
-        // com.att.aaf.ns|:<client ns>:ns|<access>
-        // AAF-724 - Make consistent response for May User", and not take the
-        // last check... too confusing.
-        Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + ndd.name + ":ns", access.name());
-        if (rv.isOK()) {
-            return rv;
-        } else if (rv.status==Result.ERR_Backend) {
-            return Result.err(rv);
-        } else {
-            return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
-                    user, access.name(), ndd.name);
-        }
+        
+        // SAFETY - Do not allow these when NS is Root
+        if(!isRoot) {
+            // com.att.aaf.ns|:<client ns>:ns|<access>
+            // AAF-724 - Make consistent response for May User", and not take the
+            // last check... too confusing.
+            Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + ndd.name + ":ns", access.name());
+                if (rv.isOK()) {
+                    return rv;
+                } else if (rv.status==Result.ERR_Backend) {
+                    return Result.err(rv);
+                }
+            }
+        return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
+                user, access.name(), ndd.name);
+
     }
 
     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
+        if(trans.user().equals(rdd.ns)) {
+            return Result.ok((NsDAO.Data)null);
+        }
         Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
         if (rnsd.isOK()) {
             return mayUser(trans, user, rnsd.value, rdd, access);
@@ -522,52 +641,70 @@ public class Question {
     }
 
     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
-        // 1) Is User in the Role?
-        Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
-        if (rurd.isOKhasData()) {
-            return Result.ok(ndd);
+        // 1) For "read", Is User in the Role is enough
+        if(Access.read.equals(access)) {
+            Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
+            if (rurd.isOKhasData()) {
+                return Result.ok(ndd);
+            }
         }
 
         String roleInst = ":role:" + rdd.name;
         // <ns>.access|:role:<role name>|<read|write>
         String ns = rdd.ns;
+        boolean isRoot = ns.startsWith(Define.ROOT_NS());
         int last;
         do {
             if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
                 return Result.ok(ndd);
             }
+            if(isRoot) {
+                break;
+            }
             if ((last = ns.lastIndexOf('.')) >= 0) {
                 ns = ns.substring(0, last);
             }
         } while (last >= 0);
 
-        // Check if Access by Global Role perm
-        // com.att.aaf.ns|:<client ns>:role:name|<access>
-        Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
-                + rdd.ns + roleInst, access.name());
-        if (rnsd.isOK()) {
-            return rnsd;
-        } else if (rnsd.status==Result.ERR_Backend) {
-            return Result.err(rnsd);
-        }
+        // SAFETY - Do not allow these when NS is Root
+        if(!isRoot) {
+            // Check if Access by Global Role perm
+            // com.att.aaf.ns|:<client ns>:role:name|<access>
+            Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
+                    + rdd.ns + roleInst, access.name());
+            if (rnsd.isOK()) {
+                return rnsd;
+            } else if (rnsd.status==Result.ERR_Backend) {
+                return Result.err(rnsd);
+            }
 
-        // Check if Access to Whole NS
-        // AAF-724 - Make consistent response for May User", and not take the
-        // last check... too confusing.
-        Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, 
-                ":" + rdd.ns + ":ns", access.name());
-        if (rv.isOK()) {
-            return rv;
-        } else if (rnsd.status==Result.ERR_Backend) {
-            return Result.err(rnsd);
-        } else {
-            return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
-                    user, access.name(), rdd.fullName());
+            // Check if Access to Whole NS
+            // AAF-724 - Make consistent response for May User", and not take the
+            // last check... too confusing.
+            Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, 
+                    ":" + rdd.ns + ":ns", access.name());
+            if (rv.isOK()) {
+                return rv;
+            } else if (rnsd.status==Result.ERR_Backend) {
+                return Result.err(rnsd);
+            }
         }
-
+        return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
+                    user, access.name(), rdd.fullName());
     }
 
     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
+        if(pdd.ns.indexOf('@')>-1) {
+            if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
+                NsDAO.Data ndd = new NsDAO.Data();
+                ndd.name = user;
+                ndd.type = NsDAO.USER;
+                ndd.parent = "";
+                return Result.ok(ndd);
+            } else {
+                return Result.err(Result.ERR_Security,"Only a User may modify User");
+            }
+        }
         Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
         if (rnsd.isOK()) {
             return mayUser(trans, user, rnsd.value, pdd, access);
@@ -576,43 +713,50 @@ public class Question {
     }
 
     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
+        // Most common occurrence... if granted Permission
         if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
             return Result.ok(ndd);
         }
+        
         String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
         // <ns>.access|:role:<role name>|<read|write>
         String ns = ndd.name;
+        boolean isRoot = ns.startsWith(Define.ROOT_NS());
         int last;
         do {
             if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
                 return Result.ok(ndd);
             }
+            if(isRoot) {
+                break;
+            }
             if ((last = ns.lastIndexOf('.')) >= 0) {
                 ns = ns.substring(0, last);
             }
         } while (last >= 0);
 
-        // Check if Access by NS perm
-        // com.att.aaf.ns|:<client ns>:role:name|<access>
-        Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
-        if (rnsd.isOK()) {
-            return rnsd;
-        } else if (rnsd.status==Result.ERR_Backend) {
-            return Result.err(rnsd);
-        }
+        // SAFETY - Do not allow these when NS is Root
+        if(!isRoot) {
+            // Check if Access by NS perm
+            // com.att.aaf.ns|:<client ns>:role:name|<access>
+            Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
+            if (rnsd.isOK()) {
+                return rnsd;
+            } else if (rnsd.status==Result.ERR_Backend) {
+                return Result.err(rnsd);
+            }
 
-        // Check if Access to Whole NS
-        // AAF-724 - Make consistent response for May User", and not take the
-        // last check... too confusing.
-        Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + pdd.ns + ":ns", access.name());
-        if (rv.isOK()) {
-            return rv;
-        } else {
-            return Result.err(Status.ERR_Denied,
-                    "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
-                    pdd.fullType(), pdd.instance, pdd.action);
+            // Check if Access to Whole NS
+            // AAF-724 - Make consistent response for May User", and not take the
+            // last check... too confusing.
+            Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + pdd.ns + ":ns", access.name());
+            if (rv.isOK()) {
+                return rv;
+            }
         }
-
+        return Result.err(Status.ERR_Denied,
+                "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
+                pdd.fullType(), pdd.instance, pdd.action);
     }
 
     public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
@@ -742,7 +886,7 @@ public class Question {
         Result<List<CredDAO.Data>> result;
         TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
         try {
-            result = credDAO.readID(trans, user);
+            result = credDAO.readIDBAth(trans, user);
         } finally {
             tt.done();
         }
@@ -755,25 +899,29 @@ public class Question {
                     trans.audit().log("Special DEBUG:", user, " does not exist in DB");
                 }
             } else {
-                Date now = new Date();//long now = System.currentTimeMillis();
-                // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.     
-                List<CredDAO.Data> cddl;
+                Date now = new Date();
+                // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.  
+                // 9/14/2019. Use TreeSet for sorting, and using only the LAST of a Tagged entry
+                Collection<CredDAO.Data> cddl;
                 if (result.value.size() > 1) {
-                    cddl = new ArrayList<>(result.value.size());
-                    for (CredDAO.Data old : result.value) {
-                        if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
-                            cddl.add(old);
+                       Map<String,CredDAO.Data> mcdd = new TreeMap<>();
+                       CredDAO.Data cdd;
+                       String tag;
+                       int pseudoTag = 0;
+                    for (CredDAO.Data rcdd : result.value) {
+                        if (rcdd.type==CredDAO.BASIC_AUTH || rcdd.type==CredDAO.BASIC_AUTH_SHA256) {
+                               if(rcdd.tag==null) {
+                                       mcdd.put(Integer.toString(++pseudoTag),rcdd);
+                               } else {
+                                       tag = rcdd.tag;
+                                       cdd = mcdd.get(tag);
+                                       if(cdd==null || cdd.expires.before(rcdd.expires)) {
+                                               mcdd.put(tag,rcdd);
+                                       }
+                               }
                         }
                     }
-                    if (cddl.size()>1) {
-                        Collections.sort(cddl,new Comparator<CredDAO.Data>() {
-                            @Override
-                            public int compare(org.onap.aaf.auth.dao.cass.CredDAO.Data a,
-                                               org.onap.aaf.auth.dao.cass.CredDAO.Data b) {
-                                return b.expires.compareTo(a.expires);
-                            }
-                        });
-                    }
+                    cddl = mcdd.values();
                 } else {
                     cddl = result.value;
                 }
@@ -792,7 +940,8 @@ public class Question {
                                 case CredDAO.BASIC_AUTH:
                                     byte[] md5=Hash.hashMD5(cred);
                                     if (Hash.compareTo(md5,dbcred)==0) {
-                                        checkLessThanDays(trans,7,now,cdd);
+                                        checkLessThanDays(trans,cldays,now,cdd);
+                                        trans.setTag(cdd.tag);
                                         return Result.ok(cdd.expires);
                                     } else if (debug!=null) {
                                         load(debug, cdd);
@@ -805,7 +954,8 @@ public class Question {
                                     byte[] hash = Hash.hashSHA256(bb.array());
     
                                     if (Hash.compareTo(hash,dbcred)==0) {
-                                        checkLessThanDays(trans,7,now,cdd);
+                                        checkLessThanDays(trans,cldays,now,cdd);
+                                        trans.setTag(cdd.tag);
                                         return Result.ok(cdd.expires);
                                     } else if (debug!=null) {
                                         load(debug, cdd);
@@ -820,34 +970,41 @@ public class Question {
                     } else {
                         if (expired==null || expired.before(cdd.expires)) {
                             expired = cdd.expires;
+                            trans.setTag(cdd.tag);
                         }
                     }
                 } // end for each
-                if (debug==null) {
-                    trans.audit().printf("No cred matches ip=%s, user=%s\n",trans.ip(),user);
-                } else {
-                    trans.audit().printf("No cred matches ip=%s, user=%s %s\n",trans.ip(),user,debug.toString());
-                }
+                
                 if (expired!=null) {
                     // Note: this is only returned if there are no good Credentials
                     rv = Result.err(Status.ERR_Security,
-                            "Credentials %s from %s expired %s",trans.user(), trans.ip(), Chrono.dateTime(expired));
+                            "Credentials expired %s",Chrono.utcStamp(expired));
+                } else {
+                    if (debug==null && alwaysSpecial) {
+                        debug = new StringBuilder();
+                    }
+                    if (debug!=null) {
+                        debug.append(trans.env().encryptor().encrypt(new String(cred)));
+                        rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
+                    }
                 }
             }
         } else {
             return Result.err(result);
         }
-        return rv == null ? Result.create((Date) null, Status.ERR_Security, "Wrong credential") : rv;
+        return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
     }
 
 
     private void load(StringBuilder debug, Data cdd) {
-        debug.append("DB Entry: user=");
+        debug.append("\nDB Entry: user=");
         debug.append(cdd.id);
         debug.append(",type=");
         debug.append(cdd.type);
         debug.append(",expires=");
         debug.append(Chrono.dateTime(cdd.expires));
+        debug.append(",tag=");
+        debug.append(cdd.tag);
         debug.append('\n');
     }
 
@@ -857,8 +1014,9 @@ public class Question {
         long cexp=cdd.expires.getTime();
         if (cexp<close) {
             int daysLeft = days-(int)((close-cexp)/86400000);
-            trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,msg=\"Password expires in less than %d day%s\"",
-                cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, daysLeft,daysLeft==1?"":"s");
+            trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
+                cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag, 
+                daysLeft,daysLeft==1?"":"s");
         }
     }
 
@@ -881,29 +1039,35 @@ public class Question {
                 tt.done();
             }
             
+        } else if (cred.type==CredDAO.FQI) {
+            cred.cred = null;
+            return Result.ok(cred);
         }
         return Result.err(Status.ERR_Security,"invalid/unreadable credential");
     }
     
     public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
-            TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
-            try {
-                switch(orig.type) {
-                    case CredDAO.BASIC_AUTH_SHA256:
-                        ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
-                        bb.putInt(orig.other);
-                        bb.put(raw);
-                        return Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
-                    case CredDAO.BASIC_AUTH:
-                        return Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
-                    default:
-                        return Result.ok(false);
-                }
-            } catch (NoSuchAlgorithmException e) {
-                return Result.err(Status.ERR_General,e.getLocalizedMessage());
-            } finally {
-                tt.done();
+        Result<Boolean> rv;
+        TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
+        try {
+            switch(orig.type) {
+                case CredDAO.BASIC_AUTH_SHA256:
+                    ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
+                    bb.putInt(orig.other);
+                    bb.put(raw);
+                    rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
+                case CredDAO.BASIC_AUTH:
+                    rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
+                case CredDAO.FQI:
+                default:
+                    rv = Result.ok(false);
             }
+        } catch (NoSuchAlgorithmException e) {
+            rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
+        } finally {
+            tt.done();
+        }
+        return rv;
     }
 
     public static final String APPROVED = "APPROVE";
@@ -949,32 +1113,32 @@ public class Question {
         Result<Void> rv = null;
 
         if (all || NsDAO.TABLE.equals(cname)) {
-            int seg[] = series(NsDAO.CACHE_SEG);
+            int[] seg = series(NsDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
         }
         if (all || PermDAO.TABLE.equals(cname)) {
-            int seg[] = series(PermDAO.CACHE_SEG);
+            int[] seg = series(PermDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
         }
         if (all || RoleDAO.TABLE.equals(cname)) {
-            int seg[] = series(RoleDAO.CACHE_SEG);
+            int[] seg = series(RoleDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
         }
         if (all || UserRoleDAO.TABLE.equals(cname)) {
-            int seg[] = series(UserRoleDAO.CACHE_SEG);
+            int[] seg = series(UserRoleDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
         }
         if (all || CredDAO.TABLE.equals(cname)) {
-            int seg[] = series(CredDAO.CACHE_SEG);
+            int[] seg = series(CredDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
         }
         if (all || CertDAO.TABLE.equals(cname)) {
-            int seg[] = series(CertDAO.CACHE_SEG);
+            int[] seg = series(CertDAO.CACHE_SEG);
             for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
             rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
         }
@@ -1099,20 +1263,22 @@ public class Question {
     }
 
     public boolean isAdmin(AuthzTrans trans, String user, String ns) {
-        Date now = new Date();
         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
-        if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
-            if (urdd.expires.after(now)) {
-                return true;
+        if (rur.isOKhasData()) {
+            Date now = new Date();
+            for (UserRoleDAO.Data urdd : rur.value){
+                if (urdd.expires.after(now)) {
+                    return true;
+                }
             }
-        }};
+        };
         return false;
     }
     
     public boolean isOwner(AuthzTrans trans, String user, String ns) {
-        Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_OWNER);
-        Date now = new Date();
+        Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
+            Date now = new Date();
             if (urdd.expires.after(now)) {
                 return true;
             }
@@ -1121,7 +1287,7 @@ public class Question {
     }
 
     public int countOwner(AuthzTrans trans, String ns) {
-        Result<List<UserRoleDAO.Data>> rur = userRoleDAO.readByRole(trans,ns+DOT_OWNER);
+        Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
         Date now = new Date();
         int count = 0;
         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){