Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions cda-gui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 21 additions & 13 deletions cwms-data-api/src/main/java/cwms/cda/data/dao/AuthDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import cwms.cda.security.DataApiPrincipal;
import cwms.cda.security.MissingRolesException;
import cwms.cda.security.Role;
import cwms.cda.util.LruTtlMap;
import io.javalin.core.security.RouteRole;
import io.javalin.http.Context;
import io.javalin.http.HttpCode;
Expand Down Expand Up @@ -112,6 +113,9 @@ public class AuthDao extends Dao<DataApiPrincipal> {
*/
private static ThreadLocal<AuthDao> instance = new ThreadLocal<>();

// start with ten second cache
private static final LruTtlMap<String, DataApiPrincipal> authCache = new LruTtlMap<>(150, 10_000);

private AuthDao(DSLContext dsl, String defaultOffice) {
super(dsl);
if (getDbVersion(dsl) < Dao.CWMS_23_03_16) {
Expand Down Expand Up @@ -180,9 +184,11 @@ public Optional<DataApiPrincipal> getByUniqueName(String uniqueName, String limi
* @throws CwmsAuthException throw for any issue with verification of Key or user information.
*/
public DataApiPrincipal getByApiKey(String apikey) throws CwmsAuthException {
String userName = checkKey(apikey);
Set<RouteRole> roles = getRolesForUser(userName);
return new DataApiPrincipal(userName,roles);
return authCache.computeIfAbsent(apikey, providedKey -> {
String userName = checkKey(providedKey);
Set<RouteRole> roles = getRolesForUser(userName);
return new DataApiPrincipal(userName,roles);
});
}

/**
Expand Down Expand Up @@ -637,16 +643,18 @@ public void resetContext(DSLContext dslContext) {
* @throws CwmsAuthException if anything goes wrong with the database query.
*/
public Optional<DataApiPrincipal> getPrincipalFromPrincipal(String principal) throws CwmsAuthException {
String user = userForPrincipal(principal);
if (user != null) {
Set<RouteRole> roles = this.getRolesForUser(user);
// In this case "cac_auth" just means the user is an actually user verify by some sort of
// identify management system. E.g. "not an apikey"
roles.add(new Role("cac_auth"));
return Optional.of(new DataApiPrincipal(user, roles));
} else {
return Optional.empty();
}
return Optional.ofNullable(authCache.computeIfAbsent(principal, newPrincipal -> {
String user = userForPrincipal(principal);
if (user != null) {
Set<RouteRole> roles = this.getRolesForUser(user);
// In this case "cac_auth" just means the user is an actually user verify by some sort of
// identify management system. E.g. "not an apikey"
roles.add(new Role("cac_auth"));
return new DataApiPrincipal(user, roles);
} else {
return null;
}
}));
}


Expand Down
150 changes: 150 additions & 0 deletions cwms-data-api/src/main/java/cwms/cda/util/LruTtlMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cwms.cda.util;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class LruTtlMap<K,V> implements Map<K,V> {

private final long ttl;
private final int maxSize;
private final Map<K, TtlEntry<V>> map;

public LruTtlMap(int maxSize, int ttl)
{
this.ttl = ttl;
this.maxSize = maxSize;
map = Collections.synchronizedMap(
new LinkedHashMap<K, TtlEntry<V>>(maxSize, 0.75f, true)
{
@Override
protected boolean removeEldestEntry(Map.Entry<K,LruTtlMap.TtlEntry<V>> eldest) {
return this.size() > LruTtlMap.this.maxSize;
};
}
);
}

@Override
public int size() {
return map.size();
}

@Override
public boolean isEmpty() {
return map.isEmpty();
}

@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}

@Override
public boolean containsValue(Object value) {
return false; // todo?
}

@Override
public V get(Object key) {
var entry = map.get(key);
V ret = null;

if (entry != null && (System.currentTimeMillis() - entry.insertTime) >= ttl) {
map.remove(key);
ret = null;
} else if (entry != null) {
ret = entry.value;
}
return ret;
}

@Override
public V put(K key, V value) {
return value != null ? map.put(key, new TtlEntry<>(value)).value : null;
}

@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return map.computeIfAbsent(key, newKey -> new TtlEntry<V>(mappingFunction.apply(newKey))).value;
}

@Override
public V remove(Object key) {
var entry = map.remove(key);
if (entry != null) {
return entry.value;
} else {
return null;
}
}

@Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach((k,v) -> put(k,v));
}

@Override
public void clear() {
map.clear();;
}

@Override
public Set<K> keySet() {
return map.keySet();
}

@Override
public Collection<V> values() {
return map.values()
.stream()
.map(e -> e != null ? e.value : null)
.collect(Collectors.toSet());
}

@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet()
.stream()
.map(es -> {
var v = es.getValue();
return new Entry<K,V>() {

@Override
public K getKey() {
return es.getKey();
}

@Override
public V getValue() {
return v != null ? v.value : null;
}

@Override
public V setValue(V value) {
throw new UnsupportedOperationException(
"method 'setValue' is not supported."
);
}
};
})
.collect(Collectors.toSet());
}

private static class TtlEntry<V>
{
private final long insertTime;
private final V value;

public TtlEntry(V value)
{
this.value = value;
insertTime = System.currentTimeMillis();
}
}

}
Loading