com.eucalyptus.auth.euare.persist.DatabasePrincipalProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.auth.euare.persist.DatabasePrincipalProvider.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.persist;

import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.hibernate.FlushMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.exception.ConstraintViolationException;
import com.eucalyptus.auth.AccessKeys;
import com.eucalyptus.auth.euare.Accounts;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.InvalidAccessKeyAuthException;
import com.eucalyptus.auth.api.PrincipalProvider;
import com.eucalyptus.auth.euare.EuareServerCertificateUtil;
import com.eucalyptus.auth.euare.persist.entities.AccessKeyEntity;
import com.eucalyptus.auth.euare.persist.entities.AccountEntity;
import com.eucalyptus.auth.euare.persist.entities.CertificateEntity;
import com.eucalyptus.auth.euare.persist.entities.InstanceProfileEntity;
import com.eucalyptus.auth.euare.persist.entities.ReservedNameEntity;
import com.eucalyptus.auth.euare.persist.entities.RoleEntity;
import com.eucalyptus.auth.euare.persist.entities.UserEntity;
import com.eucalyptus.auth.euare.principal.EuareRole;
import com.eucalyptus.auth.euare.principal.EuareUser;
import com.eucalyptus.auth.euare.principal.GlobalNamespace;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.principal.AccountIdentifiers;
import com.eucalyptus.auth.principal.Certificate;
import com.eucalyptus.auth.euare.principal.EuareInstanceProfile;
import com.eucalyptus.auth.principal.InstanceProfile;
import com.eucalyptus.auth.principal.PolicyVersion;
import com.eucalyptus.auth.principal.Role;
import com.eucalyptus.auth.principal.SecurityTokenContent;
import com.eucalyptus.auth.principal.UserPrincipal;
import com.eucalyptus.auth.euare.UserPrincipalImpl;
import com.eucalyptus.auth.tokens.SecurityTokenManager;
import com.eucalyptus.component.annotation.ComponentNamed;
import com.eucalyptus.component.auth.SystemCredentials;
import com.eucalyptus.component.id.Euare;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.TransactionException;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.util.CollectionUtils;
import com.eucalyptus.auth.principal.OwnerFullName;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;

/**
 *
 */
@ComponentNamed("localPrincipalProvider")
public class DatabasePrincipalProvider implements PrincipalProvider {

    @Override
    public UserPrincipal lookupPrincipalByUserId(final String userId, final String nonce) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(UserEntity.class)) {
            try {
                final UserEntity user = DatabaseAuthUtils.getUnique(UserEntity.class, "userId", userId);
                return decorateCredentials(new UserPrincipalImpl(user), nonce, user.getToken());
            } catch (Exception e) {
                throw new AuthException(AuthException.NO_SUCH_USER, e);
            }
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByRoleId(final String roleId, final String nonce) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(RoleEntity.class)) {
            final EuareRole role = Accounts.lookupRoleById(roleId);
            return decorateCredentials(Accounts.roleAsPrincipal(role), nonce, role.getSecret());
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByAccessKeyId(final String keyId, final String nonce) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(AccessKeyEntity.class)) {
            final UserEntity user;
            try {
                user = (UserEntity) Entities.createCriteria(UserEntity.class).createCriteria("keys")
                        .add(Restrictions.eq("accessKey", keyId)).setFlushMode(FlushMode.MANUAL).setReadOnly(true)
                        .uniqueResult();
            } catch (Exception e) {
                throw new InvalidAccessKeyAuthException("Failed to find access key", e);
            }
            if (user == null) {
                throw new InvalidAccessKeyAuthException("Failed to find access key");
            }
            final UserPrincipal principal = new UserPrincipalImpl(user);
            final Optional<AccessKey> accessKey = Iterables.tryFind(principal.getKeys(),
                    CollectionUtils.propertyPredicate(keyId, AccessKeys.accessKeyIdentifier()));
            if (!Iterables.any(accessKey.asSet(), AccessKeys.isActive())) {
                throw new InvalidAccessKeyAuthException("Invalid access key or token");
            }
            return decorateCredentials(principal, nonce, accessKey.get().getSecretKey());
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByCertificateId(final String certificateId) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(CertificateEntity.class)) {
            final Certificate certificate = Accounts.lookupCertificateByHashId(certificateId);
            if (!certificate.isActive()) {
                throw new AuthException("Certificate is inactive or revoked: "
                        + certificate.getX509Certificate().getSubjectX500Principal());
            }
            return certificate.getPrincipal();
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByCanonicalId(final String canonicalId) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(UserEntity.class)) {
            return Accounts.userAsPrincipal(Accounts.lookupAccountByCanonicalId(canonicalId).lookupAdmin());
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByAccountNumber(final String accountNumber) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(UserEntity.class)) {
            return Accounts.userAsPrincipal(Accounts.lookupAccountById(accountNumber).lookupAdmin());
        }
    }

    @Override
    public UserPrincipal lookupPrincipalByAccountNumberAndUsername(final String accountNumber, final String name)
            throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(UserEntity.class)) {
            return Accounts.userAsPrincipal(Accounts.lookupAccountById(accountNumber).lookupUserByName(name));
        }
    }

    @Override
    public UserPrincipal lookupCachedPrincipalByUserId(final UserPrincipal cached, final String userId,
            final String nonce) throws AuthException {
        return lookupPrincipalByUserId(userId, nonce);
    }

    @Override
    public UserPrincipal lookupCachedPrincipalByRoleId(final UserPrincipal cached, final String roleId,
            final String nonce) throws AuthException {
        return lookupPrincipalByRoleId(roleId, nonce);
    }

    @Override
    public UserPrincipal lookupCachedPrincipalByAccessKeyId(final UserPrincipal cached, final String keyId,
            final String nonce) throws AuthException {
        return lookupPrincipalByAccessKeyId(keyId, nonce);
    }

    @Override
    public UserPrincipal lookupCachedPrincipalByCertificateId(final UserPrincipal cached,
            final String certificateId) throws AuthException {
        return lookupPrincipalByCertificateId(certificateId);
    }

    @Override
    public UserPrincipal lookupCachedPrincipalByAccountNumber(final UserPrincipal cached,
            final String accountNumber) throws AuthException {
        return lookupPrincipalByAccountNumber(accountNumber);
    }

    @Override
    public AccountIdentifiers lookupAccountIdentifiersByAlias(final String alias) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(AccountEntity.class)) {
            return Accounts.lookupAccountByName(alias);
        }
    }

    @Override
    public AccountIdentifiers lookupAccountIdentifiersByCanonicalId(final String canonicalId) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(AccountEntity.class)) {
            return Accounts.lookupAccountByCanonicalId(canonicalId);
        }
    }

    @Override
    public AccountIdentifiers lookupAccountIdentifiersByEmail(final String email) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(UserEntity.class)) {
            final EuareUser euareUser = Accounts.lookupUserByEmailAddress(email);
            if (euareUser.isAccountAdmin()) {
                return euareUser.getAccount();
            }
            throw new AuthException(AuthException.NO_SUCH_USER);
        }
    }

    @Override
    public List<AccountIdentifiers> listAccountIdentifiersByAliasMatch(final String aliasExpression)
            throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(AccountEntity.class)) {
            return Accounts.resolveAccountNumbersForName(aliasExpression);
        }
    }

    @Override
    public InstanceProfile lookupInstanceProfileByName(final String accountNumber, final String name)
            throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(InstanceProfileEntity.class)) {
            final EuareInstanceProfile profile = Accounts.lookupAccountById(accountNumber)
                    .lookupInstanceProfileByName(name);
            final String profileArn = Accounts.getInstanceProfileArn(profile);
            final EuareRole euareRole = profile.getRole();
            final String roleArn = euareRole == null ? null : Accounts.getRoleArn(euareRole);
            final String roleAccountNumber = euareRole == null ? null : euareRole.getAccountNumber();
            final PolicyVersion rolePolicy = euareRole == null ? null : euareRole.getPolicy();
            final Role role = euareRole == null ? null : new Role() {
                @Override
                public String getAccountNumber() {
                    return roleAccountNumber;
                }

                @Override
                public String getRoleId() {
                    return euareRole.getRoleId();
                }

                @Override
                public String getRoleArn() {
                    return roleArn;
                }

                @Override
                public String getPath() {
                    return euareRole.getPath();
                }

                @Override
                public String getName() {
                    return euareRole.getName();
                }

                @Override
                public String getSecret() {
                    return euareRole.getSecret();
                }

                @Override
                public PolicyVersion getPolicy() {
                    return rolePolicy;
                }

                @Override
                public String getDisplayName() {
                    return Accounts.getRoleFullName(this);
                }

                @Override
                public OwnerFullName getOwner() {
                    return euareRole.getOwner();
                }
            };
            return new InstanceProfile() {
                @Override
                public String getAccountNumber() {
                    return accountNumber;
                }

                @Override
                public String getInstanceProfileId() {
                    return profile.getInstanceProfileId();
                }

                @Override
                public String getInstanceProfileArn() {
                    return profileArn;
                }

                @Nullable
                @Override
                public Role getRole() {
                    return role;
                }

                @Override
                public String getName() {
                    return profile.getName();
                }

                @Override
                public String getPath() {
                    return profile.getPath();
                }
            };
        }
    }

    @Override
    public Role lookupRoleByName(final String accountNumber, final String name) throws AuthException {
        try (final TransactionResource tx = Entities.readOnlyDistinctTransactionFor(RoleEntity.class)) {
            final EuareRole euareRole = Accounts.lookupAccountById(accountNumber).lookupRoleByName(name);
            final String roleArn = Accounts.getRoleArn(euareRole);
            final String roleAccountNumber = euareRole.getAccountNumber();
            final PolicyVersion assumeRolePolicy = euareRole.getPolicy();
            return new Role() {
                @Override
                public String getAccountNumber() {
                    return roleAccountNumber;
                }

                @Override
                public String getRoleId() {
                    return euareRole.getRoleId();
                }

                @Override
                public String getRoleArn() {
                    return roleArn;
                }

                @Override
                public String getPath() {
                    return euareRole.getPath();
                }

                @Override
                public String getName() {
                    return euareRole.getName();
                }

                @Override
                public String getSecret() {
                    return euareRole.getSecret();
                }

                @Override
                public PolicyVersion getPolicy() {
                    return assumeRolePolicy;
                }

                @Override
                public String getDisplayName() {
                    return Accounts.getRoleFullName(this);
                }

                @Override
                public OwnerFullName getOwner() {
                    return euareRole.getOwner();
                }
            };
        }
    }

    @Override
    public SecurityTokenContent decodeSecurityToken(final String accessKeyIdentifier, final String securityToken)
            throws AuthException {
        return SecurityTokenManager.decodeSecurityToken(accessKeyIdentifier, securityToken);
    }

    private UserPrincipal decorateCredentials(final UserPrincipal userPrincipal, final String nonce,
            final String secret) throws AuthException {
        final UserPrincipal decorated;
        if (nonce == null) {
            decorated = userPrincipal;
        } else {
            final String secretKey = SecurityTokenManager.generateSecret(nonce, secret);
            final Collection<AccessKey> keys = Collections.<AccessKey>singleton(new AccessKey() {
                @Override
                public Boolean isActive() {
                    return true;
                }

                @Override
                public String getAccessKey() {
                    return null;
                }

                @Override
                public String getSecretKey() {
                    return secretKey;
                }

                @Override
                public Date getCreateDate() {
                    return null;
                }

                @Override
                public UserPrincipal getPrincipal() {
                    return null;
                }
            });
            decorated = new UserPrincipalImpl(userPrincipal, keys);
        }
        return decorated;
    }

    @Override
    public void reserveGlobalName(final String namespace, final String name, final Integer duration,
            final String clientToken) throws AuthException {
        final GlobalNamespace globalNamespace;
        try {
            globalNamespace = GlobalNamespace.forNamespace(namespace);
        } catch (IllegalArgumentException e) {
            throw new AuthException(e);
        }

        if (duration == null || duration < 1 || duration > TimeUnit.DAYS.toSeconds(1)) {
            throw new AuthException("Requested duration not supported: " + duration);
        }

        try (final TransactionResource tx = Entities.transactionFor(ReservedNameEntity.class)) {
            Entities.persist(
                    ReservedNameEntity.create(namespace, name, duration, Strings.emptyToNull(clientToken)));
            tx.commit();
        } catch (ConstraintViolationException e) {
            boolean conflict = true;
            if (!Strings.isNullOrEmpty(clientToken))
                try (final TransactionResource tx = Entities
                        .readOnlyDistinctTransactionFor(ReservedNameEntity.class)) {
                    // use the existing reservation for the token if it matches and
                    // has half the duration remaining
                    final ReservedNameEntity entity = Entities
                            .uniqueResult(ReservedNameEntity.exampleWithToken(clientToken));
                    conflict = !entity.getNamespace().equals(namespace) || !entity.getName().equals(name)
                            || entity.getExpiry().before(
                                    new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(duration / 2)));
                } catch (TransactionException | NoSuchElementException e1) {
                    // fail with conflict
                }
            if (conflict) {
                throw new AuthException(AuthException.CONFLICT);
            }
        } catch (final Exception e) {
            throw new AuthException(e);
        }

        switch (globalNamespace) {
        case Account_Alias:
            try {
                Accounts.lookupAccountByName(name);
                throw new AuthException(AuthException.CONFLICT);
            } catch (AuthException e) {
                if (!AuthException.NO_SUCH_ACCOUNT.equals(e.getMessage())) {
                    throw new AuthException(AuthException.CONFLICT);
                }
            }
            break;
        case Signing_Certificate_Id:
            try {
                Accounts.lookupCertificateById(name);
                throw new AuthException(AuthException.CONFLICT);
            } catch (AuthException e) {
                if (!AuthException.NO_SUCH_CERTIFICATE.equals(e.getMessage())) {
                    throw new AuthException(AuthException.CONFLICT);
                }
            }
            break;
        default:
            throw new AuthException(AuthException.CONFLICT);
        }
    }

    @Override
    public X509Certificate getCertificateByAccountNumber(final String accountNumber) {
        return SystemCredentials.lookup(Euare.class).getCertificate();
    }

    @Override
    public X509Certificate signCertificate(final String accountNumber, final RSAPublicKey publicKey,
            final String principal, final int expiryInDays) throws AuthException {
        return EuareServerCertificateUtil.generateVMCertificate(publicKey, principal, expiryInDays);
    }
}