/*!
* Copyright 2010 - 2016 Pentaho Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jackrabbit.core.security.authorization.acl;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.ObservationManager;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.id.NodeId;
import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
import org.pentaho.platform.api.engine.ISystemConfig;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Customization of {@link ACLProvider}.
*
* @author mlowery
*/
public class PentahoACLProvider extends ACLProvider {
@SuppressWarnings( "rawtypes" )
private Map configuration;
// Overrides to CompiledPermissions creation require we keep an extra reference
// because this is private in ACLProvider
private EntryCollector entryCollector;
private Map<Integer, PentahoCompiledPermissionsImpl> compiledPermissionsCache =
new HashMap<Integer, PentahoCompiledPermissionsImpl>();
private boolean useCachingEntryCollector;
private Logger logger = LoggerFactory.getLogger( getClass().getName() );
private boolean initialized;
/**
* Overridden to:
* <ul>
* <li>Store {@code configuration} for later passing to {@link PentahoEntryCollector}.</li>
* <li>Add JCR_READ_ACCESS_CONTROL to root ACL. This is harmless and avoids more customization.</li>
* </ul>
*/
@Override
@SuppressWarnings( "rawtypes" )
public void init( final Session systemSession, final Map conf ) throws RepositoryException {
this.configuration = conf;
ISystemConfig settings = PentahoSystem.get( ISystemConfig.class );
if ( settings != null ) {
useCachingEntryCollector = "true".equals( settings.getProperty( "system.cachingEntryCollector" ) );
}
super.init( systemSession, conf );
// original initRootACL should run during super.init call above
updateRootAcl( (SessionImpl) systemSession, new ACLEditor( session, this, false /* allowUnknownPrincipals */ ) );
this.initialized = true;
registerEntryCollectorWithObservationManager( systemSession );
}
protected void registerEntryCollectorWithObservationManager( Session systemSession ) throws RepositoryException {
// Register Entry Collector to receive node events
if ( entryCollector != null && this.initialized ) {
ObservationManager observationMgr = systemSession.getWorkspace().getObservationManager();
observationMgr.addEventListener( entryCollector, Event.NODE_ADDED | Event.NODE_REMOVED | Event.NODE_REMOVED, "/",
true, null, null, false );
}
}
/**
* Adds ACE so that everyone can read access control. This allows Jackrabbit's default collectAcls to work without
* change. Otherwise, you have to be an admin to call acMgr.getEffectivePolicies.
*/
protected void updateRootAcl( SessionImpl systemSession, ACLEditor editor ) throws RepositoryException {
String rootPath = session.getRootNode().getPath();
AccessControlPolicy[] acls = editor.getPolicies( rootPath );
if ( acls.length > 0 ) {
PrincipalManager pMgr = systemSession.getPrincipalManager();
AccessControlManager acMgr = session.getAccessControlManager();
Principal everyone = pMgr.getEveryone();
Privilege[] privs =
new Privilege[] { acMgr.privilegeFromName( Privilege.JCR_READ ),
acMgr.privilegeFromName( Privilege.JCR_READ_ACCESS_CONTROL ) };
AccessControlList acList = (AccessControlList) acls[0];
AccessControlEntry[] acEntries = acList.getAccessControlEntries();
for ( AccessControlEntry acEntry : acEntries ) {
if ( acEntry.getPrincipal().equals( everyone ) ) {
acList.removeAccessControlEntry( acEntry );
}
}
acList.addAccessControlEntry( everyone, privs );
editor.setPolicy( rootPath, acList );
session.save();
}
}
/**
* Overridden to:
* <ul>
* <li>Return custom {@code EntryCollector}.
* <li>Later access to the {@code EntryCollector}
* </ul>
*/
@Override
protected EntryCollector createEntryCollector( SessionImpl systemSession ) throws RepositoryException {
if ( entryCollector != null ) {
return entryCollector;
}
// keep our own private reference; the one in ACLProvider is private
if ( useCachingEntryCollector ) {
entryCollector = new CachingPentahoEntryCollector( systemSession, getRootNodeId(), configuration );
logger.debug( "Using Caching EntryCollector" );
} else {
entryCollector = new PentahoEntryCollector( systemSession, getRootNodeId(), configuration );
logger.debug( "Using Non-Caching EntryCollector" );
}
registerEntryCollectorWithObservationManager( systemSession );
return entryCollector;
}
/**
* Overridden to:
* <ul>
* <li>Return custom {@code CompiledPermissions}.
* </ul>
*
* @see PentahoCompiledPermissionsImpl
*/
@Override
public CompiledPermissions compilePermissions( Set<Principal> principals ) throws RepositoryException {
checkInitialized();
if ( isAdminOrSystem( principals ) ) {
return getAdminPermissions();
} else if ( isReadOnly( principals ) ) {
return getReadOnlyPermissions();
} else {
return getCompiledPermissions( principals );
}
}
protected PentahoCompiledPermissionsImpl getCompiledPermissions( Set<Principal> principals )
throws RepositoryException {
// check the cache first
if ( compiledPermissionsCache.containsKey( principals.hashCode() ) ) {
return compiledPermissionsCache.get( principals.hashCode() );
}
PentahoCompiledPermissionsImpl compiledPermissions =
new PentahoCompiledPermissionsImpl( principals, session, entryCollector, this, true );
compiledPermissionsCache.put( principals.hashCode(), compiledPermissions );
return compiledPermissions;
}
/**
* Overridden to:
* <ul>
* <li>Use custom {@code CompiledPermissions}.
* </ul>
*
* @see PentahoCompiledPermissionsImpl
*/
@Override
public boolean canAccessRoot( Set<Principal> principals ) throws RepositoryException {
checkInitialized();
if ( isAdminOrSystem( principals ) ) {
return true;
} else {
CompiledPermissions cp = getCompiledPermissions( principals );
try {
return cp.canRead( null, getRootNodeId() );
} finally {
cp.close();
}
}
}
private NodeId getRootNodeId() throws RepositoryException {
// TODO: how expensive is this? Should we keep a reference?
return ( (NodeImpl) session.getRootNode() ).getNodeId();
}
}