HHH-14844 - Drop JACC integration
This commit is contained in:
parent
986d65a288
commit
8a8a92ca5e
|
@ -1,296 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.userguide.events;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.Timestamp;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.EntityListeners;
|
|
||||||
import jakarta.persistence.EntityManagerFactory;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.PostLoad;
|
|
||||||
import jakarta.persistence.PrePersist;
|
|
||||||
import jakarta.persistence.PreUpdate;
|
|
||||||
import jakarta.persistence.Transient;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.boot.Metadata;
|
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
|
||||||
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.event.spi.LoadEvent;
|
|
||||||
import org.hibernate.event.spi.LoadEventListener;
|
|
||||||
import org.hibernate.integrator.spi.ServiceContributingIntegrator;
|
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
|
||||||
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.secure.spi.GrantedPermission;
|
|
||||||
import org.hibernate.secure.spi.IntegrationException;
|
|
||||||
import org.hibernate.secure.spi.JaccPermissionDeclarations;
|
|
||||||
import org.hibernate.secure.spi.JaccService;
|
|
||||||
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Vlad Mihalcea
|
|
||||||
*/
|
|
||||||
public class ListenerTest extends BaseEntityManagerFunctionalTestCase {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
|
||||||
return new Class<?>[] {
|
|
||||||
Person.class,
|
|
||||||
Customer.class
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = SecurityException.class)
|
|
||||||
public void testLoadListener() {
|
|
||||||
Serializable customerId = 1L;
|
|
||||||
|
|
||||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
|
||||||
//tag::events-interceptors-load-listener-example[]
|
|
||||||
EntityManagerFactory entityManagerFactory = entityManagerFactory();
|
|
||||||
SessionFactoryImplementor sessionFactory = entityManagerFactory.unwrap( SessionFactoryImplementor.class );
|
|
||||||
sessionFactory
|
|
||||||
.getServiceRegistry()
|
|
||||||
.getService( EventListenerRegistry.class )
|
|
||||||
.prependListeners( EventType.LOAD, new SecuredLoadEntityListener() );
|
|
||||||
|
|
||||||
Customer customer = entityManager.find( Customer.class, customerId );
|
|
||||||
//end::events-interceptors-load-listener-example[]
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testJPACallback() {
|
|
||||||
Long personId = 1L;
|
|
||||||
|
|
||||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
|
||||||
Person person = new Person();
|
|
||||||
person.id = personId;
|
|
||||||
person.name = "John Doe";
|
|
||||||
person.dateOfBirth = Timestamp.valueOf(LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ));
|
|
||||||
entityManager.persist( person );
|
|
||||||
} );
|
|
||||||
|
|
||||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
|
||||||
Person person = entityManager.find( Person.class, personId );
|
|
||||||
assertTrue(person.age > 0);
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity(name = "Customer")
|
|
||||||
public static class Customer {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
public Customer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Customer(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//tag::events-jpa-callbacks-example[]
|
|
||||||
@Entity(name = "Person")
|
|
||||||
@EntityListeners( LastUpdateListener.class )
|
|
||||||
public static class Person {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private Date dateOfBirth;
|
|
||||||
|
|
||||||
@Transient
|
|
||||||
private long age;
|
|
||||||
|
|
||||||
private Date lastUpdate;
|
|
||||||
|
|
||||||
public void setLastUpdate(Date lastUpdate) {
|
|
||||||
this.lastUpdate = lastUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the transient property at load time based on a calculation.
|
|
||||||
* Note that a native Hibernate formula mapping is better for this purpose.
|
|
||||||
*/
|
|
||||||
@PostLoad
|
|
||||||
public void calculateAge() {
|
|
||||||
age = ChronoUnit.YEARS.between( LocalDateTime.ofInstant(
|
|
||||||
Instant.ofEpochMilli( dateOfBirth.getTime()), ZoneOffset.UTC),
|
|
||||||
LocalDateTime.now()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LastUpdateListener {
|
|
||||||
|
|
||||||
@PreUpdate
|
|
||||||
@PrePersist
|
|
||||||
public void setLastUpdate( Person p ) {
|
|
||||||
p.setLastUpdate( new Date() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//end::events-jpa-callbacks-example[]
|
|
||||||
|
|
||||||
//tag::events-interceptors-example[]
|
|
||||||
public static class SecuredLoadEntityListener implements LoadEventListener {
|
|
||||||
// this is the single method defined by the LoadEventListener interface
|
|
||||||
public void onLoad(LoadEvent event, LoadType loadType)
|
|
||||||
throws HibernateException {
|
|
||||||
if ( !Principal.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
|
|
||||||
throw new SecurityException( "Unauthorized access" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//end::events-Principal-example[]
|
|
||||||
public static class Principal {
|
|
||||||
public static boolean isAuthorized(String clazz, Object id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//tag::events-declarative-security-jacc-example[]
|
|
||||||
public static 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(
|
|
||||||
Metadata metadata,
|
|
||||||
SessionFactoryImplementor sessionFactory,
|
|
||||||
SessionFactoryServiceRegistry serviceRegistry) {
|
|
||||||
doIntegration(
|
|
||||||
serviceRegistry
|
|
||||||
.getService( ConfigurationService.class ).getSettings(),
|
|
||||||
// pass no permissions here, because atm actually injecting the
|
|
||||||
// permissions into the JaccService is handled on SessionFactoryImpl via
|
|
||||||
// the org.hibernate.boot.cfgxml.spi.CfgXmlAccessService
|
|
||||||
null,
|
|
||||||
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 disintegrate(SessionFactoryImplementor sessionFactory,
|
|
||||||
SessionFactoryServiceRegistry serviceRegistry) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//end::events-declarative-security-jacc-example[]
|
|
||||||
}
|
|
|
@ -24,8 +24,6 @@ import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgEventListenerType;
|
||||||
import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgHibernateConfiguration;
|
import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgHibernateConfiguration;
|
||||||
import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgMappingReferenceType;
|
import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgMappingReferenceType;
|
||||||
import org.hibernate.event.spi.EventType;
|
import org.hibernate.event.spi.EventType;
|
||||||
import org.hibernate.secure.spi.GrantedPermission;
|
|
||||||
import org.hibernate.secure.spi.JaccPermissionDeclarations;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -42,7 +40,6 @@ public class LoadedConfig {
|
||||||
|
|
||||||
private final Map configurationValues = new ConcurrentHashMap( 16, 0.75f, 1 );
|
private final Map configurationValues = new ConcurrentHashMap( 16, 0.75f, 1 );
|
||||||
|
|
||||||
private Map<String,JaccPermissionDeclarations> jaccPermissionsByContextId;
|
|
||||||
private List<CacheRegionDefinition> cacheRegionDefinitions;
|
private List<CacheRegionDefinition> cacheRegionDefinitions;
|
||||||
private List<MappingReference> mappingReferences;
|
private List<MappingReference> mappingReferences;
|
||||||
private Map<EventType,Set<String>> eventListenerMap;
|
private Map<EventType,Set<String>> eventListenerMap;
|
||||||
|
@ -59,14 +56,6 @@ public class LoadedConfig {
|
||||||
return configurationValues;
|
return configurationValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, JaccPermissionDeclarations> getJaccPermissionsByContextId() {
|
|
||||||
return jaccPermissionsByContextId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JaccPermissionDeclarations getJaccPermissions(String jaccContextId) {
|
|
||||||
return jaccPermissionsByContextId.get( jaccContextId );
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CacheRegionDefinition> getCacheRegionDefinitions() {
|
public List<CacheRegionDefinition> getCacheRegionDefinitions() {
|
||||||
return cacheRegionDefinitions == null ? Collections.emptyList() : cacheRegionDefinitions;
|
return cacheRegionDefinitions == null ? Collections.emptyList() : cacheRegionDefinitions;
|
||||||
}
|
}
|
||||||
|
@ -102,22 +91,6 @@ public class LoadedConfig {
|
||||||
cfg.addCacheRegionDefinition( parseCacheRegionDefinition( cacheDeclaration ) );
|
cfg.addCacheRegionDefinition( parseCacheRegionDefinition( cacheDeclaration ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( jaxbCfg.getSecurity() != null ) {
|
|
||||||
for ( JaxbCfgHibernateConfiguration.JaxbCfgSecurity.JaxbCfgGrant grant : jaxbCfg.getSecurity().getGrant() ) {
|
|
||||||
final JaccPermissionDeclarations jaccPermissions = cfg.getOrCreateJaccPermissions(
|
|
||||||
jaxbCfg.getSecurity()
|
|
||||||
.getContext()
|
|
||||||
);
|
|
||||||
jaccPermissions.addPermissionDeclaration(
|
|
||||||
new GrantedPermission(
|
|
||||||
grant.getRole(),
|
|
||||||
grant.getEntityName(),
|
|
||||||
grant.getActions()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !jaxbCfg.getSessionFactory().getListener().isEmpty() ) {
|
if ( !jaxbCfg.getSessionFactory().getListener().isEmpty() ) {
|
||||||
for ( JaxbCfgEventListenerType listener : jaxbCfg.getSessionFactory().getListener() ) {
|
for ( JaxbCfgEventListenerType listener : jaxbCfg.getSessionFactory().getListener() ) {
|
||||||
final EventType eventType = EventType.resolveEventTypeByName( listener.getType().value() );
|
final EventType eventType = EventType.resolveEventTypeByName( listener.getType().value() );
|
||||||
|
@ -216,20 +189,6 @@ public class LoadedConfig {
|
||||||
listenerClasses.add( listenerClass );
|
listenerClasses.add( listenerClass );
|
||||||
}
|
}
|
||||||
|
|
||||||
public JaccPermissionDeclarations getOrCreateJaccPermissions(String contextId) {
|
|
||||||
if ( jaccPermissionsByContextId == null ) {
|
|
||||||
jaccPermissionsByContextId = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
JaccPermissionDeclarations jaccPermission = jaccPermissionsByContextId.get( contextId );
|
|
||||||
if ( jaccPermission == null ) {
|
|
||||||
jaccPermission = new JaccPermissionDeclarations( contextId );
|
|
||||||
}
|
|
||||||
jaccPermissionsByContextId.put( contextId, jaccPermission );
|
|
||||||
|
|
||||||
return jaccPermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge information from loaded a {@code cfg.xml} represented by the incoming parameter
|
* Merge information from loaded a {@code cfg.xml} represented by the incoming parameter
|
||||||
* into this LoadedConfig representation
|
* into this LoadedConfig representation
|
||||||
|
@ -254,7 +213,6 @@ public class LoadedConfig {
|
||||||
addConfigurationValues( incoming.getConfigurationValues() );
|
addConfigurationValues( incoming.getConfigurationValues() );
|
||||||
addMappingReferences( incoming.getMappingReferences() );
|
addMappingReferences( incoming.getMappingReferences() );
|
||||||
addCacheRegionDefinitions( incoming.getCacheRegionDefinitions() );
|
addCacheRegionDefinitions( incoming.getCacheRegionDefinitions() );
|
||||||
addJaccPermissions( incoming.getJaccPermissionsByContextId() );
|
|
||||||
addEventListeners( incoming.getEventListenerMap() );
|
addEventListeners( incoming.getEventListenerMap() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,26 +247,6 @@ public class LoadedConfig {
|
||||||
this.cacheRegionDefinitions.addAll( cacheRegionDefinitions );
|
this.cacheRegionDefinitions.addAll( cacheRegionDefinitions );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addJaccPermissions(Map<String, JaccPermissionDeclarations> jaccPermissionsByContextId) {
|
|
||||||
if ( jaccPermissionsByContextId == null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( this.jaccPermissionsByContextId == null ) {
|
|
||||||
this.jaccPermissionsByContextId = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( Map.Entry<String, JaccPermissionDeclarations> incomingEntry : jaccPermissionsByContextId.entrySet() ) {
|
|
||||||
JaccPermissionDeclarations permissions = jaccPermissionsByContextId.get( incomingEntry.getKey() );
|
|
||||||
if ( permissions == null ) {
|
|
||||||
permissions = new JaccPermissionDeclarations( incomingEntry.getKey() );
|
|
||||||
this.jaccPermissionsByContextId.put( incomingEntry.getKey(), permissions );
|
|
||||||
}
|
|
||||||
|
|
||||||
permissions.addPermissionDeclarations( incomingEntry.getValue().getPermissionDeclarations() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addEventListeners(Map<EventType, Set<String>> eventListenerMap) {
|
private void addEventListeners(Map<EventType, Set<String>> eventListenerMap) {
|
||||||
if ( eventListenerMap == null ) {
|
if ( eventListenerMap == null ) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1997,10 +1997,6 @@ public interface AvailableSettings {
|
||||||
*/
|
*/
|
||||||
String JTA_TRACK_BY_THREAD = "hibernate.jta.track_by_thread";
|
String JTA_TRACK_BY_THREAD = "hibernate.jta.track_by_thread";
|
||||||
|
|
||||||
String JACC_CONTEXT_ID = "hibernate.jacc_context_id";
|
|
||||||
String JACC_PREFIX = "hibernate.jacc";
|
|
||||||
String JACC_ENABLED = "hibernate.jacc.enabled";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If enabled, allows schema update and validation to support synonyms. Due
|
* If enabled, allows schema update and validation to support synonyms. Due
|
||||||
* to the possibility that this would return duplicate tables (especially in
|
* to the possibility that this would return duplicate tables (especially in
|
||||||
|
|
|
@ -17,7 +17,6 @@ import jakarta.validation.Validation;
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
import jakarta.validation.ValidatorFactory;
|
import jakarta.validation.ValidatorFactory;
|
||||||
|
|
||||||
import org.hibernate.EntityMode;
|
|
||||||
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
|
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
@ -29,6 +28,7 @@ import org.hibernate.event.spi.PreUpdateEvent;
|
||||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
|
import org.hibernate.metamodel.RepresentationMode;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
@ -78,31 +78,40 @@ public class BeanValidationEventListener
|
||||||
|
|
||||||
public boolean onPreInsert(PreInsertEvent event) {
|
public boolean onPreInsert(PreInsertEvent event) {
|
||||||
validate(
|
validate(
|
||||||
event.getEntity(), event.getPersister().getEntityMode(), event.getPersister(),
|
event.getEntity(),
|
||||||
event.getSession().getFactory(), GroupsPerOperation.Operation.INSERT
|
event.getPersister().getRepresentationStrategy().getMode(),
|
||||||
|
event.getPersister(),
|
||||||
|
event.getSession().getFactory(),
|
||||||
|
GroupsPerOperation.Operation.INSERT
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onPreUpdate(PreUpdateEvent event) {
|
public boolean onPreUpdate(PreUpdateEvent event) {
|
||||||
validate(
|
validate(
|
||||||
event.getEntity(), event.getPersister().getEntityMode(), event.getPersister(),
|
event.getEntity(),
|
||||||
event.getSession().getFactory(), GroupsPerOperation.Operation.UPDATE
|
event.getPersister().getRepresentationStrategy().getMode(),
|
||||||
|
event.getPersister(),
|
||||||
|
event.getSession().getFactory(),
|
||||||
|
GroupsPerOperation.Operation.UPDATE
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onPreDelete(PreDeleteEvent event) {
|
public boolean onPreDelete(PreDeleteEvent event) {
|
||||||
validate(
|
validate(
|
||||||
event.getEntity(), event.getPersister().getEntityMode(), event.getPersister(),
|
event.getEntity(),
|
||||||
event.getSession().getFactory(), GroupsPerOperation.Operation.DELETE
|
event.getPersister().getRepresentationStrategy().getMode(),
|
||||||
|
event.getPersister(),
|
||||||
|
event.getSession().getFactory(),
|
||||||
|
GroupsPerOperation.Operation.DELETE
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void validate(T object, EntityMode mode, EntityPersister persister,
|
private <T> void validate(T object, RepresentationMode mode, EntityPersister persister,
|
||||||
SessionFactoryImplementor sessionFactory, GroupsPerOperation.Operation operation) {
|
SessionFactoryImplementor sessionFactory, GroupsPerOperation.Operation operation) {
|
||||||
if ( object == null || mode != EntityMode.POJO ) {
|
if ( object == null || mode != RepresentationMode.POJO ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TraversableResolver tr = new HibernateTraversableResolver(
|
TraversableResolver tr = new HibernateTraversableResolver(
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
package org.hibernate.event.spi;
|
package org.hibernate.event.spi;
|
||||||
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.secure.spi.PermissionCheckEntityInformation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an operation we are about to perform against the database.
|
* Represents an operation we are about to perform against the database.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPreDatabaseOperationEvent
|
public abstract class AbstractPreDatabaseOperationEvent extends AbstractEvent {
|
||||||
extends AbstractEvent
|
|
||||||
implements PermissionCheckEntityInformation {
|
|
||||||
|
|
||||||
private final Object entity;
|
private final Object entity;
|
||||||
private final Object id;
|
private final Object id;
|
||||||
|
@ -46,7 +43,6 @@ public abstract class AbstractPreDatabaseOperationEvent
|
||||||
*
|
*
|
||||||
* @return The entity.
|
* @return The entity.
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public Object getEntity() {
|
public Object getEntity() {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +57,7 @@ public abstract class AbstractPreDatabaseOperationEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The persister for the {@link #getEntity entity}.
|
* The persister for the entity.
|
||||||
*
|
*
|
||||||
* @return The entity persister.
|
* @return The entity persister.
|
||||||
*/
|
*/
|
||||||
|
@ -85,14 +81,4 @@ public abstract class AbstractPreDatabaseOperationEvent
|
||||||
public EventSource getSource() {
|
public EventSource getSource() {
|
||||||
return getSession();
|
return getSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEntityName() {
|
|
||||||
return persister.getEntityName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getIdentifier() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
package org.hibernate.event.spi;
|
package org.hibernate.event.spi;
|
||||||
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.secure.spi.PermissionCheckEntityInformation;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,9 +16,7 @@ import org.hibernate.secure.spi.PermissionCheckEntityInformation;
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class PreDeleteEvent
|
public class PreDeleteEvent extends AbstractPreDatabaseOperationEvent {
|
||||||
extends AbstractPreDatabaseOperationEvent
|
|
||||||
implements PermissionCheckEntityInformation {
|
|
||||||
|
|
||||||
private Object[] deletedState;
|
private Object[] deletedState;
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
package org.hibernate.event.spi;
|
package org.hibernate.event.spi;
|
||||||
|
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
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
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public class PreLoadEvent extends AbstractEvent implements PermissionCheckEntityInformation {
|
public class PreLoadEvent extends AbstractEvent {
|
||||||
private Object entity;
|
private Object entity;
|
||||||
private Object[] state;
|
private Object[] state;
|
||||||
private Object id;
|
private Object id;
|
||||||
|
@ -31,7 +30,6 @@ public class PreLoadEvent extends AbstractEvent implements PermissionCheckEntity
|
||||||
persister = null;
|
persister = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getEntity() {
|
public Object getEntity() {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
@ -67,14 +65,4 @@ public class PreLoadEvent extends AbstractEvent implements PermissionCheckEntity
|
||||||
this.state = state;
|
this.state = state;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEntityName() {
|
|
||||||
return persister.getEntityName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getIdentifier() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.hibernate.cache.internal.CollectionCacheInvalidator;
|
||||||
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
|
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
|
||||||
import org.hibernate.integrator.spi.Integrator;
|
import org.hibernate.integrator.spi.Integrator;
|
||||||
import org.hibernate.integrator.spi.IntegratorService;
|
import org.hibernate.integrator.spi.IntegratorService;
|
||||||
import org.hibernate.secure.spi.JaccIntegrator;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -29,7 +28,6 @@ public class IntegratorServiceImpl implements IntegratorService {
|
||||||
// register standard integrators. Envers and JPA, for example, need to be handled by discovery because in
|
// register standard integrators. Envers and JPA, for example, need to be handled by discovery because in
|
||||||
// separate project/jars.
|
// separate project/jars.
|
||||||
addIntegrator( new BeanValidationIntegrator() );
|
addIntegrator( new BeanValidationIntegrator() );
|
||||||
addIntegrator( new JaccIntegrator() );
|
|
||||||
addIntegrator( new CollectionCacheInvalidator() );
|
addIntegrator( new CollectionCacheInvalidator() );
|
||||||
|
|
||||||
// register provided integrators
|
// register provided integrators
|
||||||
|
|
|
@ -47,7 +47,6 @@ import org.hibernate.SessionFactoryObserver;
|
||||||
import org.hibernate.StatelessSession;
|
import org.hibernate.StatelessSession;
|
||||||
import org.hibernate.StatelessSessionBuilder;
|
import org.hibernate.StatelessSessionBuilder;
|
||||||
import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService;
|
import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService;
|
||||||
import org.hibernate.boot.cfgxml.spi.LoadedConfig;
|
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.spi.BootstrapContext;
|
import org.hibernate.boot.spi.BootstrapContext;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
|
@ -80,16 +79,12 @@ import org.hibernate.engine.spi.SessionBuilderImplementor;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionOwner;
|
import org.hibernate.engine.spi.SessionOwner;
|
||||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||||
import org.hibernate.event.service.spi.EventListenerGroup;
|
|
||||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
|
||||||
import org.hibernate.event.spi.EventEngine;
|
import org.hibernate.event.spi.EventEngine;
|
||||||
import org.hibernate.event.spi.EventType;
|
|
||||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.id.IdentifierGenerator;
|
import org.hibernate.id.IdentifierGenerator;
|
||||||
import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
||||||
import org.hibernate.integrator.spi.Integrator;
|
import org.hibernate.integrator.spi.Integrator;
|
||||||
import org.hibernate.integrator.spi.IntegratorService;
|
import org.hibernate.integrator.spi.IntegratorService;
|
||||||
import org.hibernate.internal.util.config.ConfigurationException;
|
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||||
import org.hibernate.jpa.internal.AfterCompletionActionLegacyJpaImpl;
|
import org.hibernate.jpa.internal.AfterCompletionActionLegacyJpaImpl;
|
||||||
import org.hibernate.jpa.internal.ExceptionMapperLegacyJpaImpl;
|
import org.hibernate.jpa.internal.ExceptionMapperLegacyJpaImpl;
|
||||||
|
@ -124,9 +119,6 @@ import org.hibernate.resource.transaction.backend.jta.internal.synchronization.A
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ExceptionMapper;
|
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ExceptionMapper;
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ManagedFlushChecker;
|
import org.hibernate.resource.transaction.backend.jta.internal.synchronization.ManagedFlushChecker;
|
||||||
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
||||||
import org.hibernate.secure.spi.GrantedPermission;
|
|
||||||
import org.hibernate.secure.spi.JaccPermissionDeclarations;
|
|
||||||
import org.hibernate.secure.spi.JaccService;
|
|
||||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||||
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
|
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
|
||||||
import org.hibernate.service.spi.SessionFactoryServiceRegistryFactory;
|
import org.hibernate.service.spi.SessionFactoryServiceRegistryFactory;
|
||||||
|
@ -226,7 +218,6 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
if ( sfName == null ) {
|
if ( sfName == null ) {
|
||||||
sfName = cfgXmlAccessService.getAggregatedConfig().getSessionFactoryName();
|
sfName = cfgXmlAccessService.getAggregatedConfig().getSessionFactoryName();
|
||||||
}
|
}
|
||||||
applyCfgXmlValues( cfgXmlAccessService.getAggregatedConfig(), serviceRegistry );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.name = sfName;
|
this.name = sfName;
|
||||||
|
@ -494,34 +485,6 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCfgXmlValues(LoadedConfig aggregatedConfig, SessionFactoryServiceRegistry serviceRegistry) {
|
|
||||||
final JaccService jaccService = serviceRegistry.getService( JaccService.class );
|
|
||||||
if ( jaccService.getContextId() != null ) {
|
|
||||||
final JaccPermissionDeclarations permissions = aggregatedConfig.getJaccPermissions( jaccService.getContextId() );
|
|
||||||
if ( permissions != null ) {
|
|
||||||
for ( GrantedPermission grantedPermission : permissions.getPermissionDeclarations() ) {
|
|
||||||
jaccService.addPermission( grantedPermission );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( aggregatedConfig.getEventListenerMap() != null ) {
|
|
||||||
final ClassLoaderService cls = serviceRegistry.getService( ClassLoaderService.class );
|
|
||||||
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
|
|
||||||
for ( Map.Entry<EventType, Set<String>> entry : aggregatedConfig.getEventListenerMap().entrySet() ) {
|
|
||||||
final EventListenerGroup group = eventListenerRegistry.getEventListenerGroup( entry.getKey() );
|
|
||||||
for ( String listenerClassName : entry.getValue() ) {
|
|
||||||
try {
|
|
||||||
group.appendListener( cls.classForName( listenerClassName ).newInstance() );
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new ConfigurationException( "Unable to instantiate event listener class : " + listenerClassName, e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JdbcConnectionAccess buildLocalConnectionAccess() {
|
private JdbcConnectionAccess buildLocalConnectionAccess() {
|
||||||
if ( settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
|
if ( settings.getMultiTenancyStrategy().requiresMultiTenantConnectionProvider() ) {
|
||||||
final MultiTenantConnectionProvider mTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class );
|
final MultiTenantConnectionProvider mTenantConnectionProvider = serviceRegistry.getService( MultiTenantConnectionProvider.class );
|
||||||
|
|
|
@ -73,8 +73,6 @@ import org.hibernate.proxy.EntityNotFoundDelegate;
|
||||||
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
|
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
||||||
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
||||||
import org.hibernate.secure.spi.GrantedPermission;
|
|
||||||
import org.hibernate.secure.spi.JaccPermissionDeclarations;
|
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.service.spi.ServiceBinding;
|
import org.hibernate.service.spi.ServiceBinding;
|
||||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||||
|
@ -98,9 +96,6 @@ import static org.hibernate.cfg.AvailableSettings.CLASS_CACHE_PREFIX;
|
||||||
import static org.hibernate.cfg.AvailableSettings.COLLECTION_CACHE_PREFIX;
|
import static org.hibernate.cfg.AvailableSettings.COLLECTION_CACHE_PREFIX;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DATASOURCE;
|
import static org.hibernate.cfg.AvailableSettings.DATASOURCE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DRIVER;
|
import static org.hibernate.cfg.AvailableSettings.DRIVER;
|
||||||
import static org.hibernate.cfg.AvailableSettings.JACC_CONTEXT_ID;
|
|
||||||
import static org.hibernate.cfg.AvailableSettings.JACC_ENABLED;
|
|
||||||
import static org.hibernate.cfg.AvailableSettings.JACC_PREFIX;
|
|
||||||
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_DRIVER;
|
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_DRIVER;
|
||||||
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_PASSWORD;
|
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_PASSWORD;
|
||||||
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_URL;
|
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JDBC_URL;
|
||||||
|
@ -550,8 +545,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
|
|
||||||
normalizeSettings( persistenceUnit, integrationSettings, mergedSettings );
|
normalizeSettings( persistenceUnit, integrationSettings, mergedSettings );
|
||||||
|
|
||||||
final String jaccContextId = (String) mergedSettings.configurationValues.get( JACC_CONTEXT_ID );
|
|
||||||
|
|
||||||
// here we are going to iterate the merged config settings looking for:
|
// here we are going to iterate the merged config settings looking for:
|
||||||
// 1) additional JACC permissions
|
// 1) additional JACC permissions
|
||||||
// 2) additional cache region declarations
|
// 2) additional cache region declarations
|
||||||
|
@ -570,22 +563,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
final String keyString = (String) entry.getKey();
|
final String keyString = (String) entry.getKey();
|
||||||
final String valueString = (String) entry.getValue();
|
final String valueString = (String) entry.getValue();
|
||||||
|
|
||||||
if ( keyString.startsWith( JACC_PREFIX ) ) {
|
if ( keyString.startsWith( CLASS_CACHE_PREFIX ) ) {
|
||||||
if( !JACC_CONTEXT_ID.equals( keyString ) && !JACC_ENABLED.equals( keyString )) {
|
|
||||||
if ( jaccContextId == null ) {
|
|
||||||
LOG.debugf(
|
|
||||||
"Found JACC permission grant [%s] in properties, but no JACC context id was specified; ignoring",
|
|
||||||
keyString
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mergedSettings.getJaccPermissions( jaccContextId ).addPermissionDeclaration(
|
|
||||||
parseJaccConfigEntry( keyString, valueString )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( keyString.startsWith( CLASS_CACHE_PREFIX ) ) {
|
|
||||||
mergedSettings.addCacheRegionDefinition(
|
mergedSettings.addCacheRegionDefinition(
|
||||||
parseCacheRegionDefinitionEntry(
|
parseCacheRegionDefinitionEntry(
|
||||||
keyString.substring( CLASS_CACHE_PREFIX.length() + 1 ),
|
keyString.substring( CLASS_CACHE_PREFIX.length() + 1 ),
|
||||||
|
@ -1204,19 +1182,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
ssrBuilder.getAggregatedCfgXml().merge( loadedConfig );
|
ssrBuilder.getAggregatedCfgXml().merge( loadedConfig );
|
||||||
}
|
}
|
||||||
|
|
||||||
private GrantedPermission parseJaccConfigEntry(String keyString, String valueString) {
|
|
||||||
try {
|
|
||||||
final int roleStart = JACC_PREFIX.length() + 1;
|
|
||||||
final String role = keyString.substring( roleStart, keyString.indexOf( '.', roleStart ) );
|
|
||||||
final int classStart = roleStart + role.length() + 1;
|
|
||||||
final String clazz = keyString.substring( classStart );
|
|
||||||
return new GrantedPermission( role, clazz, valueString );
|
|
||||||
}
|
|
||||||
catch ( IndexOutOfBoundsException e ) {
|
|
||||||
throw persistenceException( "Illegal usage of " + JACC_PREFIX + ": " + keyString );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CacheRegionDefinition parseCacheRegionDefinitionEntry(String role, String value, CacheRegionDefinition.CacheRegionType cacheType) {
|
private CacheRegionDefinition parseCacheRegionDefinitionEntry(String role, String value, CacheRegionDefinition.CacheRegionType cacheType) {
|
||||||
final StringTokenizer params = new StringTokenizer( value, ";, " );
|
final StringTokenizer params = new StringTokenizer( value, ";, " );
|
||||||
if ( !params.hasMoreTokens() ) {
|
if ( !params.hasMoreTokens() ) {
|
||||||
|
@ -1591,7 +1556,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
public static class MergedSettings {
|
public static class MergedSettings {
|
||||||
private final Map configurationValues = new ConcurrentHashMap( 16, 0.75f, 1 );
|
private final Map configurationValues = new ConcurrentHashMap( 16, 0.75f, 1 );
|
||||||
|
|
||||||
private Map<String, JaccPermissionDeclarations> jaccPermissionsByContextId;
|
|
||||||
private List<CacheRegionDefinition> cacheRegionDefinitions;
|
private List<CacheRegionDefinition> cacheRegionDefinitions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1630,19 +1594,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
return configurationValues;
|
return configurationValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JaccPermissionDeclarations getJaccPermissions(String jaccContextId) {
|
|
||||||
if ( jaccPermissionsByContextId == null ) {
|
|
||||||
jaccPermissionsByContextId = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
JaccPermissionDeclarations jaccPermissions = jaccPermissionsByContextId.get( jaccContextId );
|
|
||||||
if ( jaccPermissions == null ) {
|
|
||||||
jaccPermissions = new JaccPermissionDeclarations( jaccContextId );
|
|
||||||
jaccPermissionsByContextId.put( jaccContextId, jaccPermissions );
|
|
||||||
}
|
|
||||||
return jaccPermissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCacheRegionDefinition(CacheRegionDefinition cacheRegionDefinition) {
|
private void addCacheRegionDefinition(CacheRegionDefinition cacheRegionDefinition) {
|
||||||
if ( this.cacheRegionDefinitions == null ) {
|
if ( this.cacheRegionDefinitions == null ) {
|
||||||
this.cacheRegionDefinitions = new ArrayList<>();
|
this.cacheRegionDefinitions = new ArrayList<>();
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
import org.hibernate.secure.spi.GrantedPermission;
|
|
||||||
import org.hibernate.secure.spi.JaccService;
|
|
||||||
import org.hibernate.secure.spi.PermissibleAction;
|
|
||||||
import org.hibernate.secure.spi.PermissionCheckEntityInformation;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class DisabledJaccServiceImpl implements JaccService {
|
|
||||||
private static final Logger log = Logger.getLogger( DisabledJaccServiceImpl.class );
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getContextId() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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" );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
import org.hibernate.event.spi.PreDeleteEvent;
|
|
||||||
import org.hibernate.event.spi.PreDeleteEventListener;
|
|
||||||
import org.hibernate.secure.spi.PermissibleAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check security before any deletion
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class JaccPreDeleteEventListener extends AbstractJaccSecurableEventListener implements PreDeleteEventListener {
|
|
||||||
public JaccPreDeleteEventListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onPreDelete(PreDeleteEvent event) {
|
|
||||||
performSecurityCheck( event, PermissibleAction.DELETE );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
import org.hibernate.event.spi.PreInsertEvent;
|
|
||||||
import org.hibernate.event.spi.PreInsertEventListener;
|
|
||||||
import org.hibernate.secure.spi.PermissibleAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check security before an insertion
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class JaccPreInsertEventListener extends AbstractJaccSecurableEventListener implements PreInsertEventListener {
|
|
||||||
public JaccPreInsertEventListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onPreInsert(PreInsertEvent event) {
|
|
||||||
performSecurityCheck( event, PermissibleAction.INSERT );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
import org.hibernate.event.spi.PreLoadEvent;
|
|
||||||
import org.hibernate.event.spi.PreLoadEventListener;
|
|
||||||
import org.hibernate.secure.spi.PermissibleAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check security before any load
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class JaccPreLoadEventListener extends AbstractJaccSecurableEventListener implements PreLoadEventListener {
|
|
||||||
public JaccPreLoadEventListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPreLoad(PreLoadEvent event) {
|
|
||||||
performSecurityCheck( event.getSession(), event, PermissibleAction.READ );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
import org.hibernate.event.spi.PreUpdateEvent;
|
|
||||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
|
||||||
import org.hibernate.secure.spi.PermissibleAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check security before any update
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class JaccPreUpdateEventListener extends AbstractJaccSecurableEventListener implements PreUpdateEventListener {
|
|
||||||
public JaccPreUpdateEventListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onPreUpdate(PreUpdateEvent event) {
|
|
||||||
performSecurityCheck( event, PermissibleAction.UPDATE );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marker interface for JACC event listeners. Used in event listener duplication strategy checks; see
|
|
||||||
* {@link org.hibernate.secure.spi.JaccIntegrator} for details.
|
|
||||||
*
|
|
||||||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
|
|
||||||
*/
|
|
||||||
public interface JaccSecurityListener {
|
|
||||||
}
|
|
|
@ -1,209 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.internal;
|
|
||||||
|
|
||||||
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 javax.security.auth.Subject;
|
|
||||||
import jakarta.security.jacc.EJBMethodPermission;
|
|
||||||
import jakarta.security.jacc.PolicyConfiguration;
|
|
||||||
import jakarta.security.jacc.PolicyConfigurationFactory;
|
|
||||||
import jakarta.security.jacc.PolicyContext;
|
|
||||||
import jakarta.security.jacc.PolicyContextException;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 String getContextId() {
|
|
||||||
return contextId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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<String> {
|
|
||||||
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<Principal> 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<Subject> privilegedAction = new PrivilegedAction<Subject>() {
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Package defining support for declarative security of CRUD operations via JACC.
|
|
||||||
*/
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.spi;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.hibernate.boot.Metadata;
|
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
|
||||||
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.ServiceContributingIntegrator;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(
|
|
||||||
Metadata metadata,
|
|
||||||
SessionFactoryImplementor sessionFactory,
|
|
||||||
SessionFactoryServiceRegistry serviceRegistry) {
|
|
||||||
doIntegration(
|
|
||||||
serviceRegistry.getService( ConfigurationService.class ).getSettings(),
|
|
||||||
// pass no permissions here, because atm actually injecting the
|
|
||||||
// permissions into the JaccService is handled on SessionFactoryImpl via
|
|
||||||
// the org.hibernate.boot.cfgxml.spi.CfgXmlAccessService
|
|
||||||
null,
|
|
||||||
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 disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.spi;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class JaccPermissionDeclarations {
|
|
||||||
private final String contextId;
|
|
||||||
private List<GrantedPermission> permissionDeclarations;
|
|
||||||
|
|
||||||
public JaccPermissionDeclarations(String contextId) {
|
|
||||||
this.contextId = contextId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContextId() {
|
|
||||||
return contextId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPermissionDeclaration(GrantedPermission permissionDeclaration) {
|
|
||||||
if ( permissionDeclarations == null ) {
|
|
||||||
permissionDeclarations = new ArrayList<>();
|
|
||||||
}
|
|
||||||
permissionDeclarations.add( permissionDeclaration );
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPermissionDeclarations(Collection<GrantedPermission> permissionDeclarations) {
|
|
||||||
if ( this.permissionDeclarations == null ) {
|
|
||||||
this.permissionDeclarations = new ArrayList<>();
|
|
||||||
}
|
|
||||||
this.permissionDeclarations.addAll( permissionDeclarations );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<GrantedPermission> getPermissionDeclarations() {
|
|
||||||
return permissionDeclarations;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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 {
|
|
||||||
/**
|
|
||||||
* Obtain the JACC context-id in effect for this service. {@code null} indicates no
|
|
||||||
* context is in effect (service is disabled).
|
|
||||||
*
|
|
||||||
* @return The effective JACC context-id
|
|
||||||
*/
|
|
||||||
public String getContextId();
|
|
||||||
|
|
||||||
public void addPermission(GrantedPermission permissionDeclaration);
|
|
||||||
public void checkPermission(PermissionCheckEntityInformation entityInformation, PermissibleAction action);
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.secure.spi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public interface PermissionCheckEntityInformation {
|
|
||||||
public Object getEntity();
|
|
||||||
public String getEntityName();
|
|
||||||
public Object getIdentifier();
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.orm.test.secure;
|
|
||||||
|
|
||||||
import java.security.CodeSource;
|
|
||||||
import java.security.Permission;
|
|
||||||
import java.security.PermissionCollection;
|
|
||||||
import java.security.Policy;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.util.Collections;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import javax.security.auth.Subject;
|
|
||||||
import jakarta.security.jacc.PolicyContext;
|
|
||||||
import jakarta.security.jacc.PolicyContextException;
|
|
||||||
import jakarta.security.jacc.PolicyContextHandler;
|
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
|
||||||
import org.hibernate.testing.orm.junit.Jpa;
|
|
||||||
import org.hibernate.testing.orm.junit.Setting;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Vlad Mihalcea
|
|
||||||
*/
|
|
||||||
@Jpa(annotatedClasses = {
|
|
||||||
JaccIntegratorTest.Person.class
|
|
||||||
}, properties = {
|
|
||||||
@Setting( name = AvailableSettings.JACC_ENABLED, value = "true"),
|
|
||||||
@Setting( name = AvailableSettings.JACC_CONTEXT_ID, value = "JACC_CONTEXT_ID"),
|
|
||||||
@Setting( name = "hibernate.jacc.allowed.org.hibernate.secure.Customer", value = "insert")
|
|
||||||
})
|
|
||||||
@TestForIssue( jiraKey = "HHH-11805" )
|
|
||||||
public class JaccIntegratorTest {
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
protected void afterEntityManagerFactoryBuilt(EntityManagerFactoryScope scope) {
|
|
||||||
scope.getEntityManagerFactory();
|
|
||||||
PolicyContextHandler policyContextHandler = new PolicyContextHandler() {
|
|
||||||
@Override
|
|
||||||
public Object getContext(String key, Object data) throws PolicyContextException {
|
|
||||||
Subject subject = new Subject( true, Collections.singleton(new java.security.Principal() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "org.hibernate.secure.JaccIntegratorTest$Person";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean implies(Subject subject) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}), Collections.emptySet(), Collections.emptySet());
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getKeys() throws PolicyContextException {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(String key) throws PolicyContextException {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
PolicyContext.registerHandler( "javax.security.auth.Subject.container", policyContextHandler, true);
|
|
||||||
}
|
|
||||||
catch (PolicyContextException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setPolicy(boolean allow) {
|
|
||||||
Policy.setPolicy( new Policy() {
|
|
||||||
@Override
|
|
||||||
public Provider getProvider() {
|
|
||||||
return super.getProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return super.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Parameters getParameters() {
|
|
||||||
return super.getParameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PermissionCollection getPermissions(CodeSource codesource) {
|
|
||||||
return super.getPermissions( codesource );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PermissionCollection getPermissions(ProtectionDomain domain) {
|
|
||||||
return super.getPermissions( domain );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean implies(ProtectionDomain domain, Permission permission) {
|
|
||||||
return allow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh() {
|
|
||||||
super.refresh();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAllow(EntityManagerFactoryScope scope) {
|
|
||||||
setPolicy( true );
|
|
||||||
|
|
||||||
scope.inTransaction( entityManager -> {
|
|
||||||
Person person = new Person();
|
|
||||||
person.id = 1L;
|
|
||||||
person.name = "John Doe";
|
|
||||||
|
|
||||||
entityManager.persist( person );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDisallow(EntityManagerFactoryScope scope) {
|
|
||||||
setPolicy( false );
|
|
||||||
|
|
||||||
try {
|
|
||||||
scope.inTransaction( entityManager -> {
|
|
||||||
Person person = new Person();
|
|
||||||
person.id = 1L;
|
|
||||||
person.name = "John Doe";
|
|
||||||
|
|
||||||
entityManager.persist( person );
|
|
||||||
} );
|
|
||||||
|
|
||||||
fail("Should have thrown SecurityException");
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
assertTrue( e.getCause() instanceof SecurityException );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity(name = "Person")
|
|
||||||
public static class Person {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -57,3 +57,8 @@ Multiple component mappings for the same java class with different property mapp
|
||||||
* Functions
|
* Functions
|
||||||
* Multi-table bulk manipulation HQL/Criteria query handling
|
* Multi-table bulk manipulation HQL/Criteria query handling
|
||||||
|
|
||||||
|
|
||||||
|
=== Removal
|
||||||
|
|
||||||
|
* JMX integration
|
||||||
|
* JACC integration
|
||||||
|
|
Loading…
Reference in New Issue