Java tutorial
/************************************************************************* * Copyright 2009-2015 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.auth.euare; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.eucalyptus.auth.AuthException; import com.eucalyptus.auth.AuthenticationProperties; import com.eucalyptus.auth.principal.UserPrincipal; import com.eucalyptus.util.Pair; import com.eucalyptus.util.Parameters; import com.eucalyptus.util.async.AsyncExceptions; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilderSpec; /** * */ public class CachingPrincipalProvider extends RegionDelegatingPrincipalProvider { private final static AtomicReference<Pair<String, Cache<PrincipalCacheKey, PrincipalCacheValue>>> cacheReference = new AtomicReference<>(); @Override public UserPrincipal lookupCachedPrincipalByUserId(final UserPrincipal cached, final String userId, final String nonce) throws AuthException { return cache(new UserIdPrincipalCacheKey(userId, nonce), new PrincipalLoader() { @Override public UserPrincipal load(final UserPrincipal cached) throws AuthException { return CachingPrincipalProvider.super.lookupCachedPrincipalByUserId(cached, userId, nonce); } }); } @Override public UserPrincipal lookupCachedPrincipalByRoleId(final UserPrincipal cached, final String roleId, final String nonce) throws AuthException { return cache(new RoleIdPrincipalCacheKey(roleId, nonce), new PrincipalLoader() { @Override public UserPrincipal load(final UserPrincipal cached) throws AuthException { return CachingPrincipalProvider.super.lookupCachedPrincipalByRoleId(cached, roleId, nonce); } }); } @Override public UserPrincipal lookupCachedPrincipalByAccessKeyId(final UserPrincipal cached, final String keyId, final String nonce) throws AuthException { return cache(new AccessKeyIdPrincipalCacheKey(keyId, nonce), new PrincipalLoader() { @Override public UserPrincipal load(final UserPrincipal cached) throws AuthException { return CachingPrincipalProvider.super.lookupCachedPrincipalByAccessKeyId(cached, keyId, nonce); } }); } @Override public UserPrincipal lookupCachedPrincipalByCertificateId(final UserPrincipal cached, final String certificateId) throws AuthException { return cache(new CertificateIdPrincipalCacheKey(certificateId), new PrincipalLoader() { @Override public UserPrincipal load(final UserPrincipal cached) throws AuthException { return CachingPrincipalProvider.super.lookupCachedPrincipalByCertificateId(cached, certificateId); } }); } public UserPrincipal lookupCachedPrincipalByAccountNumber(final UserPrincipal cached, final String accountNumber) throws AuthException { return cache(new AccountNumberPrincipalCacheKey(accountNumber), new PrincipalLoader() { @Override public UserPrincipal load(final UserPrincipal cached) throws AuthException { return CachingPrincipalProvider.super.lookupCachedPrincipalByAccountNumber(cached, accountNumber); } }); } private UserPrincipal cache(final PrincipalCacheKey key, final PrincipalLoader loader) throws AuthException { PrincipalCacheValue principalValue = null; final Cache<PrincipalCacheKey, PrincipalCacheValue> cache = cache(); try { principalValue = cache.get(key, loader.callable(null)); if (principalValue.updated + AuthenticationProperties.getAuthorizationExpiry() < System .currentTimeMillis()) { cache.invalidate(key); // invalidate expired and refresh principalValue = cache.get(key, loader.callable(principalValue.principal)); } return principalValue.principal; } catch (final ExecutionException e) { // reuse cached value on failure within configured limit, but not for web service error responses if (!AsyncExceptions.asWebServiceError(e).isPresent() && principalValue != null && principalValue.created + AuthenticationProperties.getAuthorizationReuseExpiry() > System .currentTimeMillis()) { cache.put(key, new PrincipalCacheValue(principalValue)); return principalValue.principal; } if (e.getCause() instanceof AuthException) { throw (AuthException) e.getCause(); } else { throw new AuthException(e); } } } private static Cache<PrincipalCacheKey, PrincipalCacheValue> cache() { Cache<PrincipalCacheKey, PrincipalCacheValue> cache; final Pair<String, Cache<PrincipalCacheKey, PrincipalCacheValue>> cachePair = cacheReference.get(); final String cacheSpec = AuthenticationProperties.AUTHORIZATION_CACHE; if (cachePair == null || !cacheSpec.equals(cachePair.getLeft())) { final Pair<String, Cache<PrincipalCacheKey, PrincipalCacheValue>> newCachePair = Pair.pair(cacheSpec, cache(cacheSpec)); if (cacheReference.compareAndSet(cachePair, newCachePair) || cachePair == null) { cache = newCachePair.getRight(); } else { cache = cachePair.getRight(); } } else { cache = cachePair.getRight(); } return cache; } private static Cache<PrincipalCacheKey, PrincipalCacheValue> cache(final String cacheSpec) { return CacheBuilder.from(CacheBuilderSpec.parse(cacheSpec)).build(); } private static abstract class PrincipalLoader { abstract UserPrincipal load(UserPrincipal cached) throws AuthException; Callable<PrincipalCacheValue> callable(final UserPrincipal cached) { return new Callable<PrincipalCacheValue>() { @Override public PrincipalCacheValue call() throws AuthException { return new PrincipalCacheValue(load(cached)); } }; } } private static final class PrincipalCacheValue { private final long created; private final long updated; @Nonnull private final UserPrincipal principal; public PrincipalCacheValue(@Nonnull final UserPrincipal principal) { Parameters.checkParam("principal", principal, notNullValue()); this.created = System.currentTimeMillis(); this.updated = created; this.principal = principal; } public PrincipalCacheValue(@Nonnull final PrincipalCacheValue value) { Parameters.checkParam("value", value, notNullValue()); this.created = value.created; this.updated = System.currentTimeMillis(); this.principal = value.principal; } } private static abstract class PrincipalCacheKey { @Nonnull private final String identifier; @Nullable private final String nonce; protected PrincipalCacheKey(@Nonnull final String identifier, @Nullable final String nonce) { Parameters.checkParam("identifier", identifier, not(isEmptyOrNullString())); this.identifier = identifier; this.nonce = nonce; } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final PrincipalCacheKey that = (PrincipalCacheKey) o; if (!identifier.equals(that.identifier)) return false; if (nonce != null ? !nonce.equals(that.nonce) : that.nonce != null) return false; return true; } @Override public int hashCode() { int result = identifier.hashCode(); result = 31 * result + (nonce != null ? nonce.hashCode() : 0); return result; } } private static final class UserIdPrincipalCacheKey extends PrincipalCacheKey { protected UserIdPrincipalCacheKey(@Nonnull final String identifier, @Nullable final String nonce) { super(identifier, nonce); } } private static final class RoleIdPrincipalCacheKey extends PrincipalCacheKey { protected RoleIdPrincipalCacheKey(@Nonnull final String identifier, @Nullable final String nonce) { super(identifier, nonce); } } private static final class AccessKeyIdPrincipalCacheKey extends PrincipalCacheKey { protected AccessKeyIdPrincipalCacheKey(@Nonnull final String identifier, @Nullable final String nonce) { super(identifier, nonce); } } private static final class CertificateIdPrincipalCacheKey extends PrincipalCacheKey { protected CertificateIdPrincipalCacheKey(@Nonnull final String identifier) { super(identifier, null); } } private static final class AccountNumberPrincipalCacheKey extends PrincipalCacheKey { protected AccountNumberPrincipalCacheKey(@Nonnull final String identifier) { super(identifier, null); } } }