X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=cadi%2Fcore%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Faaf%2Fcadi%2FAbsUserCache.java;h=d9d4474d309fbdc449e2281b4897f3e8d829902e;hb=refs%2Fchanges%2F75%2F65275%2F1;hp=3963189406b1f05a69ce6f4b5003d6d648053df9;hpb=824dc7b5fc0e1ccdf7f460479aff344727f0f01e;p=aaf%2Fauthz.git diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java b/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java index 39631894..d9d4474d 100644 --- a/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/AbsUserCache.java @@ -48,420 +48,420 @@ import org.onap.aaf.cadi.principal.CachedBasicPrincipal; * */ public abstract class AbsUserCache { - // Need an obvious key for when there is no Authentication Cred - private static final String NO_CRED = "NoCred"; - static final int MIN_INTERVAL = 1000*60; // Min 1 min - static final int MAX_INTERVAL = 1000*60*60*4; // 4 hour max - private static Timer timer; - // Map of userName to User - private final Map> userMap; - private static final Map missMap = new TreeMap<>(); - private final Symm missEncrypt; - - private Clean clean; - protected Access access; - - protected AbsUserCache(Access access, long cleanInterval, int highCount, int usageCount) { - this.access = access; - Symm s; - try { - byte[] gennedKey = Symm.keygen(); - s = Symm.obtain(new ByteArrayInputStream(gennedKey)); - } catch (IOException e) { - access.log(e); - s = Symm.base64noSplit; - } - missEncrypt = s; - - userMap = new ConcurrentHashMap<>(); + // Need an obvious key for when there is no Authentication Cred + private static final String NO_CRED = "NoCred"; + static final int MIN_INTERVAL = 1000*60; // Min 1 min + static final int MAX_INTERVAL = 1000*60*60*4; // 4 hour max + private static Timer timer; + // Map of userName to User + private final Map> userMap; + private static final Map missMap = new TreeMap<>(); + private final Symm missEncrypt; + + private Clean clean; + protected Access access; + + protected AbsUserCache(Access access, long cleanInterval, int highCount, int usageCount) { + this.access = access; + Symm s; + try { + byte[] gennedKey = Symm.keygen(); + s = Symm.obtain(new ByteArrayInputStream(gennedKey)); + } catch (IOException e) { + access.log(e); + s = Symm.base64noSplit; + } + missEncrypt = s; + + userMap = new ConcurrentHashMap<>(); - - if(cleanInterval>0) { - cleanInterval = Math.max(MIN_INTERVAL, cleanInterval); - synchronized(AbsUserCache.class) { // Lazy instantiate.. in case there is no cleanup needed - if(timer==null) { - timer = new Timer("CADI Cleanup Timer",true); - } - - timer.schedule(clean = new Clean(access, cleanInterval, highCount, usageCount), cleanInterval, cleanInterval); - access.log(Access.Level.INIT, "Cleaning Thread initialized with interval of",cleanInterval, "ms and max objects of", highCount); - } - } - } - - @SuppressWarnings("unchecked") - public AbsUserCache(AbsUserCache cache) { - this.access = cache.access; - userMap = cache.userMap; - missEncrypt = cache.missEncrypt; - - synchronized(AbsUserCache.class) { - if(cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) { - cache.clean.lur=(CachingLur)this; - } - } - } + + if(cleanInterval>0) { + cleanInterval = Math.max(MIN_INTERVAL, cleanInterval); + synchronized(AbsUserCache.class) { // Lazy instantiate.. in case there is no cleanup needed + if(timer==null) { + timer = new Timer("CADI Cleanup Timer",true); + } + + timer.schedule(clean = new Clean(access, cleanInterval, highCount, usageCount), cleanInterval, cleanInterval); + access.log(Access.Level.INIT, "Cleaning Thread initialized with interval of",cleanInterval, "ms and max objects of", highCount); + } + } + } + + @SuppressWarnings("unchecked") + public AbsUserCache(AbsUserCache cache) { + this.access = cache.access; + userMap = cache.userMap; + missEncrypt = cache.missEncrypt; + + synchronized(AbsUserCache.class) { + if(cache.clean!=null && cache.clean.lur==null && this instanceof CachingLur) { + cache.clean.lur=(CachingLur)this; + } + } + } - protected void setLur(CachingLur lur) { - if(clean!=null)clean.lur = lur; - - } - - protected void addUser(User user) { - Principal p = user.principal; - String key; - try { - if(p instanceof GetCred) { - key = missKey(p.getName(), ((GetCred)p).getCred()); - } else { - byte[] cred; - if((cred=user.getCred())==null) { - key = user.name + NO_CRED; - } else { - key = missKey(user.name,cred); - } - } - } catch (IOException e) { - access.log(e); - return; - } - userMap.put(key, user); - } + protected void setLur(CachingLur lur) { + if(clean!=null)clean.lur = lur; + + } + + protected void addUser(User user) { + Principal p = user.principal; + String key; + try { + if(p instanceof GetCred) { + key = missKey(p.getName(), ((GetCred)p).getCred()); + } else { + byte[] cred; + if((cred=user.getCred())==null) { + key = user.name + NO_CRED; + } else { + key = missKey(user.name,cred); + } + } + } catch (IOException e) { + access.log(e); + return; + } + userMap.put(key, user); + } - // Useful for looking up by WebToken, etc. - protected void addUser(String key, User user) { - userMap.put(key, user); - } - - /** - * Add miss to missMap. If Miss exists, or too many tries, returns false. - * - * otherwise, returns true to allow another attempt. - * - * @param key - * @param bs - * @return - * @throws IOException - */ - protected synchronized boolean addMiss(String key, byte[] bs) { - String mkey; - try { - mkey = missKey(key,bs); - } catch (IOException e) { - access.log(e); - return false; - } - Miss miss = missMap.get(mkey); - if(miss==null) { - missMap.put(mkey, new Miss(bs,clean==null?MIN_INTERVAL:clean.timeInterval,key)); - return true; - } - return miss.mayContinue(); - } + // Useful for looking up by WebToken, etc. + protected void addUser(String key, User user) { + userMap.put(key, user); + } + + /** + * Add miss to missMap. If Miss exists, or too many tries, returns false. + * + * otherwise, returns true to allow another attempt. + * + * @param key + * @param bs + * @return + * @throws IOException + */ + protected synchronized boolean addMiss(String key, byte[] bs) { + String mkey; + try { + mkey = missKey(key,bs); + } catch (IOException e) { + access.log(e); + return false; + } + Miss miss = missMap.get(mkey); + if(miss==null) { + missMap.put(mkey, new Miss(bs,clean==null?MIN_INTERVAL:clean.timeInterval,key)); + return true; + } + return miss.mayContinue(); + } - protected Miss missed(String key, byte[] bs) throws IOException { - return missMap.get(missKey(key,bs)); - } + protected Miss missed(String key, byte[] bs) throws IOException { + return missMap.get(missKey(key,bs)); + } - protected User getUser(Principal principal) { - String key; - if(principal instanceof GetCred) { - GetCred gc = (GetCred)principal; - try { - key = missKey(principal.getName(), gc.getCred()); - } catch (IOException e) { - access.log(e, "Error getting key from Principal"); - key = principal.getName(); - } - } else { - key = principal.getName()+NO_CRED; - } - User u = userMap.get(key); - if(u!=null) { - u.incCount(); - } - return u; - } - - protected User getUser(CachedBasicPrincipal cbp) { - return getUser(cbp.getName(), cbp.getCred()); - } - - protected User getUser(String user, byte[] cred) { - User u; - String key=null; - try { - key =missKey(user,cred); - } catch (IOException e) { - access.log(e); - return null; - } - u = userMap.get(key); - if(u!=null) { - if(u.permExpired()) { - userMap.remove(key); - u=null; - } else { - u.incCount(); - } - } - return u; - } - - /** - * Removes User from the Cache - * @param user - */ - protected void remove(User user) { - userMap.remove(user.principal.getName()); - } - - /** - * Removes user from the Cache - * - * @param user - */ - public void remove(String user) { - Object o = userMap.remove(user); - if(o!=null) { - access.log(Level.INFO, user,"removed from Client Cache by Request"); - } - } - - /** - * Clear all Users from the Client Cache - */ - public void clearAll() { - userMap.clear(); - } - - public final List dumpInfo() { - List rv = new ArrayList<>(); - for(User user : userMap.values()) { - rv.add(new DumpInfo(user)); - } - return rv; - } + protected User getUser(Principal principal) { + String key; + if(principal instanceof GetCred) { + GetCred gc = (GetCred)principal; + try { + key = missKey(principal.getName(), gc.getCred()); + } catch (IOException e) { + access.log(e, "Error getting key from Principal"); + key = principal.getName(); + } + } else { + key = principal.getName()+NO_CRED; + } + User u = userMap.get(key); + if(u!=null) { + u.incCount(); + } + return u; + } + + protected User getUser(CachedBasicPrincipal cbp) { + return getUser(cbp.getName(), cbp.getCred()); + } + + protected User getUser(String user, byte[] cred) { + User u; + String key=null; + try { + key =missKey(user,cred); + } catch (IOException e) { + access.log(e); + return null; + } + u = userMap.get(key); + if(u!=null) { + if(u.permExpired()) { + userMap.remove(key); + u=null; + } else { + u.incCount(); + } + } + return u; + } + + /** + * Removes User from the Cache + * @param user + */ + protected void remove(User user) { + userMap.remove(user.principal.getName()); + } + + /** + * Removes user from the Cache + * + * @param user + */ + public void remove(String user) { + Object o = userMap.remove(user); + if(o!=null) { + access.log(Level.INFO, user,"removed from Client Cache by Request"); + } + } + + /** + * Clear all Users from the Client Cache + */ + public void clearAll() { + userMap.clear(); + } + + public final List dumpInfo() { + List rv = new ArrayList<>(); + for(User user : userMap.values()) { + rv.add(new DumpInfo(user)); + } + return rv; + } - /** - * The default behavior of a LUR is to not handle something exclusively. - */ - public boolean handlesExclusively(Permission ... pond) { - return false; - } - - /** - * Container calls when cleaning up... - * - * If overloading in Derived class, be sure to call "super.destroy()" - */ - public void destroy() { - if(timer!=null) { - timer.purge(); - timer.cancel(); - } - } - - + /** + * The default behavior of a LUR is to not handle something exclusively. + */ + public boolean handlesExclusively(Permission ... pond) { + return false; + } + + /** + * Container calls when cleaning up... + * + * If overloading in Derived class, be sure to call "super.destroy()" + */ + public void destroy() { + if(timer!=null) { + timer.purge(); + timer.cancel(); + } + } + + - // Simple map of Group name to a set of User Names - // private Map> groupMap = new HashMap<>(); + // Simple map of Group name to a set of User Names + // private Map> groupMap = new HashMap<>(); - /** - * Class to hold a small subset of the data, because we don't want to expose actual Permission or User Objects - */ - public final class DumpInfo { - public String user; - public List perms; - - public DumpInfo(User user) { - this.user = user.principal.getName(); - perms = new ArrayList<>(user.perms.keySet()); - } - } - - /** - * Clean will examine resources, and remove those that have expired. - * - * If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run - * without checking contents more than once, making a good average "high" in the minimum speed. - * - * @author Jonathan - * - */ - private final class Clean extends TimerTask { - private final Access access; - private CachingLur lur; - - // The idea here is to not be too restrictive on a high, but to Expire more items by - // shortening the time to expire. This is done by judiciously incrementing "advance" - // when the "highs" are exceeded. This effectively reduces numbers of cached items quickly. - private final int high; - private long advance; - private final long timeInterval; - private final int usageTriggerCount; - - public Clean(Access access, long cleanInterval, int highCount, int usageTriggerCount) { - this.access = access; - lur = null; - high = highCount; - timeInterval = cleanInterval; - advance = 0; - this.usageTriggerCount=usageTriggerCount; - } - public void run() { - int renewed = 0; - int count = 0; - int total = 0; - try { - // look at now. If we need to expire more by increasing "now" by "advance" - ArrayList> al = new ArrayList<>(userMap.values().size()); - al.addAll(0, userMap.values()); - long now = System.currentTimeMillis() + advance; - for(User user : al) { - ++total; - if(user.count>usageTriggerCount) { - boolean touched = false, removed=false; - if(user.principal instanceof CachedPrincipal) { - CachedPrincipal cp = (CachedPrincipal)user.principal; - if(cp.expires() < now) { - switch(cp.revalidate(null)) { - case INACCESSIBLE: - access.log(Level.AUDIT, "AAF Inaccessible. Keeping credentials"); - break; - case REVALIDATED: - user.resetCount(); - touched = true; - break; - default: - user.resetCount(); - remove(user); - ++count; - removed = true; - break; - } - } - } - - if(!removed && lur!=null && user.permExpires<= now ) { - if(lur.reload(user).equals(Resp.REVALIDATED)) { - user.renewPerm(); - access.log(Level.DEBUG, "Reloaded Perms for",user); - touched = true; - } - } - user.resetCount(); - if(touched) { - ++renewed; - } - - } else { - if(user.permExpired()) { - remove(user); - ++count; - } - } - } - - // Clean out Misses - int missTotal = missMap.keySet().size(); - int miss = 0; - if(missTotal>0) { - ArrayList keys = new ArrayList<>(missTotal); - keys.addAll(missMap.keySet()); - for(String key : keys) { - Miss m = missMap.get(key); - if(m!=null) { - long timeLeft = m.timestamp - System.currentTimeMillis(); - if(timeLeft<0) { - synchronized(missMap) { - missMap.remove(key); - } - access.log(Level.INFO, m.name, " has been removed from Missed Credential Map (" + m.tries + " invalid tries)"); - ++miss; - } else { - access.log(Level.INFO, m.name, " remains in Missed Credential Map (" + m.tries + " invalid tries) for " + (timeLeft/1000) + " more seconds"); - } - } - } - } - - if(count+renewed+miss>0) { - access.log(Level.INFO, (lur==null?"Cache":lur.getClass().getSimpleName()), "removed",count, - "and renewed",renewed,"expired Permissions out of", total,"and removed", miss, "password misses out of",missTotal); - } - - // If High (total) is reached during this period, increase the number of expired services removed for next time. - // There's no point doing it again here, as there should have been cleaned items. - if(total>high) { - // advance cleanup by 10%, without getting greater than timeInterval. - advance = Math.min(timeInterval, advance+(timeInterval/10)); - } else { - // reduce advance by 10%, without getting lower than 0. - advance = Math.max(0, advance-(timeInterval/10)); - } - } catch (Exception e) { - access.log(Level.ERROR,e.getMessage()); - } - } - } + /** + * Class to hold a small subset of the data, because we don't want to expose actual Permission or User Objects + */ + public final class DumpInfo { + public String user; + public List perms; + + public DumpInfo(User user) { + this.user = user.principal.getName(); + perms = new ArrayList<>(user.perms.keySet()); + } + } + + /** + * Clean will examine resources, and remove those that have expired. + * + * If "highs" have been exceeded, then we'll expire 10% more the next time. This will adjust after each run + * without checking contents more than once, making a good average "high" in the minimum speed. + * + * @author Jonathan + * + */ + private final class Clean extends TimerTask { + private final Access access; + private CachingLur lur; + + // The idea here is to not be too restrictive on a high, but to Expire more items by + // shortening the time to expire. This is done by judiciously incrementing "advance" + // when the "highs" are exceeded. This effectively reduces numbers of cached items quickly. + private final int high; + private long advance; + private final long timeInterval; + private final int usageTriggerCount; + + public Clean(Access access, long cleanInterval, int highCount, int usageTriggerCount) { + this.access = access; + lur = null; + high = highCount; + timeInterval = cleanInterval; + advance = 0; + this.usageTriggerCount=usageTriggerCount; + } + public void run() { + int renewed = 0; + int count = 0; + int total = 0; + try { + // look at now. If we need to expire more by increasing "now" by "advance" + ArrayList> al = new ArrayList<>(userMap.values().size()); + al.addAll(0, userMap.values()); + long now = System.currentTimeMillis() + advance; + for(User user : al) { + ++total; + if(user.count>usageTriggerCount) { + boolean touched = false, removed=false; + if(user.principal instanceof CachedPrincipal) { + CachedPrincipal cp = (CachedPrincipal)user.principal; + if(cp.expires() < now) { + switch(cp.revalidate(null)) { + case INACCESSIBLE: + access.log(Level.AUDIT, "AAF Inaccessible. Keeping credentials"); + break; + case REVALIDATED: + user.resetCount(); + touched = true; + break; + default: + user.resetCount(); + remove(user); + ++count; + removed = true; + break; + } + } + } + + if(!removed && lur!=null && user.permExpires<= now ) { + if(lur.reload(user).equals(Resp.REVALIDATED)) { + user.renewPerm(); + access.log(Level.DEBUG, "Reloaded Perms for",user); + touched = true; + } + } + user.resetCount(); + if(touched) { + ++renewed; + } + + } else { + if(user.permExpired()) { + remove(user); + ++count; + } + } + } + + // Clean out Misses + int missTotal = missMap.keySet().size(); + int miss = 0; + if(missTotal>0) { + ArrayList keys = new ArrayList<>(missTotal); + keys.addAll(missMap.keySet()); + for(String key : keys) { + Miss m = missMap.get(key); + if(m!=null) { + long timeLeft = m.timestamp - System.currentTimeMillis(); + if(timeLeft<0) { + synchronized(missMap) { + missMap.remove(key); + } + access.log(Level.INFO, m.name, " has been removed from Missed Credential Map (" + m.tries + " invalid tries)"); + ++miss; + } else { + access.log(Level.INFO, m.name, " remains in Missed Credential Map (" + m.tries + " invalid tries) for " + (timeLeft/1000) + " more seconds"); + } + } + } + } + + if(count+renewed+miss>0) { + access.log(Level.INFO, (lur==null?"Cache":lur.getClass().getSimpleName()), "removed",count, + "and renewed",renewed,"expired Permissions out of", total,"and removed", miss, "password misses out of",missTotal); + } + + // If High (total) is reached during this period, increase the number of expired services removed for next time. + // There's no point doing it again here, as there should have been cleaned items. + if(total>high) { + // advance cleanup by 10%, without getting greater than timeInterval. + advance = Math.min(timeInterval, advance+(timeInterval/10)); + } else { + // reduce advance by 10%, without getting lower than 0. + advance = Math.max(0, advance-(timeInterval/10)); + } + } catch (Exception e) { + access.log(Level.ERROR,e.getMessage()); + } + } + } - private String missKey(String name, byte[] bs) throws IOException { - return name + Hash.toHex(missEncrypt.encode(bs)); - } + private String missKey(String name, byte[] bs) throws IOException { + return name + Hash.toHex(missEncrypt.encode(bs)); + } - protected static class Miss { - private static final int MAX_TRIES = 3; + protected static class Miss { + private static final int MAX_TRIES = 3; - long timestamp; + long timestamp; - private long timetolive; + private long timetolive; - private long tries; + private long tries; - private final String name; - - public Miss(final byte[] first, final long timeInterval, final String name) { - timestamp = System.currentTimeMillis() + timeInterval; - this.timetolive = timeInterval; - tries = 0L; - this.name = name; - } - - - public synchronized boolean mayContinue() { - long ts = System.currentTimeMillis(); - if(ts>timestamp) { - tries = 0; - timestamp = ts + timetolive; - } else if(MAX_TRIES <= ++tries) { - return false; - } - return true; - } - - } - - /** - * Report on state - */ - public String toString() { - return getClass().getSimpleName() + - " Cache:\n Users Cached: " + - userMap.size() + - "\n Misses Saved: " + - missMap.size() + - '\n'; - - } + private final String name; + + public Miss(final byte[] first, final long timeInterval, final String name) { + timestamp = System.currentTimeMillis() + timeInterval; + this.timetolive = timeInterval; + tries = 0L; + this.name = name; + } + + + public synchronized boolean mayContinue() { + long ts = System.currentTimeMillis(); + if(ts>timestamp) { + tries = 0; + timestamp = ts + timetolive; + } else if(MAX_TRIES <= ++tries) { + return false; + } + return true; + } + + } + + /** + * Report on state + */ + public String toString() { + return getClass().getSimpleName() + + " Cache:\n Users Cached: " + + userMap.size() + + "\n Misses Saved: " + + missMap.size() + + '\n'; + + } - public void clear(Principal p, StringBuilder sb) { - sb.append(toString()); - userMap.clear(); - missMap.clear(); - access.log(Level.AUDIT, p.getName(),"has cleared User Cache in",getClass().getSimpleName()); - sb.append("Now cleared\n"); - } + public void clear(Principal p, StringBuilder sb) { + sb.append(toString()); + userMap.clear(); + missMap.clear(); + access.log(Level.AUDIT, p.getName(),"has cleared User Cache in",getClass().getSimpleName()); + sb.append("Now cleared\n"); + } } \ No newline at end of file