Java tutorial
/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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. * * * Copyright 2006 - 2013 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.repository2.unified.jcr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.mt.ITenant; 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.RepositoryFileSid.Type; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.repository2.messages.Messages; import org.pentaho.platform.repository2.unified.IRepositoryFileAclDao; import org.pentaho.platform.repository2.unified.jcr.IAclMetadataStrategy.AclMetadata; import org.pentaho.platform.repository2.unified.jcr.jackrabbit.security.SpringSecurityRolePrincipal; import org.pentaho.platform.repository2.unified.jcr.jackrabbit.security.SpringSecurityUserPrincipal; import org.springframework.extensions.jcr.JcrCallback; import org.springframework.extensions.jcr.JcrTemplate; import org.springframework.util.Assert; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.security.AccessControlEntry; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; import javax.jcr.security.AccessControlPolicy; import javax.jcr.security.AccessControlPolicyIterator; import javax.jcr.security.Privilege; import java.io.IOException; import java.io.Serializable; import java.security.Principal; import java.security.acl.Group; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; /** * Jackrabbit-based implementation of {@link IRepositoryFileAclDao}. * <p/> * <p> * All mutating public methods require checkout and checkin calls since the act of simply calling * {@code AccessControlManager.getApplicablePolicies()} (as is done in * {@link #toAcl(Session, PentahoJcrConstants, Serializable)}) will query that the node is allowed to have the * "access controlled" mixin type added. If the node is checked in, this query will return false. See Jackrabbit's * {@code ItemValidator.hasCondition()}. * </p> * * @author mlowery */ public class JcrRepositoryFileAclDao implements IRepositoryFileAclDao { // ~ Static fields/initializers // ====================================================================================== private static final Log logger = LogFactory.getLog(JcrRepositoryFileAclDao.class); // ~ Instance fields // ================================================================================================= private JcrTemplate jcrTemplate; private IPathConversionHelper pathConversionHelper; private String tenantAdminAuthorityName; // ~ Constructors // ==================================================================================================== public JcrRepositoryFileAclDao(final JcrTemplate jcrTemplate, final IPathConversionHelper pathConversionHelper, String tenantAdminAuthorityName) { super(); this.jcrTemplate = jcrTemplate; this.pathConversionHelper = pathConversionHelper; this.tenantAdminAuthorityName = tenantAdminAuthorityName; } // ~ Methods // ========================================================================================================= /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<RepositoryFileAce> getEffectiveAces(final Serializable id, final boolean forceEntriesInheriting) { return (List<RepositoryFileAce>) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { Node node = session.getNodeByIdentifier(id.toString()); if (node == null) { throw new RepositoryException(Messages.getInstance() .getString("JackrabbitRepositoryFileAclDao.ERROR_0001_NODE_NOT_FOUND", id.toString())); //$NON-NLS-1$ } // consult the parent node's effective policy if force is true and parent is not null if (forceEntriesInheriting && session.getNodeByIdentifier(id.toString()).getParent() != null) { node = node.getParent(); } String absPath = node.getPath(); AccessControlPolicy[] acPolicies = session.getAccessControlManager().getEffectivePolicies(absPath); // logic assumes policies are ordered from leaf to root for (AccessControlPolicy policy : acPolicies) { Assert.isTrue(policy instanceof AccessControlList); AccessControlList acList = ((AccessControlList) policy); if (!isEntriesInheriting(session, absPath, acList)) { List<RepositoryFileAce> aces = new ArrayList<RepositoryFileAce>(); AccessControlEntry[] acEntries = acList.getAccessControlEntries(); List<AccessControlEntry> cleanedAcEntries = JcrRepositoryFileAclUtils .removeAclMetadata(Arrays.asList(acEntries)); for (AccessControlEntry acEntry : cleanedAcEntries) { if (!acEntry.getPrincipal().equals(new SpringSecurityRolePrincipal( JcrTenantUtils.getTenantedRole(tenantAdminAuthorityName)))) { aces.add(toAce(session, acEntry)); } } return aces; } } // none are entriesInheriting=false so root aces are the effective aces AccessControlList acList = (AccessControlList) acPolicies[acPolicies.length - 1]; List<RepositoryFileAce> aces = new ArrayList<RepositoryFileAce>(); AccessControlEntry[] acEntries = acList.getAccessControlEntries(); List<AccessControlEntry> cleanedAcEntries = JcrRepositoryFileAclUtils .removeAclMetadata(Arrays.asList(acEntries)); for (AccessControlEntry acEntry : cleanedAcEntries) { if (!acEntry.getPrincipal().equals(new SpringSecurityRolePrincipal( JcrTenantUtils.getTenantedRole(tenantAdminAuthorityName)))) { aces.add(toAce(session, acEntry)); } } return aces; } }); } protected String getOwner(final Session session, final String path, final AccessControlList acList) throws RepositoryException { AclMetadata aclMetadata = JcrRepositoryFileAclUtils.getAclMetadata(session, path, acList); if (aclMetadata != null) { return aclMetadata.getOwner(); } else { return null; } } protected boolean isEntriesInheriting(final Session session, final String path, final AccessControlList acList) throws RepositoryException { AclMetadata aclMetadata = JcrRepositoryFileAclUtils.getAclMetadata(session, path, acList); if (aclMetadata != null) { return aclMetadata.isEntriesInheriting(); } else { return false; } } /** * {@inheritDoc} */ public boolean hasAccess(final String relPath, final EnumSet<RepositoryFilePermission> permissions) { return (Boolean) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { DefaultPermissionConversionHelper permissionConversionHelper = new DefaultPermissionConversionHelper( session); Privilege[] privs = permissionConversionHelper.pentahoPermissionsToPrivileges(session, permissions); try { String absPath = pathConversionHelper.relToAbs(relPath); return session.getAccessControlManager().hasPrivileges(JcrStringHelper.pathEncode(absPath), privs); } catch (PathNotFoundException e) { // never throw an exception if the path does not exist; just return false return false; } } }); } private RepositoryFileAcl toAcl(final Session session, final PentahoJcrConstants pentahoJcrConstants, final Serializable id) throws RepositoryException { Node node = session.getNodeByIdentifier(id.toString()); if (node == null) { throw new RepositoryException(Messages.getInstance() .getString("JackrabbitRepositoryFileAclDao.ERROR_0001_NODE_NOT_FOUND", id.toString())); //$NON-NLS-1$ } String absPath = node.getPath(); AccessControlManager acMgr = session.getAccessControlManager(); AccessControlList acList = getAccessControlList(acMgr, absPath); RepositoryFileSid owner = null; String ownerString = getOwner(session, absPath, acList); if (ownerString != null) { // for now, just assume all owners are users; only has UI impact owner = new RepositoryFileSid(JcrTenantUtils.getUserNameUtils().getPrincipleName(ownerString), RepositoryFileSid.Type.USER); } RepositoryFileAcl.Builder aclBuilder = new RepositoryFileAcl.Builder(id, owner); aclBuilder.entriesInheriting(isEntriesInheriting(session, absPath, acList)); List<AccessControlEntry> cleanedAcEntries = JcrRepositoryFileAclUtils .removeAclMetadata(Arrays.asList(acList.getAccessControlEntries())); for (AccessControlEntry acEntry : cleanedAcEntries) { if (!acEntry.getPrincipal().equals( new SpringSecurityRolePrincipal(JcrTenantUtils.getTenantedRole(tenantAdminAuthorityName)))) { aclBuilder.ace(toAce(session, acEntry)); } } return aclBuilder.build(); } protected RepositoryFileAce toAce(final Session session, final AccessControlEntry acEntry) throws RepositoryException { Principal principal = acEntry.getPrincipal(); RepositoryFileSid sid = null; String name = principal.getName(); DefaultPermissionConversionHelper permissionConversionHelper = new DefaultPermissionConversionHelper( session); if (principal instanceof Group) { sid = new RepositoryFileSid(JcrTenantUtils.getRoleNameUtils().getPrincipleName(name), RepositoryFileSid.Type.ROLE); } else { sid = new RepositoryFileSid(JcrTenantUtils.getUserNameUtils().getPrincipleName(name), RepositoryFileSid.Type.USER); } logger.debug(String.format("principal class [%s]", principal.getClass().getName())); //$NON-NLS-1$ Privilege[] privileges = acEntry.getPrivileges(); return new RepositoryFileAce(sid, permissionConversionHelper.privilegesToPentahoPermissions(session, privileges)); } /** * Converts between {@code RepositoryFilePermission} and {@code Privilege} instances. */ public static interface IPermissionConversionHelper { Privilege[] pentahoPermissionsToPrivileges(final Session session, final EnumSet<RepositoryFilePermission> permission) throws RepositoryException; EnumSet<RepositoryFilePermission> privilegesToPentahoPermissions(final Session session, final Privilege[] privileges) throws RepositoryException; } public void addAce(final Serializable id, final RepositoryFileSid recipient, final EnumSet<RepositoryFilePermission> permission) { if (isKioskEnabled()) { throw new RuntimeException( Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0006_ACCESS_DENIED")); //$NON-NLS-1$ } Assert.notNull(id); Assert.notNull(recipient); Assert.notNull(permission); RepositoryFileAcl acl = getAcl(id); Assert.notNull(acl); // TODO mlowery find an ACE with the recipient and update that rather than adding a new ACE RepositoryFileSid newRecipient = recipient; if (recipient.getType().equals(Type.USER)) { if (JcrTenantUtils.getUserNameUtils().getTenant(recipient.getName()) == null) { newRecipient = new RepositoryFileSid(JcrTenantUtils.getTenantedUser(recipient.getName()), recipient.getType()); } } else { if (JcrTenantUtils.getRoleNameUtils().getTenant(recipient.getName()) == null) { newRecipient = new RepositoryFileSid(JcrTenantUtils.getTenantedRole(recipient.getName()), recipient.getType()); } } RepositoryFileAcl updatedAcl = new RepositoryFileAcl.Builder(acl).ace(newRecipient, permission).build(); updateAcl(updatedAcl); logger.debug("added ace: id=" + id + ", sid=" + recipient + ", permission=" + permission); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } public RepositoryFileAcl createAcl(final Serializable fileId, final RepositoryFileAcl acl) { if (isKioskEnabled()) { throw new RuntimeException( Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0006_ACCESS_DENIED")); //$NON-NLS-1$ } return (RepositoryFileAcl) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session); Node node = session.getNodeByIdentifier(fileId.toString()); String absPath = node.getPath(); AccessControlManager acMgr = session.getAccessControlManager(); AccessControlList acList = getAccessControlList(acMgr, absPath); acMgr.setPolicy(absPath, acList); return internalUpdateAcl(session, pentahoJcrConstants, fileId, acl); } }); } private AccessControlList getAccessControlList(final AccessControlManager acMgr, final String path) throws RepositoryException { AccessControlPolicyIterator applicablePolicies = acMgr.getApplicablePolicies(path); while (applicablePolicies.hasNext()) { AccessControlPolicy policy = applicablePolicies.nextAccessControlPolicy(); if (policy instanceof AccessControlList) { return (AccessControlList) policy; } } AccessControlPolicy[] policies = acMgr.getPolicies(path); for (int i = 0; i < policies.length; i++) { if (policies[i] instanceof AccessControlList) { return (AccessControlList) policies[i]; } } throw new IllegalStateException("no access control list applies or is bound to node"); } public RepositoryFileAcl getAcl(final Serializable id) { return (RepositoryFileAcl) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session); return toAcl(session, pentahoJcrConstants, id); } }); } protected RepositoryFileAcl getParentAcl(final Serializable id) { return (RepositoryFileAcl) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session); Node node = session.getNodeByIdentifier(id.toString()); if (!node.getParent().isSame(session.getRootNode())) { return toAcl(session, pentahoJcrConstants, node.getParent().getIdentifier()); } else { return null; } } }); } public void setFullControl(Serializable id, RepositoryFileSid sid, RepositoryFilePermission permission) { addAce(id, sid, EnumSet.of(permission)); } public RepositoryFileAcl updateAcl(final RepositoryFileAcl acl) { return (RepositoryFileAcl) jcrTemplate.execute(new JcrCallback() { public Object doInJcr(final Session session) throws RepositoryException, IOException { PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session); JcrRepositoryFileUtils.checkoutNearestVersionableFileIfNecessary(session, pentahoJcrConstants, acl.getId()); RepositoryFileAcl updatedAcl = internalUpdateAcl(session, pentahoJcrConstants, acl.getId(), acl); JcrRepositoryFileUtils.checkinNearestVersionableFileIfNecessary(session, pentahoJcrConstants, acl.getId(), null, null, true); return updatedAcl; } }); } protected RepositoryFileAcl internalUpdateAcl(final Session session, final PentahoJcrConstants pentahoJcrConstants, final Serializable fileId, final RepositoryFileAcl acl) throws RepositoryException { if (isKioskEnabled()) { throw new RuntimeException( Messages.getInstance().getString("JcrRepositoryFileDao.ERROR_0006_ACCESS_DENIED")); //$NON-NLS-1$ } DefaultPermissionConversionHelper permissionConversionHelper = new DefaultPermissionConversionHelper( session); Node node = session.getNodeByIdentifier(fileId.toString()); if (node == null) { throw new RepositoryException(Messages.getInstance() .getString("JackrabbitRepositoryFileAclDao.ERROR_0001_NODE_NOT_FOUND", fileId.toString())); //$NON-NLS-1$ } String absPath = node.getPath(); AccessControlManager acMgr = session.getAccessControlManager(); AccessControlList acList = getAccessControlList(acMgr, absPath); // clear all entries AccessControlEntry[] acEntries = acList.getAccessControlEntries(); for (int i = 0; i < acEntries.length; i++) { acList.removeAccessControlEntry(acEntries[i]); } JcrRepositoryFileAclUtils.setAclMetadata(session, absPath, acList, new AclMetadata(acl.getOwner().getName(), acl.isEntriesInheriting())); // add entries to now empty list but only if not inheriting; force user to start with clean slate boolean adminPrincipalExist = false; ITenant principalTenant = null; if (!acl.isEntriesInheriting()) { for (RepositoryFileAce ace : acl.getAces()) { Principal principal = null; if (RepositoryFileSid.Type.ROLE == ace.getSid().getType()) { String principalName = JcrTenantUtils.getRoleNameUtils() .getPrincipleName(ace.getSid().getName()); if (tenantAdminAuthorityName.equals(principalName)) { adminPrincipalExist = true; } principal = new SpringSecurityRolePrincipal( JcrTenantUtils.getTenantedRole(ace.getSid().getName())); } else { principal = new SpringSecurityUserPrincipal( JcrTenantUtils.getTenantedUser(ace.getSid().getName())); } acList.addAccessControlEntry(principal, permissionConversionHelper.pentahoPermissionsToPrivileges(session, ace.getPermissions())); } if (!adminPrincipalExist) { if (acl.getAces() != null && acl.getAces().size() > 0) { principalTenant = JcrTenantUtils.getRoleNameUtils() .getTenant(acl.getAces().get(0).getSid().getName()); } if (principalTenant == null || principalTenant.getId() == null) { principalTenant = JcrTenantUtils.getTenant(); } List<RepositoryFilePermission> permissionList = new ArrayList<RepositoryFilePermission>(); permissionList.add(RepositoryFilePermission.ALL); Principal adminPrincipal = new SpringSecurityRolePrincipal(JcrTenantUtils.getRoleNameUtils() .getPrincipleId(principalTenant, tenantAdminAuthorityName)); acList.addAccessControlEntry(adminPrincipal, permissionConversionHelper .pentahoPermissionsToPrivileges(session, EnumSet.copyOf(permissionList))); } } acMgr.setPolicy(absPath, acList); session.save(); return getAcl(fileId); } private boolean isKioskEnabled() { if (PentahoSystem.getInitializedOK()) { return "true".equals(PentahoSystem.getSystemSetting("kiosk-mode", "false")); } else { return false; } } }