/*************************************************************************
* 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.util.Date;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.criterion.Restrictions;
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.GroupEntity;
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.EuareAccount;
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.Policy;
import com.eucalyptus.entities.Entities;
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.Supplier;
import com.google.common.collect.Lists;
public class DatabaseGroupProxy implements EuareGroup {
private static final long serialVersionUID = 1L;
private static final ValueChecker NAME_CHECKER = ValueCheckerFactory.createGroupNameChecker( );
private static final ValueChecker PATH_CHECKER = ValueCheckerFactory.createPathChecker( );
private static final ValueChecker POLICY_NAME_CHECKER = ValueCheckerFactory.createPolicyNameChecker( );
private static Logger LOG = Logger.getLogger( DatabaseGroupProxy.class );
private GroupEntity delegate;
private transient Supplier<String> accountNumberSupplier =
DatabaseAuthUtils.getAccountNumberSupplier( this );
public DatabaseGroupProxy( GroupEntity delegate ) {
this.delegate = delegate;
}
public DatabaseGroupProxy( final GroupEntity delegate,
final Supplier<String> accountNumberSupplier ) {
this.delegate = delegate;
this.accountNumberSupplier = accountNumberSupplier;
}
@Override
public String toString( ) {
final StringBuilder sb = new StringBuilder( );
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity 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 void setName( final String name ) throws AuthException {
try {
NAME_CHECKER.check( name );
} catch ( InvalidValueException e ) {
Debugging.logError( LOG, e, "Invalid group name " + name );
throw new AuthException( AuthException.INVALID_NAME, e );
}
try {
// try looking up the group with the same name first
this.getAccount( ).lookupGroupByName( name );
} catch ( AuthException ae ) {
// not found
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity t ) {
t.setName( name );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setName for " + this.delegate );
throw new AuthException( e );
}
return;
}
// found
throw new AuthException( AuthException.GROUP_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( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity 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 isUserGroup( ) {
return this.delegate.isUserGroup( );
}
@Override
public void setUserGroup( final Boolean userGroup ) throws AuthException {
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity t ) {
t.setUserGroup( userGroup );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to setUserGroup for " + this.delegate );
throw new AuthException( e );
}
}
@Override
public void addUserByName( String userName ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( GroupEntity.class ) ) {
GroupEntity groupEntity = DatabaseAuthUtils.getUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ) );
UserEntity userEntity = DatabaseAuthUtils.getUniqueUser( userName, groupEntity.getAccount( ).getName( ) );
groupEntity.getUsers( ).add( userEntity );
userEntity.getGroups( ).add( groupEntity );
db.commit( );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to add user " + userName + " to group " + this.delegate );
throw new AuthException( e );
}
}
@Override
public void removeUserByName( String userName ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( GroupEntity.class ) ) {
GroupEntity groupEntity = DatabaseAuthUtils.getUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ) );
UserEntity userEntity = DatabaseAuthUtils.getUniqueUser( userName, groupEntity.getAccount( ).getName( ) );
groupEntity.getUsers( ).remove( userEntity );
userEntity.getGroups( ).remove( groupEntity );
db.commit( );
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to remove user " + userName + " from group " + this.delegate );
throw new AuthException( e );
}
}
@Override
public boolean hasUser( String userName ) throws AuthException {
try ( final TransactionResource db = Entities.transactionFor( UserEntity.class ) ) {
@SuppressWarnings( "unchecked" )
List<UserEntity> users = Entities
.criteriaQuery( UserEntity.class ).whereEqual( UserEntity_.name, userName )
.join( UserEntity_.groups ).whereEqual( GroupEntity_.groupId, this.delegate.getGroupId( ) )
.list( );
db.commit( );
return users.size( ) > 0;
} catch ( Exception e ) {
Debugging.logError( LOG, e, "Failed to check membership for group " + this.delegate );
throw new AuthException( e );
}
}
@Override
public List<Policy> getPolicies( ) {
final List<Policy> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity t ) {
for ( PolicyEntity p : t.getPolicies( ) ) {
results.add( new DatabasePolicyProxy( p ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getPolicies for " + this.delegate );
}
return results;
}
@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 );
try ( final TransactionResource db = Entities.transactionFor( GroupEntity.class ) ) {
final GroupEntity groupEntity = DatabaseAuthUtils.getUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ) );
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 ( name == null ) {
throw new AuthException( AuthException.EMPTY_POLICY_NAME );
}
try ( final TransactionResource db = Entities.transactionFor( GroupEntity.class ) ) {
GroupEntity group = DatabaseAuthUtils.getUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ) );
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 );
}
}
@Override
public List<EuareManagedPolicy> getAttachedPolicies( ) {
final List<EuareManagedPolicy> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity 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( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity 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( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity 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 );
}
}
@Override
public List<EuareUser> getUsers( ) {
final List<EuareUser> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity t ) {
for ( UserEntity u : t.getUsers( ) ) {
results.add( new DatabaseUserProxy( u ) );
}
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getUsers for " + this.delegate );
}
return results;
}
@Override
public String getAccountNumber( ) throws AuthException {
return DatabaseAuthUtils.extract( accountNumberSupplier );
}
@Override
public EuareAccount getAccount( ) {
final List<DatabaseAccountProxy> results = Lists.newArrayList( );
try {
DatabaseAuthUtils.invokeUnique( GroupEntity.class, GroupEntity_.groupId, this.delegate.getGroupId( ), new Tx<GroupEntity>( ) {
public void fire( GroupEntity t ) {
Entities.initialize( t.getAccount( ) );
results.add( new DatabaseAccountProxy( t.getAccount( ) ) );
}
} );
} catch ( ExecutionException e ) {
Debugging.logError( LOG, e, "Failed to getAccount for " + this.delegate );
}
return results.get( 0 );
}
@Override
public String getGroupId( ) {
return this.delegate.getGroupId( );
}
private void readObject( ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject( );
this.accountNumberSupplier = DatabaseAuthUtils.getAccountNumberSupplier( this );
}
}