package org.pentaho.platform.repository2.unified.jcr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.engine.IAuthorizationPolicy; import org.pentaho.platform.api.repository2.unified.IAclNodeHelper; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.RepositoryFileAce; import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.api.repository2.unified.RepositoryFileSid; import org.pentaho.platform.api.repository2.unified.data.node.DataNode; import org.pentaho.platform.api.repository2.unified.data.node.DataNodeRef; import org.pentaho.platform.api.repository2.unified.data.node.NodeRepositoryFileData; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.security.SecurityHelper; import org.pentaho.platform.repository.messages.Messages; import org.pentaho.platform.security.policy.rolebased.actions.AdministerSecurityAction; import org.pentaho.platform.security.policy.rolebased.actions.RepositoryCreateAction; import org.pentaho.platform.security.policy.rolebased.actions.RepositoryReadAction; import java.util.EnumSet; import java.util.List; import java.util.UUID; import java.util.concurrent.Callable; /** * @author Andrey Khayrutdinov * @author Nick Baker * @author Marc Batchelor */ public class JcrAclNodeHelper implements IAclNodeHelper { private static final Log logger = LogFactory.getLog( JcrAclNodeHelper.class ); private static final String IS_ACL_NODE = "IS_ACL_NODE"; private static final String TARGET = "TARGET"; private final IUnifiedRepository unifiedRepository; public JcrAclNodeHelper( IUnifiedRepository unifiedRepository) { this.unifiedRepository = unifiedRepository; } // Set to protected for test access protected RepositoryFile getAclNode( final RepositoryFile file ) { try { return SecurityHelper.getInstance().runAsSystem( new Callable<RepositoryFile>() { @Override public RepositoryFile call() throws Exception { List<RepositoryFile> referrers = unifiedRepository.getReferrers( file.getId() ); // Loop through nodes referring to the target file, return the first one designated as an ACL node int i = referrers.size(); while ( i-- > 0 ) { RepositoryFile referrer = referrers.get( i ); NodeRepositoryFileData dataForRead = unifiedRepository.getDataForRead( referrer.getId(), NodeRepositoryFileData.class ); if ( dataForRead != null && dataForRead.getNode().hasProperty( IS_ACL_NODE ) ) { return referrer; } } // No ACL node found return null; } } ); } catch ( Exception e ) { logger.error( "Error retrieving ACL Node", e ); return null; } } @Override public boolean canAccess( final RepositoryFile repositoryFile, final EnumSet<RepositoryFilePermission> permissions ) { if ( repositoryFile == null ) { return false; } // Obtain a reference to ACL node as "system", guaranteed access final RepositoryFile aclNode = getAclNode( repositoryFile ); // If no ACL node is present, it's a public resource // Removed redundant call to getAclNode via BISERVER-12780 if ( aclNode == null ) { return true; } boolean notFound; try { // Check to see if user has READ access to file, this will return null if not. notFound = ( unifiedRepository.getFileById( aclNode.getId() ) == null ); } catch ( Exception e ) { if ( logger.isWarnEnabled() ) { logger.warn( "Error checking access for file", e ); } notFound = true; } if ( notFound ) { return false; } // if read passed, check the other permissions return unifiedRepository.hasAccess( aclNode.getPath(), permissions ); } /** * {@inheritDoc} */ @Override public RepositoryFileAcl getAclFor( final RepositoryFile repositoryFile ) { if ( repositoryFile == null ) { return null; } // Obtain a reference to ACL node as "system", guaranteed access final RepositoryFile aclNode = getAclNode( repositoryFile ); // If no ACL node is present, it's a public resource // Removed redundant call to getAclNode via BISERVER-12780 if ( aclNode == null ) { return null; } RepositoryFileAcl acl; try { acl = unifiedRepository.getAcl( aclNode.getId() ); } catch ( Exception e ) { return null; } RepositoryFileAcl.Builder aclBuilder = new RepositoryFileAcl.Builder( acl.getId(), acl.getOwner().getName(), RepositoryFileSid.Type.ROLE ); aclBuilder.aces( acl.getAces() ); //add the Administrator role if( canAdminister() ) { String adminRoleName = PentahoSystem.get( String.class, "singleTenantAdminAuthorityName", PentahoSessionHolder.getSession() ); RepositoryFileAce adminGroup = new RepositoryFileAce( new RepositoryFileSid( adminRoleName, RepositoryFileSid.Type.ROLE ), RepositoryFilePermission.ALL ); aclBuilder.ace( adminGroup ); } return aclBuilder.build(); } /** * {@inheritDoc} */ @Override public void setAclFor( final RepositoryFile fileToAddAclFor, final RepositoryFileAcl acl ) { try { SecurityHelper.getInstance().runAsSystem( new Callable<Void>() { @Override public Void call() throws Exception { RepositoryFile aclNode = getAclNode( fileToAddAclFor ); if ( acl == null ) { if ( aclNode != null ) { unifiedRepository.deleteFile( aclNode.getId(), true, Messages.getInstance().getString( "AclNodeHelper.WARN_0001_REMOVE_ACL_NODE", aclNode.getPath() ) ); } // ignore if no ACL node is present. } else { if ( aclNode == null ) { // Create ACL Node with reference to given file. aclNode = createAclNode( fileToAddAclFor ); } // Update ACL on file. RepositoryFileAcl existing = unifiedRepository.getAcl( aclNode.getId() ); RepositoryFileAcl updated = new RepositoryFileAcl.Builder( existing ) .aces( acl.getAces() ) .build(); unifiedRepository.updateAcl( updated ); } return null; } } ); } catch ( Exception e ) { logger.error( "Error setting ACL on node: " + fileToAddAclFor.getPath(), e ); } } private RepositoryFile createAclNode( RepositoryFile fileToAddAclFor ) { DataNode dataNode = new DataNode( "acl node" ); DataNodeRef dataNodeRef = new DataNodeRef( fileToAddAclFor.getId() ); dataNode.setProperty( TARGET, dataNodeRef ); dataNode.setProperty( IS_ACL_NODE, true ); NodeRepositoryFileData nodeRepositoryFileData = new NodeRepositoryFileData( dataNode ); return unifiedRepository.createFile( unifiedRepository.getFile( "/" ).getId(), new RepositoryFile.Builder( UUID.randomUUID().toString() ).aclNode( true ).build(), nodeRepositoryFileData, "" ); } @Override public void removeAclFor( RepositoryFile file ) { setAclFor( file, null ); } private boolean canAdminister() { IAuthorizationPolicy policy = PentahoSystem.get( IAuthorizationPolicy.class ); return policy.isAllowed( RepositoryReadAction.NAME ) && policy.isAllowed( RepositoryCreateAction.NAME ) && ( policy.isAllowed( AdministerSecurityAction.NAME ) ); } }