/*************************************************************************
* 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.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.auth.euare.persist;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.eucalyptus.auth.Accounts;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.Debugging;
import com.eucalyptus.auth.PolicyParseException;
import com.eucalyptus.auth.euare.checker.InvalidValueException;
import com.eucalyptus.auth.euare.checker.ValueChecker;
import com.eucalyptus.auth.euare.checker.ValueCheckerFactory;
import com.eucalyptus.auth.euare.persist.entities.AccessKeyEntity;
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.CertificateEntity_;
import com.eucalyptus.auth.euare.persist.entities.GroupEntity;
import com.eucalyptus.auth.euare.persist.entities.ManagedPolicyEntity;
import com.eucalyptus.auth.euare.persist.entities.PolicyEntity;
import com.eucalyptus.auth.euare.persist.entities.UserEntity;
import com.eucalyptus.auth.euare.persist.entities.UserEntity_;
import com.eucalyptus.auth.euare.principal.EuareAccessKey;
import com.eucalyptus.auth.euare.principal.EuareAccount;
import com.eucalyptus.auth.euare.principal.EuareCertificate;
import com.eucalyptus.auth.euare.principal.EuareGroup;
import com.eucalyptus.auth.euare.principal.EuareManagedPolicy;
import com.eucalyptus.auth.euare.principal.EuareUser;
import com.eucalyptus.auth.policy.PolicyParser;
import com.eucalyptus.auth.policy.PolicyPolicy;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.principal.Authorization;
import com.eucalyptus.auth.principal.Certificate;
import com.eucalyptus.auth.principal.Policy;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.entities.Entities;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.Tx;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class DatabaseUserProxy implements EuareUser {
private static final long serialVersionUID = 1L;
private static Logger LOG = Logger.getLogger( DatabaseUserProxy.class );
private static final ValueChecker NAME_CHECKER = ValueCheckerFactory.createUserNameChecker( );
private static final ValueChecker PATH_CHECKER = ValueCheckerFactory.createPathChecker( );
private static final ValueChecker POLICY_NAME_CHECKER = ValueCheckerFactory.createPolicyNameChecker( );
private UserEntity delegate;
private transient Supplier<String> accountNumberSupplier =
DatabaseAuthUtils.getAccountNumberSupplier( this );
private transient Supplier<Map<String,String>> userInfoSupplier =
getUserInfoSupplier();
private transient Boolean isSystemAdmin = null;
public DatabaseUserProxy( UserEntity delegate ) {
this.delegate = delegate;
}
public DatabaseUserProxy( UserEntity delegate,
String accountNumber ) {
this.delegate = delegate;
accountNumberSupplier = Suppliers.ofInstance( accountNumber );
}
public DatabaseUserProxy( UserEntity delegate,
String accountNumber,
Map<String,String> info ) {
this.delegate = delegate;
accountNumberSupplier = Suppliers.ofInstance( accountNumber );
userInfoSupplier = Suppliers.ofInstance( info );
}
@Override
public String toString( ) {
final StringBuilder sb = new StringBuilder( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
sb.append( t.toString( ) );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to toString for " + this.delegate );
}
return sb.toString();
}
@Override
public String getName( ) {
return this.delegate.getName( );
}
@Override
public String getUserId( ) {
return this.delegate.getUserId();
}
@Override
public void setName( String name ) throws AuthException {
try {
NAME_CHECKER.check( name );
} catch ( InvalidValueException e ) {
Debugging.logError( LOG, e, "Invalid user name " + name );
throw new AuthException( AuthException.INVALID_NAME, e );
}
try {
// try looking up the user with same name
this.getAccount( ).lookupUserByName( name );
} catch ( AuthException e ) {
// not found
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
user.setName( name );
for ( GroupEntity g : user.getGroups( ) ) {
if ( g.isUserGroup( ) ) {
g.setName( DatabaseAuthUtils.getUserGroupName( name ) );
break;
}
}
db.commit( );
} catch ( Exception t ) {
Debugging.logError( LOG, t, "Failed to setName for " + this.delegate );
throw new AuthException( t );
}
return;
}
// found
throw new AuthException( AuthException.USER_ALREADY_EXISTS );
}
@Override
public String getPath( ) {
return this.delegate.getPath();
}
@Override
public void setPath( final String path ) throws AuthException {
try {
PATH_CHECKER.check( path );
} catch ( InvalidValueException e ) {
Debugging.logError( LOG, e, "Invalid path " + path );
throw new AuthException( AuthException.INVALID_PATH, e );
}
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.setPath( path );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setPath for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public Date getCreateDate() {
return this.delegate.getCreationTimestamp( );
}
@Override
public boolean isEnabled( ) {
return this.delegate.isEnabled( );
}
@Override
public void setEnabled( final boolean enabled ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.setEnabled( enabled );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setEnabled for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public String getToken( ) {
return this.delegate.getToken( );
}
@Override
public void setToken( final String token ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.setToken( token );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setToken for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public String resetToken( ) throws AuthException {
String original = this.delegate.getToken( );
this.setToken( Crypto.generateSessionToken() );
return original;
}
@Override
public String getPassword( ) {
return this.delegate.getPassword( );
}
@Override
public void setPassword( final String password ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.setPassword( password );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setPassword for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public Long getPasswordExpires( ) {
return this.delegate.getPasswordExpires( );
}
@Override
public void setPasswordExpires( final Long time ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.setPasswordExpires( time );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setPasswordExpires for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public String getInfo( final String key ) throws AuthException {
return DatabaseAuthUtils.extract( userInfoSupplier ).get( key.toLowerCase( ) );
}
@Override
public Map<String, String> getInfo( ) throws AuthException {
return DatabaseAuthUtils.extract( userInfoSupplier );
}
@Override
public void setInfo( final String key, final String value ) throws AuthException {
if ( Strings.isNullOrEmpty( key ) ) {
throw new AuthException( "Empty key" );
}
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.getInfo( ).put( key.toLowerCase( ), value );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setInfo for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public void removeInfo( final String key ) throws AuthException {
if ( Strings.isNullOrEmpty( key ) ) {
throw new AuthException( "Empty key" );
}
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.getInfo( ).remove( key.toLowerCase( ) );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to removeInfo for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public void setInfo( final Map<String, String> newInfo ) throws AuthException {
if ( newInfo == null ) {
throw new AuthException( "Empty user info map" );
}
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
t.getInfo( ).clear( );
for ( Map.Entry<String, String> entry : newInfo.entrySet( ) ) {
t.getInfo( ).put( entry.getKey( ).toLowerCase( ), entry.getValue( ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setInfo for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public List<AccessKey> getKeys( ) throws AuthException {
final List<AccessKey> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
for ( AccessKeyEntity k : t.getKeys( ) ) {
results.add( new DatabaseAccessKeyProxy( k ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getKeys for " + this.delegate );
throw new AuthException( e );
}
return results;
}
@Override
public EuareAccessKey getKey( final String keyId ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( AccessKeyEntity.class ) ) {
AccessKeyEntity key = DatabaseAuthUtils.getUnique( AccessKeyEntity.class, AccessKeyEntity_.accessKey, keyId );
checkKeyOwner( key );
db.commit( );
return new DatabaseAccessKeyProxy( key );
} catch ( NoSuchElementException e ) {
throw new AuthException( AuthException.NO_SUCH_KEY );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get access key " + keyId );
throw new AuthException( AuthException.NO_SUCH_KEY );
}
}
@Override
public void removeKey( final String keyId ) throws AuthException {
if ( Strings.isNullOrEmpty( keyId ) ) {
throw new AuthException( AuthException.EMPTY_KEY_ID );
}
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
AccessKeyEntity keyEntity = DatabaseAuthUtils.getUnique( AccessKeyEntity.class, AccessKeyEntity_.accessKey, keyId );
checkKeyOwner( keyEntity );
if ( !user.getKeys( ).remove( keyEntity ) ) {
throw new AuthException( AuthException.NO_SUCH_KEY );
}
Entities.delete( keyEntity );
db.commit( );
} catch ( AuthException e ) {
throw e;
} catch ( NoSuchElementException e ) {
throw new AuthException( AuthException.NO_SUCH_KEY, e );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get delete key " + keyId );
throw new AuthException( e );
}
}
@Override
public EuareAccessKey createKey( ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
AccessKeyEntity keyEntity = new AccessKeyEntity( user );
keyEntity.setActive( true );
Entities.persist( keyEntity );
user.getKeys( ).add( keyEntity );
db.commit( );
return new DatabaseAccessKeyProxy( keyEntity );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get create new access key: " + e.getMessage( ) );
throw new AuthException( e );
}
}
@Override
public List<Certificate> getCertificates( ) throws AuthException {
final List<Certificate> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
for ( CertificateEntity c : t.getCertificates( ) ) {
results.add( new DatabaseCertificateProxy( c ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getCertificates for " + this.delegate );
throw new AuthException( e );
}
return results;
}
@Override
public EuareCertificate getCertificate( final String certificateId ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( CertificateEntity.class ) ) {
CertificateEntity cert = DatabaseAuthUtils.getUnique( CertificateEntity.class, CertificateEntity_.certificateId, certificateId );
checkCertOwner( cert );
db.commit( );
return new DatabaseCertificateProxy( cert );
} catch ( NoSuchElementException e ) {
throw new AuthException( AuthException.NO_SUCH_CERTIFICATE );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get signing certificate " + certificateId );
throw new AuthException( AuthException.NO_SUCH_CERTIFICATE );
}
}
@Override
public EuareCertificate addCertificate( String certificateId, X509Certificate cert ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
CertificateEntity certEntity = new CertificateEntity( certificateId, cert );
certEntity.setActive( true );
Entities.persist( certEntity );
certEntity.setUser( user );
user.getCertificates( ).add( certEntity );
db.commit( );
return new DatabaseCertificateProxy( certEntity );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get add certificate " + cert );
throw new AuthException( e );
}
}
@Override
public void removeCertificate( final String certificateId ) throws AuthException {
if ( Strings.isNullOrEmpty( certificateId ) ) {
throw new AuthException( AuthException.EMPTY_CERT_ID );
}
try ( final TransactionResource db = Entities.transactionFor( CertificateEntity.class ) ) {
CertificateEntity certificateEntity =
DatabaseAuthUtils.getUnique( CertificateEntity.class, CertificateEntity_.certificateId, certificateId );
checkCertOwner( certificateEntity );
Entities.delete( certificateEntity );
db.commit( );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get delete certificate " + certificateId );
throw new AuthException( e );
}
}
@Override
public List<EuareGroup> getGroups( ) throws AuthException {
final List<EuareGroup> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
for ( GroupEntity g : t.getGroups( ) ) {
results.add( new DatabaseGroupProxy( g, accountNumberSupplier ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getGroups for " + this.delegate );
throw new AuthException( e );
}
return results;
}
@Override
public String getAccountNumber( ) throws AuthException {
return DatabaseAuthUtils.extract( accountNumberSupplier );
}
@Override
public EuareAccount getAccount( ) throws AuthException {
final List<EuareAccount> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
if ( t.getGroups( ).size( ) < 1 ) {
throw new RuntimeException( "Unexpected group number of the user" );
}
final AccountEntity account = t.getGroups( ).get( 0 ).getAccount( );
Entities.initialize( account );
results.add( new DatabaseAccountProxy( account ) );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getAccount for " + this.delegate );
throw new AuthException( e );
}
return results.get( 0 );
}
@Override
public boolean isSystemAdmin( ) {
if ( isSystemAdmin != null ) {
// Safe to cache as the account alias cannot be changed for
// system accounts
return isSystemAdmin;
}
try {
final EuareAccount account = this.getAccount( );
accountNumberSupplier = Suppliers.ofInstance( account.getAccountNumber( ) );
return isSystemAdmin = Accounts.isSystemAccount( account.getName() );
} catch ( AuthException e ) {
LOG.error( e, e );
return false;
}
}
@Override
public boolean isSystemUser() {
return isSystemAdmin( );
}
@Override
public boolean isAccountAdmin( ) {
return DatabaseAuthUtils.isAccountAdmin( this.getName( ) );
}
private GroupEntity getUserGroupEntity( UserEntity userEntity ) {
GroupEntity groupEntity = null;
for ( GroupEntity g : userEntity.getGroups( ) ) {
if ( g.isUserGroup( ) ) {
groupEntity = g;
break;
}
}
return groupEntity;
}
@Override
public List<Policy> getPolicies( ) throws AuthException {
List<Policy> results = Lists.newArrayList( );
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
GroupEntity group = getUserGroupEntity( user );
if ( group == null ) {
throw new RuntimeException( "Can't find user group for user " + this.delegate.getName( ) );
}
for ( PolicyEntity p : group.getPolicies( ) ) {
results.add( new DatabasePolicyProxy( p ) );
}
db.commit( );
return results;
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to get policies for " + this.delegate );
throw new AuthException( "Failed to get policies", e );
}
}
@Override
public Policy addPolicy( String name, String policy ) throws AuthException, PolicyParseException {
return storePolicy( name, policy, /*allowUpdate*/ false );
}
@Override
public Policy putPolicy( String name, String policy ) throws AuthException, PolicyParseException {
return storePolicy( name, policy, /*allowUpdate*/ true );
}
private Policy storePolicy( String name, String policy, boolean allowUpdate ) throws AuthException, PolicyParseException {
try {
POLICY_NAME_CHECKER.check( name );
} catch ( InvalidValueException e ) {
Debugging.logError( LOG, e, "Invalid policy name " + name );
throw new AuthException( AuthException.INVALID_NAME, e );
}
if ( DatabaseAuthUtils.policyNameinList( name, this.getPolicies( ) ) && !allowUpdate ) {
Debugging.logError( LOG, null, "Policy name already used: " + name );
throw new AuthException( AuthException.INVALID_NAME );
}
final PolicyPolicy policyPolicy = PolicyParser.getInstance().parse( policy );
final PolicyEntity parsedPolicy = PolicyEntity.create( name, policyPolicy.getPolicyVersion( ), policy );
if ( this.isAccountAdmin( ) && containsAllowEffect( policyPolicy ) ) {
throw new PolicyParseException( "Policy with Allow effect can not be assigned to account/account admin" );
}
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity userEntity = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
final GroupEntity groupEntity = getUserGroupEntity( userEntity );
if ( groupEntity == null ) {
throw new RuntimeException( "Can't find user group for user " + this.delegate.getName( ) );
}
final PolicyEntity remove = DatabaseAuthUtils.removeGroupPolicy( groupEntity, name );
if ( remove != null ) {
Entities.delete( remove );
}
Entities.persist( parsedPolicy );
parsedPolicy.setGroup( groupEntity );
groupEntity.getPolicies( ).add( parsedPolicy );
db.commit( );
return new DatabasePolicyProxy( parsedPolicy );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to attach policy for " + this.delegate.getName( ) );
throw new AuthException( "Failed to attach policy", e );
}
}
@Override
public void removePolicy( String name ) throws AuthException {
if ( Strings.isNullOrEmpty( name ) ) {
throw new AuthException( AuthException.EMPTY_POLICY_NAME );
}
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
UserEntity user = DatabaseAuthUtils.getUnique( UserEntity.class, UserEntity_.userId, this.delegate.getUserId( ) );
GroupEntity group = getUserGroupEntity( user );
if ( group == null ) {
throw new RuntimeException( "Can't find user group for user " + this.delegate.getName( ) );
}
PolicyEntity policy = DatabaseAuthUtils.removeGroupPolicy( group, name );
if ( policy != null ) {
Entities.delete( policy );
}
db.commit( );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to remove policy " + name + " in " + this.delegate );
throw new AuthException( "Failed to remove policy", e );
}
}
/**
* @return true if the policy contains Effect is "Allow".
*/
private boolean containsAllowEffect( final PolicyPolicy policy ) {
for ( final Authorization authorization : policy.getAuthorizations( ) ) {
if ( authorization.getEffect( ) == Authorization.EffectType.Allow ) {
return true;
}
}
return false;
}
@Override
public List<EuareManagedPolicy> getAttachedPolicies( ) {
final List<EuareManagedPolicy> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, DatabaseUserProxy.this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
for ( ManagedPolicyEntity p : t.getAttachedPolicies( ) ) {
results.add( new DatabaseManagedPolicyProxy( p ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getAttachedPolicies for " + this.delegate );
}
return results;
}
@Override
public void attachPolicy( final EuareManagedPolicy policy ) throws AuthException {
try {
final String accountNumber = policy.getAccountNumber( );
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, DatabaseUserProxy.this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
final ManagedPolicyEntity policyEntity = Entities.criteriaQuery(
ManagedPolicyEntity.exampleWithName( accountNumber, policy.getName( ) )
).uniqueResult( );
if ( t.getAttachedPolicies( ).add( policyEntity ) ) {
policyEntity.setAttachmentCount( DatabaseAuthUtils.countAttachments( policyEntity ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to attachPolicy for " + this.delegate );
}
}
@Override
public void detachPolicy( final EuareManagedPolicy policy ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, DatabaseUserProxy.this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
ManagedPolicyEntity policyEntity = null;
for ( final ManagedPolicyEntity attachedPolicy : t.getAttachedPolicies( ) ) {
if ( attachedPolicy.getPolicyId( ).equals( policy.getPolicyId( ) ) ) {
policyEntity = attachedPolicy;
break;
}
}
if ( policyEntity != null ) {
t.getAttachedPolicies( ).remove( policyEntity );
policyEntity.setAttachmentCount( DatabaseAuthUtils.countAttachments( policyEntity ) );
} else {
throw Exceptions.toUndeclared( new AuthException( AuthException.NO_SUCH_POLICY ) );
}
}
} );
} catch ( ExecutionException e ) {
Exceptions.findAndRethrow( e, AuthException.class );
Debugging.logError( LOG, e, "Failed to detachPolicy for " + this.delegate );
}
}
private Supplier<Map<String,String>> getUserInfoSupplier( ) {
return new Supplier<Map<String, String>>() {
@Override
public Map<String, String> get() {
final Map<String, String> results = Maps.newHashMap( );
try {
DatabaseAuthUtils.invokeUnique( UserEntity.class, UserEntity_.userId, DatabaseUserProxy.this.delegate.getUserId( ), new Tx<UserEntity>( ) {
public void fire( UserEntity t ) {
results.putAll( t.getInfo( ) );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getInfo for " + DatabaseUserProxy.this.delegate );
throw new RuntimeException( new AuthException( e ).fillInStackTrace( ) );
}
return results;
}
};
}
private void checkKeyOwner( final AccessKeyEntity keyEnt ) throws AuthException {
checkOwner( keyEnt.getUser( ), AuthException.NO_SUCH_KEY, "key" );
}
private void checkCertOwner( final CertificateEntity certEnt ) throws AuthException {
checkOwner( certEnt.getUser( ), AuthException.NO_SUCH_CERTIFICATE, "certificate" );
}
private void checkOwner( final UserEntity user, final String error, final String type ) throws AuthException {
if ( !this.delegate.getUserId( ).equals( user.getUserId( ) ) ) {
AuthException ae = new AuthException( error );
Debugging.logError( LOG, ae, this.delegate.getName( ) + " is not owner of provided " + type );
throw ae;
}
}
private void readObject( ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject( );
this.accountNumberSupplier = DatabaseAuthUtils.getAccountNumberSupplier( this );
this.userInfoSupplier = getUserInfoSupplier( );
}
}