From d184cb3eb4b7721ac3e9729c2a38d85150e40d46 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sun, 24 Mar 2013 12:09:52 -0500 Subject: [PATCH] HHH-7462 - Make JACC service --- .../org/hibernate/cfg/AvailableSettings.java | 9 +- .../java/org/hibernate/cfg/Configuration.java | 50 ++-- .../AbstractPreDatabaseOperationEvent.java | 17 +- .../hibernate/event/spi/PreDeleteEvent.java | 7 +- .../org/hibernate/event/spi/PreLoadEvent.java | 18 +- .../internal/IntegratorServiceImpl.java | 2 + .../AbstractJaccSecurableEventListener.java | 53 +++++ .../internal/DisabledJaccServiceImpl.java | 48 ++++ .../secure/internal/HibernatePermission.java | 70 ------ .../secure/internal/JACCConfiguration.java | 88 ------- .../secure/internal/JACCPermissions.java | 144 ------------ ...r.java => JaccPreDeleteEventListener.java} | 19 +- ...r.java => JaccPreInsertEventListener.java} | 19 +- ...ner.java => JaccPreLoadEventListener.java} | 19 +- ...r.java => JaccPreUpdateEventListener.java} | 19 +- ...istener.java => JaccSecurityListener.java} | 5 +- .../internal/StandardJaccServiceImpl.java | 221 ++++++++++++++++++ .../org/hibernate/secure/package-info.java | 5 + .../java/org/hibernate/secure/package.html | 33 --- .../secure/spi/GrantedPermission.java | 53 +++++ .../secure/spi/IntegrationException.java | 39 ++++ .../hibernate/secure/spi/JaccIntegrator.java | 132 +++++++++++ .../spi/JaccPermissionDeclarations.java | 50 ++++ .../org/hibernate/secure/spi/JaccService.java | 36 +++ .../secure/spi/PermissibleAction.java | 80 +++++++ .../spi/PermissionCheckEntityInformation.java | 35 +++ .../org/hibernate/jpa/AvailableSettings.java | 6 +- .../EntityManagerFactoryBuilderImpl.java | 24 +- .../jpa/event/spi/JpaIntegrator.java | 43 ---- 29 files changed, 865 insertions(+), 479 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/internal/AbstractJaccSecurableEventListener.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/internal/DisabledJaccServiceImpl.java delete mode 100755 hibernate-core/src/main/java/org/hibernate/secure/internal/HibernatePermission.java delete mode 100755 hibernate-core/src/main/java/org/hibernate/secure/internal/JACCConfiguration.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPermissions.java rename hibernate-core/src/main/java/org/hibernate/secure/internal/{JACCPreDeleteEventListener.java => JaccPreDeleteEventListener.java} (72%) rename hibernate-core/src/main/java/org/hibernate/secure/internal/{JACCPreInsertEventListener.java => JaccPreInsertEventListener.java} (72%) rename hibernate-core/src/main/java/org/hibernate/secure/internal/{JACCPreLoadEventListener.java => JaccPreLoadEventListener.java} (72%) rename hibernate-core/src/main/java/org/hibernate/secure/internal/{JACCPreUpdateEventListener.java => JaccPreUpdateEventListener.java} (72%) rename hibernate-core/src/main/java/org/hibernate/secure/internal/{JACCSecurityListener.java => JaccSecurityListener.java} (84%) create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/internal/StandardJaccServiceImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/package-info.java delete mode 100755 hibernate-core/src/main/java/org/hibernate/secure/package.html create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/GrantedPermission.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/IntegrationException.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/JaccIntegrator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/JaccPermissionDeclarations.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/JaccService.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/PermissibleAction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/secure/spi/PermissionCheckEntityInformation.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 88af1d7786..3d89015f0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -417,11 +417,6 @@ public interface AvailableSettings { */ public static final String DEFAULT_ENTITY_MODE = "hibernate.default_entity_mode"; - /** - * The jacc context id of the deployment - */ - public static final String JACC_CONTEXTID = "hibernate.jacc_context_id"; - /** * Should all database identifiers be quoted. */ @@ -639,4 +634,8 @@ public interface AvailableSettings { * Default is true (enabled). */ public static final String JTA_TRACK_BY_THREAD = "hibernate.jta.track_by_thread"; + + public static final String JACC_CONTEXT_ID = "hibernate.jacc_context_id"; + public static final String JACC_PREFIX = "hibernate.jacc"; + public static final String JACC_ENABLED = "hibernate.jacc.enabled"; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index 701d769946..c62ef69eff 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -134,7 +134,8 @@ import org.hibernate.mapping.Table; import org.hibernate.mapping.TypeDef; import org.hibernate.mapping.UniqueKey; import org.hibernate.proxy.EntityNotFoundDelegate; -import org.hibernate.secure.internal.JACCConfiguration; +import org.hibernate.secure.spi.GrantedPermission; +import org.hibernate.secure.spi.JaccPermissionDeclarations; import org.hibernate.service.ServiceRegistry; import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.IndexMetadata; @@ -2198,23 +2199,42 @@ public class Configuration implements Serializable { } } + private JaccPermissionDeclarations jaccPermissionDeclarations; + private void parseSecurity(Element secNode) { - String contextId = secNode.attributeValue( "context" ); - setProperty( Environment.JACC_CONTEXTID, contextId ); - LOG.jaccContextId( contextId ); - JACCConfiguration jcfg = new JACCConfiguration( contextId ); - Iterator grantElements = secNode.elementIterator(); - while ( grantElements.hasNext() ) { - Element grantElement = (Element) grantElements.next(); - String elementName = grantElement.getName(); - if ( "grant".equals( elementName ) ) { - jcfg.addPermission( - grantElement.attributeValue( "role" ), - grantElement.attributeValue( "entity-name" ), - grantElement.attributeValue( "actions" ) - ); + final String nodeContextId = secNode.attributeValue( "context" ); + + final String explicitContextId = getProperty( AvailableSettings.JACC_CONTEXT_ID ); + if ( explicitContextId == null ) { + setProperty( AvailableSettings.JACC_CONTEXT_ID, nodeContextId ); + LOG.jaccContextId( nodeContextId ); + } + else { + // if they dont match, throw an error + if ( ! nodeContextId.equals( explicitContextId ) ) { + throw new HibernateException( "Non-matching JACC context ids" ); } } + jaccPermissionDeclarations = new JaccPermissionDeclarations( nodeContextId ); + + Iterator grantElements = secNode.elementIterator(); + while ( grantElements.hasNext() ) { + final Element grantElement = (Element) grantElements.next(); + final String elementName = grantElement.getName(); + if ( "grant".equals( elementName ) ) { + jaccPermissionDeclarations.addPermissionDeclaration( + new GrantedPermission( + grantElement.attributeValue( "role" ), + grantElement.attributeValue( "entity-name" ), + grantElement.attributeValue( "actions" ) + ) + ); + } + } + } + + public JaccPermissionDeclarations getJaccPermissionDeclarations() { + return jaccPermissionDeclarations; } RootClass getRootClassMapping(String clazz) throws MappingException { diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java index c39afd5dde..685e72c13b 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/AbstractPreDatabaseOperationEvent.java @@ -26,13 +26,17 @@ package org.hibernate.event.spi; import java.io.Serializable; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; /** * Represents an operation we are about to perform against the database. * * @author Steve Ebersole */ -public abstract class AbstractPreDatabaseOperationEvent extends AbstractEvent { +public abstract class AbstractPreDatabaseOperationEvent + extends AbstractEvent + implements PermissionCheckEntityInformation { + private final Object entity; private final Serializable id; private final EntityPersister persister; @@ -61,6 +65,7 @@ public abstract class AbstractPreDatabaseOperationEvent extends AbstractEvent { * * @return The entity. */ + @Override public Object getEntity() { return entity; } @@ -97,4 +102,14 @@ public abstract class AbstractPreDatabaseOperationEvent extends AbstractEvent { public EventSource getSource() { return getSession(); } + + @Override + public String getEntityName() { + return persister.getEntityName(); + } + + @Override + public Serializable getIdentifier() { + return id; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java index 9d4ded751a..7fae5576b9 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreDeleteEvent.java @@ -26,6 +26,8 @@ package org.hibernate.event.spi; import java.io.Serializable; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; + /** * Represents a pre-delete event, which occurs just prior to @@ -34,7 +36,10 @@ import org.hibernate.persister.entity.EntityPersister; * @author Gavin King * @author Steve Ebersole */ -public class PreDeleteEvent extends AbstractPreDatabaseOperationEvent { +public class PreDeleteEvent + extends AbstractPreDatabaseOperationEvent + implements PermissionCheckEntityInformation{ + private Object[] deletedState; /** diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java index 7d825fee87..e7b2a2e283 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PreLoadEvent.java @@ -26,14 +26,14 @@ package org.hibernate.event.spi; import java.io.Serializable; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; /** - * Called before injecting property values into a newly - * loaded entity instance. + * Called before injecting property values into a newly loaded entity instance. * * @author Gavin King */ -public class PreLoadEvent extends AbstractEvent { +public class PreLoadEvent extends AbstractEvent implements PermissionCheckEntityInformation { private Object entity; private Object[] state; private Serializable id; @@ -43,6 +43,7 @@ public class PreLoadEvent extends AbstractEvent { super(session); } + @Override public Object getEntity() { return entity; } @@ -78,5 +79,14 @@ public class PreLoadEvent extends AbstractEvent { this.state = state; return this; } - + + @Override + public String getEntityName() { + return persister.getEntityName(); + } + + @Override + public Serializable getIdentifier() { + return id; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/integrator/internal/IntegratorServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/integrator/internal/IntegratorServiceImpl.java index b4cd7872d7..f8829a04e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/integrator/internal/IntegratorServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/integrator/internal/IntegratorServiceImpl.java @@ -31,6 +31,7 @@ import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator; import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.IntegratorService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.secure.spi.JaccIntegrator; /** * @author Steve Ebersole @@ -44,6 +45,7 @@ public class IntegratorServiceImpl implements IntegratorService { // register standard integrators. Envers and JPA, for example, need to be handled by discovery because in // separate project/jars. addIntegrator( new BeanValidationIntegrator() ); + addIntegrator( new JaccIntegrator() ); // register provided integrators for ( Integrator integrator : providedIntegrators ) { diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/AbstractJaccSecurableEventListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/AbstractJaccSecurableEventListener.java new file mode 100644 index 0000000000..38173b73c3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/AbstractJaccSecurableEventListener.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.internal; + +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.event.spi.AbstractPreDatabaseOperationEvent; +import org.hibernate.secure.spi.JaccService; +import org.hibernate.secure.spi.PermissibleAction; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; + +/** + * Base class for JACC-securable event listeners + * + * @author Steve Ebersole + */ +public abstract class AbstractJaccSecurableEventListener implements JaccSecurityListener { + private JaccService jaccService; + + protected void performSecurityCheck(AbstractPreDatabaseOperationEvent event, PermissibleAction action) { + performSecurityCheck( event.getSession(), event, action ); + } + + protected void performSecurityCheck( + SessionImplementor session, + PermissionCheckEntityInformation entityInformation, + PermissibleAction action) { + if ( jaccService == null ) { + jaccService = session.getFactory().getServiceRegistry().getService( JaccService.class ); + } + jaccService.checkPermission( entityInformation, action ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/DisabledJaccServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/DisabledJaccServiceImpl.java new file mode 100644 index 0000000000..29aaf8b61d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/DisabledJaccServiceImpl.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.internal; + +import org.jboss.logging.Logger; + +import org.hibernate.secure.spi.GrantedPermission; +import org.hibernate.secure.spi.JaccService; +import org.hibernate.secure.spi.PermissibleAction; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; + +/** + * @author Steve Ebersole + */ +public class DisabledJaccServiceImpl implements JaccService { + private static final Logger log = Logger.getLogger( DisabledJaccServiceImpl.class ); + + @Override + public void addPermission(GrantedPermission permissionDeclaration) { + log.debug( "Ignoring call to addPermission on disabled JACC service" ); + } + + @Override + public void checkPermission(PermissionCheckEntityInformation entityInformation, PermissibleAction action) { + log.debug( "Ignoring call to checkPermission on disabled JACC service" ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/HibernatePermission.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/HibernatePermission.java deleted file mode 100755 index 071754d679..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/HibernatePermission.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * 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 Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.secure.internal; - -import java.security.Permission; - -/** - * @author Gavin King - */ -public class HibernatePermission extends Permission { - public static final String INSERT = "insert"; - public static final String UPDATE = "update"; - public static final String DELETE = "delete"; - public static final String READ = "read"; - public static final String ANY = "*"; - - private final String actions; - - public HibernatePermission(String entityName, String actions) { - super(entityName); - this.actions = actions; - } - - public boolean implies(Permission permission) { - //TODO! - return ( "*".equals( getName() ) || getName().equals( permission.getName() ) ) && - ( "*".equals(actions) || actions.indexOf( permission.getActions() ) >= 0 ); - } - - public boolean equals(Object obj) { - if ( !(obj instanceof HibernatePermission) ) return false; - HibernatePermission permission = (HibernatePermission) obj; - return permission.getName().equals( getName() ) && - permission.getActions().equals(actions); - } - - public int hashCode() { - return getName().hashCode() * 37 + actions.hashCode(); - } - - public String getActions() { - return actions; - } - - public String toString() { - return "HibernatePermission(" + getName() + ':' + actions + ')'; - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCConfiguration.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCConfiguration.java deleted file mode 100755 index a6e2a31a83..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCConfiguration.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * 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 Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.secure.internal; - -import java.util.StringTokenizer; -import javax.security.jacc.EJBMethodPermission; -import javax.security.jacc.PolicyConfiguration; -import javax.security.jacc.PolicyConfigurationFactory; -import javax.security.jacc.PolicyContextException; - -import org.jboss.logging.Logger; - -import org.hibernate.HibernateException; -import org.hibernate.internal.CoreMessageLogger; - -/** - * Adds Hibernate permissions to roles via JACC - * - * @author Gavin King - */ -public class JACCConfiguration { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JACCConfiguration.class.getName()); - - private final PolicyConfiguration policyConfiguration; - - public JACCConfiguration(String contextId) throws HibernateException { - try { - policyConfiguration = PolicyConfigurationFactory - .getPolicyConfigurationFactory() - .getPolicyConfiguration( contextId, false ); - } - catch (ClassNotFoundException cnfe) { - throw new HibernateException( "JACC provider class not found", cnfe ); - } - catch (PolicyContextException pce) { - throw new HibernateException( "policy context exception occurred", pce ); - } - } - - public void addPermission(String role, String entityName, String action) { - - if ( action.equals( "*" ) ) { - action = "insert,read,update,delete"; - } - - StringTokenizer tok = new StringTokenizer( action, "," ); - - while ( tok.hasMoreTokens() ) { - String methodName = tok.nextToken().trim(); - EJBMethodPermission permission = new EJBMethodPermission( - entityName, - methodName, - null, // interfaces - null // arguments - ); - - LOG.debugf("Adding permission to role %s: %s", role, permission); - try { - policyConfiguration.addToRole( role, permission ); - } - catch (PolicyContextException pce) { - throw new HibernateException( "policy context exception occurred", pce ); - } - } - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPermissions.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPermissions.java deleted file mode 100644 index 31eb980f47..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPermissions.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * 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 Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.secure.internal; - -import java.lang.reflect.UndeclaredThrowableException; -import java.security.AccessController; -import java.security.CodeSource; -import java.security.Policy; -import java.security.Principal; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.ProtectionDomain; -import java.util.Set; -import javax.security.auth.Subject; -import javax.security.jacc.EJBMethodPermission; -import javax.security.jacc.PolicyContext; -import javax.security.jacc.PolicyContextException; - -/** - * Copied from JBoss org.jboss.ejb3.security.JaccHelper and org.jboss.ejb3.security.SecurityActions - * - * @author Kabir Khan - */ -public class JACCPermissions { - - public static void checkPermission(Class clazz, String contextID, EJBMethodPermission methodPerm) - throws SecurityException { - CodeSource ejbCS = clazz.getProtectionDomain().getCodeSource(); - - try { - setContextID( contextID ); - - Policy policy = Policy.getPolicy(); - // Get the caller - Subject caller = getContextSubject(); - - Principal[] principals = null; - if ( caller != null ) { - // Get the caller principals - Set principalsSet = caller.getPrincipals(); - principals = new Principal[ principalsSet.size() ]; - principalsSet.toArray( principals ); - } - - ProtectionDomain pd = new ProtectionDomain( ejbCS, null, null, principals ); - if ( policy.implies( pd, methodPerm ) == false ) { - String msg = "Denied: " + methodPerm + ", caller=" + caller; - SecurityException e = new SecurityException( msg ); - throw e; - } - } - catch (PolicyContextException e) { - throw new RuntimeException( e ); - } - } - - interface PolicyContextActions { - /** - * The JACC PolicyContext key for the current Subject - */ - static final String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container"; - PolicyContextActions PRIVILEGED = new PolicyContextActions() { - private final PrivilegedExceptionAction exAction = new PrivilegedExceptionAction() { - public Object run() throws Exception { - return PolicyContext.getContext( SUBJECT_CONTEXT_KEY ); - } - }; - - public Subject getContextSubject() throws PolicyContextException { - try { - return (Subject) AccessController.doPrivileged( exAction ); - } - catch (PrivilegedActionException e) { - Exception ex = e.getException(); - if ( ex instanceof PolicyContextException ) { - throw (PolicyContextException) ex; - } - else { - throw new UndeclaredThrowableException( ex ); - } - } - } - }; - - PolicyContextActions NON_PRIVILEGED = new PolicyContextActions() { - public Subject getContextSubject() throws PolicyContextException { - return (Subject) PolicyContext.getContext( SUBJECT_CONTEXT_KEY ); - } - }; - - Subject getContextSubject() throws PolicyContextException; - } - - static Subject getContextSubject() throws PolicyContextException { - if ( System.getSecurityManager() == null ) { - return PolicyContextActions.NON_PRIVILEGED.getContextSubject(); - } - else { - return PolicyContextActions.PRIVILEGED.getContextSubject(); - } - } - - private static class SetContextID implements PrivilegedAction { - String contextID; - - SetContextID(String contextID) { - this.contextID = contextID; - } - - public Object run() { - String previousID = PolicyContext.getContextID(); - PolicyContext.setContextID( contextID ); - return previousID; - } - } - - static String setContextID(String contextID) { - PrivilegedAction action = new SetContextID( contextID ); - String previousID = (String) AccessController.doPrivileged( action ); - return previousID; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreDeleteEventListener.java similarity index 72% rename from hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreDeleteEventListener.java rename to hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreDeleteEventListener.java index 955842f8d9..5ad75293cd 100755 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreDeleteEventListener.java @@ -23,31 +23,22 @@ */ package org.hibernate.secure.internal; -import javax.security.jacc.EJBMethodPermission; - import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreDeleteEventListener; +import org.hibernate.secure.spi.PermissibleAction; /** * Check security before any deletion * * @author Kabir Khan + * @author Steve Ebersole */ -public class JACCPreDeleteEventListener implements PreDeleteEventListener, JACCSecurityListener { - private final String contextId; - - public JACCPreDeleteEventListener(String contextId) { - this.contextId = contextId; +public class JaccPreDeleteEventListener extends AbstractJaccSecurableEventListener implements PreDeleteEventListener { + public JaccPreDeleteEventListener() { } public boolean onPreDelete(PreDeleteEvent event) { - final EJBMethodPermission deletePermission = new EJBMethodPermission( - event.getPersister().getEntityName(), - HibernatePermission.DELETE, - null, - null - ); - JACCPermissions.checkPermission( event.getEntity().getClass(), contextId, deletePermission ); + performSecurityCheck( event, PermissibleAction.DELETE ); return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreInsertEventListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreInsertEventListener.java similarity index 72% rename from hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreInsertEventListener.java rename to hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreInsertEventListener.java index b05a73cfd7..6dd0babb2f 100755 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreInsertEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreInsertEventListener.java @@ -23,31 +23,22 @@ */ package org.hibernate.secure.internal; -import javax.security.jacc.EJBMethodPermission; - import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; +import org.hibernate.secure.spi.PermissibleAction; /** * Check security before an insertion * * @author Kabir Khan + * @author Steve Ebersole */ -public class JACCPreInsertEventListener implements PreInsertEventListener, JACCSecurityListener { - private final String contextId; - - public JACCPreInsertEventListener(String contextId) { - this.contextId = contextId; +public class JaccPreInsertEventListener extends AbstractJaccSecurableEventListener implements PreInsertEventListener { + public JaccPreInsertEventListener() { } public boolean onPreInsert(PreInsertEvent event) { - final EJBMethodPermission insertPermission = new EJBMethodPermission( - event.getPersister().getEntityName(), - HibernatePermission.INSERT, - null, - null - ); - JACCPermissions.checkPermission( event.getEntity().getClass(), contextId, insertPermission ); + performSecurityCheck( event, PermissibleAction.INSERT ); return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreLoadEventListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreLoadEventListener.java similarity index 72% rename from hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreLoadEventListener.java rename to hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreLoadEventListener.java index 51423314a6..492ce52680 100755 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreLoadEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreLoadEventListener.java @@ -23,30 +23,21 @@ */ package org.hibernate.secure.internal; -import javax.security.jacc.EJBMethodPermission; - import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.event.spi.PreLoadEventListener; +import org.hibernate.secure.spi.PermissibleAction; /** * Check security before any load * * @author Kabir Khan + * @author Steve Ebersole */ -public class JACCPreLoadEventListener implements PreLoadEventListener, JACCSecurityListener { - private final String contextId; - - public JACCPreLoadEventListener(String contextId) { - this.contextId = contextId; +public class JaccPreLoadEventListener extends AbstractJaccSecurableEventListener implements PreLoadEventListener { + public JaccPreLoadEventListener() { } public void onPreLoad(PreLoadEvent event) { - final EJBMethodPermission loadPermission = new EJBMethodPermission( - event.getPersister().getEntityName(), - HibernatePermission.READ, - null, - null - ); - JACCPermissions.checkPermission( event.getEntity().getClass(), contextId, loadPermission ); + performSecurityCheck( event.getSession(), event, PermissibleAction.READ ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreUpdateEventListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreUpdateEventListener.java similarity index 72% rename from hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreUpdateEventListener.java rename to hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreUpdateEventListener.java index f7e9d73303..4f481266ed 100755 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCPreUpdateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccPreUpdateEventListener.java @@ -23,31 +23,22 @@ */ package org.hibernate.secure.internal; -import javax.security.jacc.EJBMethodPermission; - import org.hibernate.event.spi.PreUpdateEvent; import org.hibernate.event.spi.PreUpdateEventListener; +import org.hibernate.secure.spi.PermissibleAction; /** * Check security before any update * * @author Kabir Khan + * @author Steve Ebersole */ -public class JACCPreUpdateEventListener implements PreUpdateEventListener, JACCSecurityListener { - private final String contextId; - - public JACCPreUpdateEventListener(String contextId) { - this.contextId = contextId; +public class JaccPreUpdateEventListener extends AbstractJaccSecurableEventListener implements PreUpdateEventListener { + public JaccPreUpdateEventListener() { } public boolean onPreUpdate(PreUpdateEvent event) { - final EJBMethodPermission updatePermission = new EJBMethodPermission( - event.getPersister().getEntityName(), - HibernatePermission.UPDATE, - null, - null - ); - JACCPermissions.checkPermission( event.getEntity().getClass(), contextId, updatePermission ); + performSecurityCheck( event, PermissibleAction.UPDATE ); return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCSecurityListener.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccSecurityListener.java similarity index 84% rename from hibernate-core/src/main/java/org/hibernate/secure/internal/JACCSecurityListener.java rename to hibernate-core/src/main/java/org/hibernate/secure/internal/JaccSecurityListener.java index 90b9871790..2dfe030f12 100644 --- a/hibernate-core/src/main/java/org/hibernate/secure/internal/JACCSecurityListener.java +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/JaccSecurityListener.java @@ -24,9 +24,10 @@ package org.hibernate.secure.internal; /** - * Marker interface for JACC event listeners + * Marker interface for JACC event listeners. Used in event listener duplication strategy checks; see + * {@link org.hibernate.secure.spi.JaccIntegrator} for details. * * @author Kabir Khan */ -public interface JACCSecurityListener { +public interface JaccSecurityListener { } diff --git a/hibernate-core/src/main/java/org/hibernate/secure/internal/StandardJaccServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/secure/internal/StandardJaccServiceImpl.java new file mode 100644 index 0000000000..25e173d581 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/internal/StandardJaccServiceImpl.java @@ -0,0 +1,221 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.internal; + +import javax.security.auth.Subject; +import javax.security.jacc.EJBMethodPermission; +import javax.security.jacc.PolicyConfiguration; +import javax.security.jacc.PolicyConfigurationFactory; +import javax.security.jacc.PolicyContext; +import javax.security.jacc.PolicyContextException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Policy; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Map; +import java.util.Set; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.secure.spi.GrantedPermission; +import org.hibernate.secure.spi.IntegrationException; +import org.hibernate.secure.spi.JaccService; +import org.hibernate.secure.spi.PermissibleAction; +import org.hibernate.secure.spi.PermissionCheckEntityInformation; +import org.hibernate.service.spi.Configurable; + +/** + * @author Steve Ebersole + */ +public class StandardJaccServiceImpl implements JaccService, Configurable { + private static final Logger log = Logger.getLogger( StandardJaccServiceImpl.class ); + + private String contextId; + private PolicyConfiguration policyConfiguration; + + @Override + public void configure(Map configurationValues) { + this.contextId = (String) configurationValues.get( AvailableSettings.JACC_CONTEXT_ID ); + } + + @Override + public void addPermission(GrantedPermission permissionDeclaration) { + // todo : do we need to wrap these PolicyConfiguration calls in privileged actions like we do during permission checks? + + if ( policyConfiguration == null ) { + policyConfiguration = locatePolicyConfiguration( contextId ); + } + + for ( String grantedAction : permissionDeclaration.getPermissibleAction().getImpliedActions() ) { + final EJBMethodPermission permission = new EJBMethodPermission( + permissionDeclaration.getEntityName(), + grantedAction, + null, // interfaces + null // arguments + ); + + log.debugf( "Adding permission [%s] to role [%s]", grantedAction, permissionDeclaration.getRole() ); + try { + policyConfiguration.addToRole( permissionDeclaration.getRole(), permission ); + } + catch (PolicyContextException pce) { + throw new HibernateException( "policy context exception occurred", pce ); + } + } + } + + private PolicyConfiguration locatePolicyConfiguration(String contextId) { + try { + return PolicyConfigurationFactory + .getPolicyConfigurationFactory() + .getPolicyConfiguration( contextId, false ); + } + catch (Exception e) { + throw new IntegrationException( "Unable to access JACC PolicyConfiguration" ); + } + } + + @Override + public void checkPermission(PermissionCheckEntityInformation entityInformation, PermissibleAction action) { + if ( action == PermissibleAction.ANY ) { + throw new HibernateException( "ANY action (*) is not legal for permission check, only for configuration" ); + } + + final String originalContextId = AccessController.doPrivileged( new ContextIdSetAction( contextId ) ); + try { + doPermissionCheckInContext( entityInformation, action ); + } + finally { + AccessController.doPrivileged( new ContextIdSetAction( originalContextId ) ); + } + } + + private static class ContextIdSetAction implements PrivilegedAction { + private final String contextId; + + private ContextIdSetAction(String contextId) { + this.contextId = contextId; + } + + @Override + public String run() { + String previousID = PolicyContext.getContextID(); + PolicyContext.setContextID( contextId ); + return previousID; + } + } + + private void doPermissionCheckInContext(PermissionCheckEntityInformation entityInformation, PermissibleAction action) { + final Policy policy = Policy.getPolicy(); + final Principal[] principals = getCallerPrincipals(); + + final CodeSource codeSource = entityInformation.getEntity().getClass().getProtectionDomain().getCodeSource(); + final ProtectionDomain pd = new ProtectionDomain( codeSource, null, null, principals ); + + // the action is known as 'method name' in JACC + final EJBMethodPermission jaccPermission = new EJBMethodPermission( + entityInformation.getEntityName(), + action.getImpliedActions()[0], + null, + null + ); + + if ( ! policy.implies( pd, jaccPermission) ) { + throw new SecurityException( + String.format( + "JACC denied permission to [%s.%s] for [%s]", + entityInformation.getEntityName(), + action.getImpliedActions()[0], + join( principals ) + ) + ); + } + } + + private String join(Principal[] principals) { + String separator = ""; + final StringBuilder buffer = new StringBuilder(); + for ( Principal principal : principals ) { + buffer.append( separator ).append( principal.getName() ); + separator = ", "; + } + return buffer.toString(); + } + + protected Principal[] getCallerPrincipals() { + final Subject caller = getContextSubjectAccess().getContextSubject(); + if ( caller == null ) { + return new Principal[0]; + } + + final Set principalsSet = caller.getPrincipals(); + return principalsSet.toArray( new Principal[ principalsSet.size()] ); + } + + private ContextSubjectAccess getContextSubjectAccess() { + return ( System.getSecurityManager() == null ) + ? NonPrivilegedContextSubjectAccess.INSTANCE + : PrivilegedContextSubjectAccess.INSTANCE; + } + + protected static interface ContextSubjectAccess { + public static final String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container"; + + public Subject getContextSubject(); + } + + protected static class PrivilegedContextSubjectAccess implements ContextSubjectAccess { + public static final PrivilegedContextSubjectAccess INSTANCE = new PrivilegedContextSubjectAccess(); + + private final PrivilegedAction privilegedAction = new PrivilegedAction() { + public Subject run() { + return NonPrivilegedContextSubjectAccess.INSTANCE.getContextSubject(); + } + }; + + @Override + public Subject getContextSubject() { + return AccessController.doPrivileged( privilegedAction ); + } + } + + protected static class NonPrivilegedContextSubjectAccess implements ContextSubjectAccess { + public static final NonPrivilegedContextSubjectAccess INSTANCE = new NonPrivilegedContextSubjectAccess(); + + @Override + public Subject getContextSubject() { + try { + return (Subject) PolicyContext.getContext( SUBJECT_CONTEXT_KEY ); + } + catch (PolicyContextException e) { + throw new HibernateException( "Unable to access JACC PolicyContext in order to locate calling Subject", e ); + } + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/package-info.java b/hibernate-core/src/main/java/org/hibernate/secure/package-info.java new file mode 100644 index 0000000000..31a9b0b7ff --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/package-info.java @@ -0,0 +1,5 @@ +package org.hibernate.secure; + +/** + * Package defining support for declarative security of CRUD operations via JACC. + */ \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/secure/package.html b/hibernate-core/src/main/java/org/hibernate/secure/package.html deleted file mode 100755 index 24eae8cfdf..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/secure/package.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - -

- Declarative security for CRUD operations on entities. -

- - diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/GrantedPermission.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/GrantedPermission.java new file mode 100644 index 0000000000..6d4ca397cc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/GrantedPermission.java @@ -0,0 +1,53 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +/** + * Describes a Hibernate (persistence) permission. + * + * @author Steve Ebersole + */ +public class GrantedPermission { + private final String role; + private final String entityName; + private final PermissibleAction action; + + public GrantedPermission(String role, String entityName, String action) { + this.role = role; + this.entityName = entityName; + this.action = PermissibleAction.interpret( action ); + } + + public String getRole() { + return role; + } + + public String getEntityName() { + return entityName; + } + + public PermissibleAction getPermissibleAction() { + return action; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/IntegrationException.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/IntegrationException.java new file mode 100644 index 0000000000..78762bc960 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/IntegrationException.java @@ -0,0 +1,39 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +import org.hibernate.HibernateException; + +/** + * @author Steve Ebersole + */ +public class IntegrationException extends HibernateException { + public IntegrationException(String message) { + super( message ); + } + + public IntegrationException(String message, Throwable root) { + super( message, root ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccIntegrator.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccIntegrator.java new file mode 100644 index 0000000000..44095920d4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccIntegrator.java @@ -0,0 +1,132 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +import java.util.Map; + +import org.jboss.logging.Logger; + +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.event.service.spi.DuplicationStrategy; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventType; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.integrator.spi.ServiceContributingIntegrator; +import org.hibernate.metamodel.source.MetadataImplementor; +import org.hibernate.secure.internal.DisabledJaccServiceImpl; +import org.hibernate.secure.internal.JaccPreDeleteEventListener; +import org.hibernate.secure.internal.JaccPreInsertEventListener; +import org.hibernate.secure.internal.JaccPreLoadEventListener; +import org.hibernate.secure.internal.JaccPreUpdateEventListener; +import org.hibernate.secure.internal.JaccSecurityListener; +import org.hibernate.secure.internal.StandardJaccServiceImpl; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +/** + * Integrator for setting up JACC integration + * + * @author Steve Ebersole + */ +public class JaccIntegrator implements ServiceContributingIntegrator { + private static final Logger log = Logger.getLogger( JaccIntegrator.class ); + + private static final DuplicationStrategy DUPLICATION_STRATEGY = new DuplicationStrategy() { + @Override + public boolean areMatch(Object listener, Object original) { + return listener.getClass().equals( original.getClass() ) && + JaccSecurityListener.class.isInstance( original ); + } + + @Override + public Action getAction() { + return Action.KEEP_ORIGINAL; + } + }; + + @Override + public void prepareServices(StandardServiceRegistryBuilder serviceRegistryBuilder) { + boolean isSecurityEnabled = serviceRegistryBuilder.getSettings().containsKey( AvailableSettings.JACC_ENABLED ); + final JaccService jaccService = isSecurityEnabled ? new StandardJaccServiceImpl() : new DisabledJaccServiceImpl(); + serviceRegistryBuilder.addService( JaccService.class, jaccService ); + } + + @Override + public void integrate( + Configuration configuration, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + doIntegration( configuration.getProperties(), configuration.getJaccPermissionDeclarations(), serviceRegistry ); + } + + private void doIntegration( + Map properties, + JaccPermissionDeclarations permissionDeclarations, + SessionFactoryServiceRegistry serviceRegistry) { + boolean isSecurityEnabled = properties.containsKey( AvailableSettings.JACC_ENABLED ); + if ( ! isSecurityEnabled ) { + log.debug( "Skipping JACC integration as it was not enabled" ); + return; + } + + final String contextId = (String) properties.get( AvailableSettings.JACC_CONTEXT_ID ); + if ( contextId == null ) { + throw new IntegrationException( "JACC context id must be specified" ); + } + + final JaccService jaccService = serviceRegistry.getService( JaccService.class ); + if ( jaccService == null ) { + throw new IntegrationException( "JaccService was not set up" ); + } + + if ( permissionDeclarations != null ) { + for ( GrantedPermission declaration : permissionDeclarations.getPermissionDeclarations() ) { + jaccService.addPermission( declaration ); + } + } + + final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); + eventListenerRegistry.addDuplicationStrategy( DUPLICATION_STRATEGY ); + + eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JaccPreDeleteEventListener() ); + eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JaccPreInsertEventListener() ); + eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JaccPreUpdateEventListener() ); + eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JaccPreLoadEventListener() ); + } + + @Override + public void integrate( + MetadataImplementor metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry) { + doIntegration( sessionFactory.getProperties(), null, serviceRegistry ); + } + + @Override + public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + // nothing to do + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccPermissionDeclarations.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccPermissionDeclarations.java new file mode 100644 index 0000000000..571cba36a3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccPermissionDeclarations.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Steve Ebersole + */ +public class JaccPermissionDeclarations { + private final String contextId; + private List permissionDeclarations; + + public JaccPermissionDeclarations(String contextId) { + this.contextId = contextId; + } + + public void addPermissionDeclaration(GrantedPermission permissionDeclaration) { + if ( permissionDeclarations == null ) { + permissionDeclarations = new ArrayList(); + } + permissionDeclarations.add( permissionDeclaration ); + } + + public Iterable getPermissionDeclarations() { + return permissionDeclarations; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccService.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccService.java new file mode 100644 index 0000000000..ffd15dbd01 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/JaccService.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +import org.hibernate.service.Service; + +/** + * Service describing Hibernate integration with JACC for security service. + * + * @author Steve Ebersole + */ +public interface JaccService extends Service { + public void addPermission(GrantedPermission permissionDeclaration); + public void checkPermission(PermissionCheckEntityInformation entityInformation, PermissibleAction action); +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissibleAction.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissibleAction.java new file mode 100644 index 0000000000..473ed503b5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissibleAction.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +/** + * @author Steve Ebersole + */ +public enum PermissibleAction { + INSERT( "insert" ), + UPDATE( "update" ), + DELETE( "delete" ), + READ( "read" ), + ANY( "*" ) { + @Override + public String[] getImpliedActions() { + return new String[] { INSERT.externalName, UPDATE.externalName, DELETE.externalName, READ.externalName }; + } + }; + + private final String externalName; + private final String[] impliedActions; + + private PermissibleAction(String externalName) { + this.externalName = externalName; + this.impliedActions = buildImpliedActions( externalName ); + } + + private String[] buildImpliedActions(String externalName) { + return new String[] { externalName }; + } + + public String getExternalName() { + return externalName; + } + + public String[] getImpliedActions() { + return impliedActions; + } + + public static PermissibleAction interpret(String action) { + if ( INSERT.externalName.equals( action ) ) { + return INSERT; + } + else if ( UPDATE.externalName.equals( action ) ) { + return UPDATE; + } + else if ( DELETE.externalName.equals( action ) ) { + return DELETE; + } + else if ( READ.externalName.equals( action ) ) { + return READ; + } + else if ( ANY.externalName.equals( action ) ) { + return ANY; + } + + throw new IllegalArgumentException( "Unrecognized action : " + action ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissionCheckEntityInformation.java b/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissionCheckEntityInformation.java new file mode 100644 index 0000000000..e1b544b65e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/secure/spi/PermissionCheckEntityInformation.java @@ -0,0 +1,35 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.secure.spi; + +import java.io.Serializable; + +/** + * @author Steve Ebersole + */ +public interface PermissionCheckEntityInformation { + public Object getEntity(); + public String getEntityName(); + public Serializable getIdentifier(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java index a291438e39..66051b1ea3 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java @@ -509,9 +509,9 @@ public interface AvailableSettings { public static final String XML_FILE_NAMES = "hibernate.ejb.xml_files"; public static final String HBXML_FILES = "hibernate.hbmxml.files"; public static final String LOADED_CLASSES = "hibernate.ejb.loaded.classes"; - public static final String JACC_CONTEXT_ID = "hibernate.jacc.ctx.id"; - public static final String JACC_PREFIX = "hibernate.jacc"; - public static final String JACC_ENABLED = "hibernate.jacc.enabled"; + public static final String JACC_CONTEXT_ID = org.hibernate.cfg.AvailableSettings.JACC_CONTEXT_ID; + public static final String JACC_PREFIX = org.hibernate.cfg.AvailableSettings.JACC_PREFIX; + public static final String JACC_ENABLED = org.hibernate.cfg.AvailableSettings.JACC_ENABLED; public static final String PERSISTENCE_UNIT_NAME = "hibernate.ejb.persistenceUnitName"; } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index dd47f695b0..eba7b99013 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -104,7 +104,8 @@ import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JandexHelper; import org.hibernate.proxy.EntityNotFoundDelegate; -import org.hibernate.secure.internal.JACCConfiguration; +import org.hibernate.secure.spi.GrantedPermission; +import org.hibernate.secure.spi.JaccService; import org.hibernate.service.ConfigLoader; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -147,7 +148,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil private final StandardServiceRegistryBuilder serviceRegistryBuilder; private final Map configurationValues; - private final List jaccDefinitions = new ArrayList(); + private final List grantedJaccPermissions = new ArrayList(); private final List cacheRegionDefinitions = new ArrayList(); // todo : would much prefer this as a local variable... private final List cfgXmlNamedMappings = new ArrayList(); @@ -618,11 +619,9 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } if ( configurationElement.getSecurity() != null ) { - final String contextId = configurationElement.getSecurity().getContext(); for ( JaxbHibernateConfiguration.JaxbSecurity.JaxbGrant grant : configurationElement.getSecurity().getGrant() ) { - jaccDefinitions.add( - new JaccDefinition( - contextId, + grantedJaccPermissions.add( + new GrantedPermission( grant.getRole(), grant.getEntityName(), grant.getActions() @@ -651,10 +650,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil final int classStart = roleStart + role.length() + 1; final String clazz = key.substring( classStart, key.length() ); - final JaccDefinition def = new JaccDefinition( jaccContextId, role, clazz, (String) value ); - - jaccDefinitions.add( def ); - + grantedJaccPermissions.add( new GrantedPermission( role, clazz, (String) value ) ); } catch ( IndexOutOfBoundsException e ) { throw persistenceException( "Illegal usage of " + AvailableSettings.JACC_PREFIX + ": " + key ); @@ -1006,10 +1002,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil } } - if ( jaccDefinitions != null ) { - for ( JaccDefinition jaccDefinition : jaccDefinitions ) { - JACCConfiguration jaccCfg = new JACCConfiguration( jaccDefinition.contextId ); - jaccCfg.addPermission( jaccDefinition.role, jaccDefinition.clazz, jaccDefinition.actions ); + if ( grantedJaccPermissions != null ) { + final JaccService jaccService = serviceRegistry.getService( JaccService.class ); + for ( GrantedPermission grantedPermission : grantedJaccPermissions ) { + jaccService.addPermission( grantedPermission ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java index d7a397c236..20f56e0b75 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java @@ -30,7 +30,6 @@ import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingActions; @@ -65,11 +64,6 @@ import org.hibernate.jpa.event.internal.jpa.StandardListenerFactory; import org.hibernate.mapping.PersistentClass; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.source.MetadataImplementor; -import org.hibernate.secure.internal.JACCPreDeleteEventListener; -import org.hibernate.secure.internal.JACCPreInsertEventListener; -import org.hibernate.secure.internal.JACCPreLoadEventListener; -import org.hibernate.secure.internal.JACCPreUpdateEventListener; -import org.hibernate.secure.internal.JACCSecurityListener; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.SessionFactoryServiceRegistry; @@ -97,19 +91,6 @@ public class JpaIntegrator implements Integrator { } }; - private static final DuplicationStrategy JACC_DUPLICATION_STRATEGY = new DuplicationStrategy() { - @Override - public boolean areMatch(Object listener, Object original) { - return listener.getClass().equals( original.getClass() ) && - JACCSecurityListener.class.isInstance( original ); - } - - @Override - public Action getAction() { - return Action.KEEP_ORIGINAL; - } - }; - @Override @SuppressWarnings( {"unchecked"}) public void integrate( @@ -137,10 +118,7 @@ public class JpaIntegrator implements Integrator { // then prepare listeners final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); - boolean isSecurityEnabled = configuration.getProperties().containsKey( AvailableSettings.JACC_ENABLED ); - eventListenerRegistry.addDuplicationStrategy( JPA_DUPLICATION_STRATEGY ); - eventListenerRegistry.addDuplicationStrategy( JACC_DUPLICATION_STRATEGY ); // op listeners eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, JpaAutoFlushEventListener.INSTANCE ); @@ -153,15 +131,6 @@ public class JpaIntegrator implements Integrator { eventListenerRegistry.setListeners( EventType.SAVE, new JpaSaveEventListener() ); eventListenerRegistry.setListeners( EventType.SAVE_UPDATE, new JpaSaveOrUpdateEventListener() ); - // pre op listeners - if ( isSecurityEnabled ) { - final String jaccContextId = configuration.getProperty( Environment.JACC_CONTEXTID ); - eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JACCPreDeleteEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JACCPreInsertEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JACCPreUpdateEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JACCPreLoadEventListener(jaccContextId) ); - } - // post op listeners eventListenerRegistry.prependListeners( EventType.POST_DELETE, new JpaPostDeleteEventListener() ); eventListenerRegistry.prependListeners( EventType.POST_INSERT, new JpaPostInsertEventListener() ); @@ -268,10 +237,7 @@ public class JpaIntegrator implements Integrator { // then prepare listeners final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); - boolean isSecurityEnabled = sessionFactory.getProperties().containsKey( AvailableSettings.JACC_ENABLED ); - eventListenerRegistry.addDuplicationStrategy( JPA_DUPLICATION_STRATEGY ); - eventListenerRegistry.addDuplicationStrategy( JACC_DUPLICATION_STRATEGY ); // op listeners eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, JpaAutoFlushEventListener.INSTANCE ); @@ -284,15 +250,6 @@ public class JpaIntegrator implements Integrator { eventListenerRegistry.setListeners( EventType.SAVE, new JpaSaveEventListener() ); eventListenerRegistry.setListeners( EventType.SAVE_UPDATE, new JpaSaveOrUpdateEventListener() ); - // pre op listeners - if ( isSecurityEnabled ) { - final String jaccContextId = sessionFactory.getProperties().getProperty( Environment.JACC_CONTEXTID ); - eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JACCPreDeleteEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JACCPreInsertEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JACCPreUpdateEventListener(jaccContextId) ); - eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JACCPreLoadEventListener(jaccContextId) ); - } - // post op listeners eventListenerRegistry.prependListeners( EventType.POST_DELETE, new JpaPostDeleteEventListener() ); eventListenerRegistry.prependListeners( EventType.POST_INSERT, new JpaPostInsertEventListener() );