package io.dropwizard.auth;

import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheBuilderSpec;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.UncheckedExecutionException;
import org.apache.commons.lang3.tuple.ImmutablePair;

import java.security.Principal;
import java.util.function.Predicate;

import static com.codahale.metrics.MetricRegistry.name;

An Authorizer decorator which uses a Guava Cache to temporarily cache principals' role associations.

Cache entries include both inclusion and exclusion of a principal within a given role.

Type parameters:
  • <P> – the type of principals on which the authorizer operates
/** * An {@link Authorizer} decorator which uses a Guava {@link Cache} to * temporarily cache principals' role associations. * <p> * Cache entries include both inclusion and exclusion of a principal * within a given role. * * @param <P> the type of principals on which the authorizer operates */
public class CachingAuthorizer<P extends Principal> implements Authorizer<P> { private final Authorizer<P> underlying; private final Meter cacheMisses; private final Timer getsTimer; // A cache which maps (principal, role) tuples to boolean // authorization states. // // A cached value of `true` indicates that the key's principal is // authorized to assume the given role. False values indicate the // principal is not authorized to assume the role. // // `null` cache values are interpreted as cache misses, and will // thus result in read through to the underlying `Authorizer`. private final LoadingCache<ImmutablePair<P, String>, Boolean> cache;
Creates a new cached authorizer.
Params:
  • metricRegistry – the application's registry of metrics
  • authorizer – the underlying authorizer
  • cacheSpec – CacheBuilderSpec
/** * Creates a new cached authorizer. * * @param metricRegistry the application's registry of metrics * @param authorizer the underlying authorizer * @param cacheSpec {@link CacheBuilderSpec} */
public CachingAuthorizer( final MetricRegistry metricRegistry, final Authorizer<P> authorizer, final CacheBuilderSpec cacheSpec ) { this(metricRegistry, authorizer, CacheBuilder.from(cacheSpec)); }
Creates a new cached authorizer.
Params:
  • metricRegistry – the application's registry of metrics
  • authorizer – the underlying authorizer
  • builder – a CacheBuilder
/** * Creates a new cached authorizer. * * @param metricRegistry the application's registry of metrics * @param authorizer the underlying authorizer * @param builder a {@link CacheBuilder} */
public CachingAuthorizer( final MetricRegistry metricRegistry, final Authorizer<P> authorizer, final CacheBuilder<Object, Object> builder ) { this.underlying = authorizer; this.cacheMisses = metricRegistry.meter(name(authorizer.getClass(), "cache-misses")); this.getsTimer = metricRegistry.timer(name(authorizer.getClass(), "gets")); this.cache = builder.recordStats().build(new CacheLoader<ImmutablePair<P, String>, Boolean>() { @Override public Boolean load(ImmutablePair<P, String> key) throws Exception { cacheMisses.mark(); return underlying.authorize(key.left, key.right); } }); } @Override public boolean authorize(P principal, String role) { final Timer.Context context = getsTimer.time(); try { final ImmutablePair<P, String> cacheKey = ImmutablePair.of(principal, role); return cache.getUnchecked(cacheKey); } catch (UncheckedExecutionException e) { Throwables.throwIfUnchecked(e.getCause()); throw e; } finally { context.stop(); } }
Discards any cached role associations for the given principal and role.
Params:
  • principal –
  • role –
/** * Discards any cached role associations for the given principal and role. * * @param principal * @param role */
public void invalidate(P principal, String role) { cache.invalidate(ImmutablePair.of(principal, role)); }
Discards any cached role associations for the given principal.
Params:
  • principal –
/** * Discards any cached role associations for the given principal. * * @param principal */
public void invalidate(P principal) { final Predicate<ImmutablePair<P, String>> predicate = cacheKey -> cacheKey.getLeft().equals(principal); cache.invalidateAll(Sets.filter(cache.asMap().keySet(), predicate::test)); }
Discards any cached role associations for the given collection of principals.
Params:
  • principals – a list of principals
/** * Discards any cached role associations for the given collection * of principals. * * @param principals a list of principals */
public void invalidateAll(Iterable<P> principals) { final Predicate<ImmutablePair<P, String>> predicate = cacheKey -> Iterables.contains(principals, cacheKey.getLeft()); cache.invalidateAll(Sets.filter(cache.asMap().keySet(), predicate::test)); }
Discards any cached role associations for principals satisfying the given predicate.
Params:
  • predicate – a predicate to filter credentials
/** * Discards any cached role associations for principals satisfying * the given predicate. * * @param predicate a predicate to filter credentials */
public void invalidateAll(Predicate<? super P> predicate) { final Predicate<ImmutablePair<P, String>> nestedPredicate = cacheKey -> predicate.test(cacheKey.getLeft()); cache.invalidateAll(Sets.filter(cache.asMap().keySet(), nestedPredicate::test)); }
Discards all cached role associations.
/** * Discards all cached role associations. */
public void invalidateAll() { cache.invalidateAll(); }
Returns the number of principals for which there are cached role associations.
Returns:the number of cached principals
/** * Returns the number of principals for which there are cached * role associations. * * @return the number of cached principals */
public long size() { return cache.size(); }
Returns a set of statistics about the cache contents and usage.
Returns:a set of statistics about the cache contents and usage
/** * Returns a set of statistics about the cache contents and usage. * * @return a set of statistics about the cache contents and usage */
public CacheStats stats() { return cache.stats(); } }