com.eucalyptus.auth.euare.CachingPrincipalProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.auth.euare.CachingPrincipalProvider.java

Source

/*************************************************************************
 * 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);
        }
    }
}