diff --git a/.github/workflows/crosschecks.yml b/.github/workflows/crosschecks.yml index d0deecd39b9..2038b8a3754 100644 --- a/.github/workflows/crosschecks.yml +++ b/.github/workflows/crosschecks.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: language: ['java'] - java: [ '25', '27-ea' ] + java: [ '25', '28-ea' ] os: [ubuntu-latest, windows-latest, macos-latest] steps: diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml index 39282e92af6..6d0b2de0e32 100644 --- a/archetype/src/main/resources/archetype-resources/core/pom.xml +++ b/archetype/src/main/resources/archetype-resources/core/pom.xml @@ -47,6 +47,10 @@ under the License. org.apache.syncope.core syncope-core-starter + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + org.apache.syncope.core.idm syncope-core-idm-rest-cxf diff --git a/archetype/src/main/resources/archetype-resources/pom.xml b/archetype/src/main/resources/archetype-resources/pom.xml index 25a71bc0e92..ae9d094b4ef 100644 --- a/archetype/src/main/resources/archetype-resources/pom.xml +++ b/archetype/src/main/resources/archetype-resources/pom.xml @@ -67,6 +67,11 @@ under the License. syncope-core-starter ${syncope.version} + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${syncope.version} + org.apache.syncope.core.idm syncope-core-idm-rest-cxf diff --git a/core/cache/caffeine/pom.xml b/core/cache/caffeine/pom.xml new file mode 100644 index 00000000000..ceeb6d677ad --- /dev/null +++ b/core/cache/caffeine/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.syncope.core + syncope-core-cache + 5.0.0-SNAPSHOT + + + Apache Syncope Core Cache Caffeine + Apache Syncope Core Cache Caffeine + org.apache.syncope.core.cache + syncope-core-cache-caffeine + jar + + + ${basedir}/../../.. + + + + + com.github.ben-manes.caffeine + jcache + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + diff --git a/core/cache/ehcache/pom.xml b/core/cache/ehcache/pom.xml new file mode 100644 index 00000000000..d2d9e3a2c04 --- /dev/null +++ b/core/cache/ehcache/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.syncope.core + syncope-core-cache + 5.0.0-SNAPSHOT + + + Apache Syncope Core Cache Ehcache + Apache Syncope Core Cache Ehcache + org.apache.syncope.core.cache + syncope-core-cache-ehcache + jar + + + ${basedir}/../../.. + + + + + org.ehcache + ehcache + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + diff --git a/core/cache/hazelcast/pom.xml b/core/cache/hazelcast/pom.xml new file mode 100644 index 00000000000..d6873a57b01 --- /dev/null +++ b/core/cache/hazelcast/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.syncope.core + syncope-core-cache + 5.0.0-SNAPSHOT + + + Apache Syncope Core Cache Hazelcast + Apache Syncope Core Cache Hazelcast + org.apache.syncope.core.cache + syncope-core-cache-hazelcast + jar + + + ${basedir}/../../.. + + + + + com.hazelcast + hazelcast + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + diff --git a/core/cache/infinispan/pom.xml b/core/cache/infinispan/pom.xml new file mode 100644 index 00000000000..c6906fea850 --- /dev/null +++ b/core/cache/infinispan/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.syncope.core + syncope-core-cache + 5.0.0-SNAPSHOT + + + Apache Syncope Core Cache Infinispan + Apache Syncope Core Cache Infinispan + org.apache.syncope.core.cache + syncope-core-cache-infinispan + jar + + + ${basedir}/../../.. + + + + + org.infinispan + infinispan-jcache + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + diff --git a/core/cache/pom.xml b/core/cache/pom.xml new file mode 100644 index 00000000000..10c81c82286 --- /dev/null +++ b/core/cache/pom.xml @@ -0,0 +1,94 @@ + + + + + 4.0.0 + + + org.apache.syncope + syncope-core + 5.0.0-SNAPSHOT + + + Apache Syncope Core Cache + Apache Syncope Core Cache + org.apache.syncope.core + syncope-core-cache + pom + + + ${basedir}/../.. + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + wrap + + zip + + false + + + / + false + false + runtime + + + + + ${rootpom.basedir}/LICENSE + META-INF + + + ${rootpom.basedir}/NOTICE + META-INF + + + + + + + + + + + + + + caffeine + hazelcast + ehcache + infinispan + + diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java index e9dbe5dac13..df1fa5e3d53 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java @@ -21,6 +21,7 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.ByteArrayInputStream; +import javax.cache.CacheManager; import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.core.persistence.api.DomainHolder; @@ -33,9 +34,12 @@ public class JPADomainRegistry implements DomainRegistry { + protected final CacheManager cacheManager; + protected final ConfigurableApplicationContext ctx; - public JPADomainRegistry(final ConfigurableApplicationContext ctx) { + public JPADomainRegistry(final CacheManager cacheManager, final ConfigurableApplicationContext ctx) { + this.cacheManager = cacheManager; this.ctx = ctx; } @@ -78,12 +82,12 @@ public void register(final JPADomain domain) { addPropertyValue("jndiName", "java:comp/env/jdbc/syncope" + domain.getKey() + "DataSource"). addPropertyValue("defaultObject", new HikariDataSource(hikariConfig)). getBeanDefinition()); - DataSource initedDataSource = beanFactory().getBean(domain.getKey() + "DataSource", DataSource.class); + DataSource initedDataSource = ctx.getBean(domain.getKey() + "DataSource", DataSource.class); domainHolder().getDomains().put(domain.getKey(), initedDataSource); // DomainRoutingEntityManagerFactory#domain - beanFactory().getBean(DomainRoutingEntityManagerFactory.class).domain(domain, initedDataSource); + ctx.getBean(DomainRoutingEntityManagerFactory.class).domain(domain, initedDataSource, cacheManager); // domainContentXML beanFactory().registerBeanDefinition(domain.getKey() + "ContentXML", @@ -109,7 +113,7 @@ public void unregister(final String domain) { beanFactory().removeBeanDefinition(domain + "ContentXML"); // DomainRoutingEntityManagerFactory#remove - beanFactory().getBean(DomainRoutingEntityManagerFactory.class).remove(domain); + ctx.getBean(DomainRoutingEntityManagerFactory.class).remove(domain); // domainDataSourceInitializer unregisterSingleton(domain.toLowerCase() + "DataSourceInitializer"); @@ -121,6 +125,6 @@ public void unregister(final String domain) { unregisterSingleton(domain + "DataSource"); beanFactory().removeBeanDefinition(domain + "DataSource"); - beanFactory().getBean(DomainHolder.class).getDomains().remove(domain); + ctx.getBean(DomainHolder.class).getDomains().remove(domain); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index 8a8f871f70c..374ad73776c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -20,10 +20,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceConfiguration; import jakarta.persistence.ValidationMode; import java.util.HashMap; import java.util.Map; -import java.util.Optional; +import javax.cache.CacheManager; import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; @@ -188,6 +189,8 @@ import org.apache.syncope.core.persistence.jpa.spring.SyncopeJPARepository; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.spring.security.SecurityProperties; +import org.hibernate.cache.jcache.ConfigSettings; +import org.hibernate.cfg.AvailableSettings; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -217,7 +220,8 @@ public class PersistenceContext { public CommonEntityManagerFactoryConf commonEMFConf( final PersistenceProperties props, @Qualifier("MasterDataSource") - final JndiObjectFactoryBean masterDataSource) { + final JndiObjectFactoryBean masterDataSource, + final CacheManager cacheManager) { CommonEntityManagerFactoryConf commonEMFConf = new CommonEntityManagerFactoryConf(); commonEMFConf.setPackagesToScan("org.apache.syncope.core.persistence.jpa.entity"); @@ -225,15 +229,14 @@ public CommonEntityManagerFactoryConf commonEMFConf( commonEMFConf.setPersistenceUnitPostProcessors(new MultiJarAwarePersistenceUnitPostProcessor()); Map jpaPropertyMap = new HashMap<>(); - jpaPropertyMap.put("jakarta.persistence.schema-generation.database.action", "create"); + jpaPropertyMap.put(PersistenceConfiguration.SCHEMAGEN_DATABASE_ACTION, "update"); + + jpaPropertyMap.put(AvailableSettings.USE_SECOND_LEVEL_CACHE, "true"); + jpaPropertyMap.put(AvailableSettings.USE_QUERY_CACHE, "true"); + jpaPropertyMap.put(AvailableSettings.CACHE_REGION_FACTORY, DomainJCacheRegionFactory.class.getName()); + jpaPropertyMap.put(ConfigSettings.CACHE_MANAGER, cacheManager); - jpaPropertyMap.put("hibernate.cache.use_second_level_cache", "true"); - jpaPropertyMap.put("hibernate.cache.use_query_cache", "true"); - jpaPropertyMap.put("hibernate.cache.region.factory_class", "jcache"); - jpaPropertyMap.put("hibernate.javax.cache.provider", props.getCacheProvider()); - Optional.ofNullable(props.getCacheURI()). - ifPresent(cacheURI -> jpaPropertyMap.put("hibernate.javax.cache.uri", cacheURI)); - jpaPropertyMap.put("hibernate.cache.region.factory_class", DomainJCacheRegionFactory.class.getName()); + jpaPropertyMap.putAll(props.getAdditionalJPAProperties()); commonEMFConf.setJpaPropertyMap(jpaPropertyMap); @@ -250,11 +253,12 @@ public DomainRoutingEntityManagerFactory entityManagerFactory( final JndiObjectFactoryBean masterDataSource, final CommonEntityManagerFactoryConf commonEMFConf, final @Lazy ConnectorManager connectorManager, - final @Lazy ExternalResourceDAO resourceDAO) { + final @Lazy ExternalResourceDAO resourceDAO, + final CacheManager cacheManager) { DomainRoutingEntityManagerFactory emf = new DomainRoutingEntityManagerFactory( commonEMFConf, connectorManager, resourceDAO); - emf.master(props, masterDataSource); + emf.master(props, masterDataSource, cacheManager); return emf; } @@ -299,8 +303,11 @@ public XMLContentExporter xmlContentExporter( @ConditionalOnMissingBean @Bean - public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { - return new JPADomainRegistry(ctx); + public DomainRegistry domainRegistry( + final CacheManager cacheManager, + final ConfigurableApplicationContext ctx) { + + return new JPADomainRegistry(cacheManager, ctx); } @ConditionalOnMissingBean diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java index adc5c4e3c31..5c20252192d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java @@ -18,6 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa; +import java.util.HashMap; +import java.util.Map; import org.apache.syncope.core.persistence.common.AbstractPersistenceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -28,27 +30,9 @@ public class PersistenceProperties extends AbstractPersistenceProperties additionalJPAProperties = new HashMap<>(); public String getViewsXML() { return viewsXML; @@ -57,4 +41,12 @@ public String getViewsXML() { public void setViewsXML(final String viewsXML) { this.viewsXML = viewsXML; } + + public Map getAdditionalJPAProperties() { + return additionalJPAProperties; + } + + public void setAdditionalJPAProperties(final Map additionalJPAProperties) { + this.additionalJPAProperties = additionalJPAProperties; + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java index 5d89e319a36..9c9a02c3723 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java @@ -60,8 +60,7 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { @JoinColumn(name = "realm_id"), uniqueConstraints = @UniqueConstraint(columnNames = { "role_id", "realm_id" })) - @Valid - private List realms = new ArrayList<>(); + private List<@Valid JPARealm> realms = new ArrayList<>(); @Override public Set getEntitlements() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java index bc931c30aa8..7814485e4ac 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java @@ -91,12 +91,10 @@ public class JPAAnyObject private List auxClasses = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd") - @Valid - private List relationships = new ArrayList<>(); + private List<@Valid JPAARelationship> relationships = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd") - @Valid - private List memberships = new ArrayList<>(); + private List<@Valid JPAAMembership> memberships = new ArrayList<>(); @Override public String getName() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java index d934f052168..99effe74f55 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java @@ -93,8 +93,7 @@ public class JPAGroup private List typeExtensions = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd") - @Valid - private List relationships = new ArrayList<>(); + private List<@Valid JPAGRelationship> relationships = new ArrayList<>(); @Override public String getName() { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java index 31ad23b78b5..e46a7d2ee6f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java @@ -65,8 +65,7 @@ public class JPAMacroTask extends AbstractSchedTask implements MacroTask { @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "macroTask") @OrderBy("idx") - @Valid - private List formPropertyDefs = new ArrayList<>(); + private List<@Valid JPAFormPropertyDef> formPropertyDefs = new ArrayList<>(); @ManyToOne(fetch = FetchType.EAGER) private JPAImplementation macroActions; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java index aa4fb93a9f1..a0a40775d7a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java @@ -158,12 +158,10 @@ public class JPAUser protected List auxClasses = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd") - @Valid - protected List relationships = new ArrayList<>(); + protected List<@Valid JPAURelationship> relationships = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "leftEnd") - @Valid - protected List memberships = new ArrayList<>(); + protected List<@Valid JPAUMembership> memberships = new ArrayList<>(); @ManyToOne(fetch = FetchType.EAGER) protected JPASecurityQuestion securityQuestion; @@ -175,8 +173,7 @@ public class JPAUser protected String mfa; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "owner") - @Valid - protected List linkedAccounts = new ArrayList<>(); + protected List<@Valid JPALinkedAccount> linkedAccounts = new ArrayList<>(); protected Encryptor encryptor() { return ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance(); @@ -231,11 +228,11 @@ protected String encode(final String value) throws Exception { value, Optional.ofNullable(cipherAlgorithm). orElseGet(() -> CipherAlgorithm.valueOf( - ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( - AuthContextUtils.getDomain(), - StandardConfParams.PASSWORD_CIPHER_ALGORITHM, - CipherAlgorithm.AES.name(), - String.class)))); + ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( + AuthContextUtils.getDomain(), + StandardConfParams.PASSWORD_CIPHER_ALGORITHM, + CipherAlgorithm.AES.name(), + String.class)))); } @Override @@ -519,7 +516,7 @@ public boolean add(final LinkedAccount account) { public Optional getLinkedAccount(final String resource, final String connObjectKeyValue) { return linkedAccounts.stream(). filter(account -> account.getResource().getKey().equals(resource) - && account.getConnObjectKeyValue().equals(connObjectKeyValue)). + && account.getConnObjectKeyValue().equals(connObjectKeyValue)). findFirst(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java index 1cd004a735a..d5fe7b8c455 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java @@ -33,7 +33,7 @@ /** * Container for common configuration options among all EntityManagerFactory entities (one for each domain). - * Acts as a commodity place for fetching each domain's {@link DataSource}.. + * Acts as a commodity place for fetching each domain's {@link DataSource}. */ public class CommonEntityManagerFactoryConf implements DomainHolder { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java index e1789bf58a1..2f6549796cf 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java @@ -18,15 +18,10 @@ */ package org.apache.syncope.core.persistence.jpa.spring; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.spi.PersistenceUnitInfo; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import javax.cache.Caching; +import javax.cache.CacheManager; import javax.cache.configuration.FactoryBuilder; import javax.cache.configuration.MutableCacheEntryListenerConfiguration; -import javax.cache.event.CacheEntryListener; import org.apache.syncope.core.persistence.jpa.ConnectorManagerCacheEntryListener; import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; @@ -43,11 +38,11 @@ public class DomainEntityManagerFactoryBean extends LocalContainerEntityManagerF private static final long serialVersionUID = 49152547930966545L; - protected final List> cacheEntryListeners = new ArrayList<>(); + protected final CacheManager cacheManager; - protected ConnectorManagerCacheEntryListener connectorManagerCacheEntryListener; - - protected ImplementationManagerCacheEntryListener implementationManagerCacheEntryListener; + public DomainEntityManagerFactoryBean(final CacheManager cacheManager) { + this.cacheManager = cacheManager; + } public void setCommonEntityManagerFactoryConf(final CommonEntityManagerFactoryConf commonEMFConf) { super.setJpaPropertyMap(commonEMFConf.getJpaPropertyMap()); @@ -62,42 +57,32 @@ public void setCommonEntityManagerFactoryConf(final CommonEntityManagerFactoryCo } public void setConnectorManagerCacheEntryListener( + final String domain, final ConnectorManagerCacheEntryListener connectorManagerCacheEntryListener) { - this.connectorManagerCacheEntryListener = connectorManagerCacheEntryListener; + Optional.ofNullable(cacheManager.getCache( + RegionNameQualifier.INSTANCE.qualify(domain, JPAConnInstance.class.getName()))). + ifPresent(cache -> cache.registerCacheEntryListener( + new MutableCacheEntryListenerConfiguration( + FactoryBuilder.factoryOf(connectorManagerCacheEntryListener), + null, false, false))); + Optional.ofNullable(cacheManager.getCache( + RegionNameQualifier.INSTANCE.qualify(domain, JPAExternalResource.class.getName()))). + ifPresent(cache -> cache.registerCacheEntryListener( + new MutableCacheEntryListenerConfiguration( + FactoryBuilder.factoryOf(connectorManagerCacheEntryListener), + null, false, false))); } public void setImplementationManagerCacheEntryListener( + final String domain, final ImplementationManagerCacheEntryListener implementationManagerCacheEntryListener) { - this.implementationManagerCacheEntryListener = implementationManagerCacheEntryListener; - } - - @Override - protected void postProcessEntityManagerFactory(final EntityManagerFactory emf, final PersistenceUnitInfo pui) { - super.postProcessEntityManagerFactory(emf, pui); - - Optional.ofNullable(Caching.getCachingProvider().getCacheManager(). - getCache(RegionNameQualifier.INSTANCE.qualify( - pui.getPersistenceUnitName(), JPAConnInstance.class.getName()))). - ifPresent(cache -> cache.registerCacheEntryListener( - new MutableCacheEntryListenerConfiguration( - FactoryBuilder.factoryOf(connectorManagerCacheEntryListener), - null, false, false))); - Optional.ofNullable(Caching.getCachingProvider().getCacheManager(). - getCache(RegionNameQualifier.INSTANCE.qualify( - pui.getPersistenceUnitName(), JPAExternalResource.class.getName()))). - ifPresent(cache -> cache.registerCacheEntryListener( - new MutableCacheEntryListenerConfiguration( - FactoryBuilder.factoryOf(connectorManagerCacheEntryListener), - null, false, false))); - - Optional.ofNullable(Caching.getCachingProvider().getCacheManager(). - getCache(RegionNameQualifier.INSTANCE.qualify( - pui.getPersistenceUnitName(), JPAImplementation.class.getName()))). + Optional.ofNullable(cacheManager.getCache( + RegionNameQualifier.INSTANCE.qualify(domain, JPAImplementation.class.getName()))). ifPresent(cache -> cache.registerCacheEntryListener( - new MutableCacheEntryListenerConfiguration( - FactoryBuilder.factoryOf(implementationManagerCacheEntryListener), - null, false, false))); + new MutableCacheEntryListenerConfiguration( + FactoryBuilder.factoryOf(implementationManagerCacheEntryListener), + null, false, false))); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java index c7481ae4086..f9c83a2a6cb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java @@ -37,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import javax.cache.CacheManager; import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.SyncopeConstants; @@ -90,23 +91,27 @@ protected void addToJpaPropertyMap( public void master( final PersistenceProperties props, - final JndiObjectFactoryBean dataSource) { + final JndiObjectFactoryBean dataSource, + final CacheManager cacheManager) { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(false); vendorAdapter.setGenerateDdl(true); vendorAdapter.setDatabasePlatform(props.getDomain().getFirst().getDatabasePlatform()); - DomainEntityManagerFactoryBean emf = new DomainEntityManagerFactoryBean(); + DomainEntityManagerFactoryBean emf = new DomainEntityManagerFactoryBean(cacheManager); emf.setPersistenceUnitName(SyncopeConstants.MASTER_DOMAIN); emf.setMappingResources(props.getDomain().getFirst().getOrm()); emf.setDataSource(Objects.requireNonNull((DataSource) dataSource.getObject())); emf.setJpaVendorAdapter(vendorAdapter); emf.setCommonEntityManagerFactoryConf(commonEMFConf); - emf.setConnectorManagerCacheEntryListener(new ConnectorManagerCacheEntryListener( - this, connectorManager, resourceDAO, SyncopeConstants.MASTER_DOMAIN)); - emf.setImplementationManagerCacheEntryListener(new ImplementationManagerCacheEntryListener( - this, SyncopeConstants.MASTER_DOMAIN)); + emf.setConnectorManagerCacheEntryListener( + SyncopeConstants.MASTER_DOMAIN, + new ConnectorManagerCacheEntryListener( + this, connectorManager, resourceDAO, SyncopeConstants.MASTER_DOMAIN)); + emf.setImplementationManagerCacheEntryListener( + SyncopeConstants.MASTER_DOMAIN, + new ImplementationManagerCacheEntryListener(this, SyncopeConstants.MASTER_DOMAIN)); addToJpaPropertyMap( emf, @@ -121,23 +126,26 @@ public void master( public void domain( final JPADomain domain, - final DataSource dataSource) { + final DataSource dataSource, + final CacheManager cacheManager) { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setShowSql(false); vendorAdapter.setGenerateDdl(true); vendorAdapter.setDatabasePlatform(domain.getDatabasePlatform()); - DomainEntityManagerFactoryBean emf = new DomainEntityManagerFactoryBean(); + DomainEntityManagerFactoryBean emf = new DomainEntityManagerFactoryBean(cacheManager); emf.setPersistenceUnitName(domain.getKey()); emf.setMappingResources(domain.getOrm()); emf.setDataSource(dataSource); emf.setJpaVendorAdapter(vendorAdapter); emf.setCommonEntityManagerFactoryConf(commonEMFConf); - emf.setConnectorManagerCacheEntryListener(new ConnectorManagerCacheEntryListener( - this, connectorManager, resourceDAO, domain.getKey())); - emf.setImplementationManagerCacheEntryListener(new ImplementationManagerCacheEntryListener( - this, domain.getKey())); + emf.setConnectorManagerCacheEntryListener( + domain.getKey(), + new ConnectorManagerCacheEntryListener(this, connectorManager, resourceDAO, domain.getKey())); + emf.setImplementationManagerCacheEntryListener( + domain.getKey(), + new ImplementationManagerCacheEntryListener(this, domain.getKey())); addToJpaPropertyMap(emf, vendorAdapter, domain.getDbSchema(), domain.getKey()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java index 89753aef2f0..0fa5bc7e139 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java @@ -20,6 +20,8 @@ import jakarta.persistence.EntityManagerFactory; import java.io.IOException; +import javax.cache.CacheManager; +import javax.cache.Caching; import javax.sql.DataSource; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; @@ -126,6 +128,11 @@ public EncryptorManager encryptorManager() throws IOException { return new DefaultEncryptorManager(new DefaultCredentialChecker("", "", "", "", false), securityProperties); } + @Bean + public CacheManager cacheManager() { + return Caching.getCachingProvider().getCacheManager(); + } + @Bean public DomainRoutingEntityManagerFactory entityManagerFactory( final PersistenceProperties props, @@ -133,7 +140,8 @@ public DomainRoutingEntityManagerFactory entityManagerFactory( final JndiObjectFactoryBean masterDataSource, final CommonEntityManagerFactoryConf commonEMFConf, final @Lazy ConnectorManager connectorManager, - final @Lazy ExternalResourceDAO resourceDAO) { + final @Lazy ExternalResourceDAO resourceDAO, + final CacheManager cacheManager) { DomainRoutingEntityManagerFactory emf = new DomainRoutingEntityManagerFactory( commonEMFConf, connectorManager, resourceDAO) { @@ -145,7 +153,7 @@ protected EntityManagerFactory delegate() { delegates.get(SyncopeConstants.MASTER_DOMAIN)); } }; - emf.master(props, masterDataSource); + emf.master(props, masterDataSource, cacheManager); return emf; } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java index 7c1174705d1..4c8318e81a5 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java @@ -74,8 +74,7 @@ public class Neo4jMacroTask extends Neo4jSchedTask implements MacroTask { commands, Neo4jMacroTaskCommandRelationship.builder()); @Relationship(type = MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = Relationship.Direction.INCOMING) - @Valid - private SortedSet formPropertyDefs = new TreeSet<>(); + private SortedSet<@Valid Neo4jFormPropertyDefRelationship> formPropertyDefs = new TreeSet<>(); @Transient private List sortedFormPropertyDefs = new SortedSetList<>( diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java index e8a006974b9..b57855ec000 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java @@ -145,8 +145,7 @@ public class Neo4jUser protected String mfa; @Relationship(direction = Relationship.Direction.INCOMING) - @Valid - protected List linkedAccounts = new ArrayList<>(); + protected List<@Valid Neo4jLinkedAccount> linkedAccounts = new ArrayList<>(); protected Encryptor encryptor() { return ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance(); @@ -206,11 +205,11 @@ protected String encode(final String value) throws Exception { value, Optional.ofNullable(cipherAlgorithm). orElseGet(() -> CipherAlgorithm.valueOf( - ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( - AuthContextUtils.getDomain(), - StandardConfParams.PASSWORD_CIPHER_ALGORITHM, - CipherAlgorithm.AES.name(), - String.class)))); + ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get( + AuthContextUtils.getDomain(), + StandardConfParams.PASSWORD_CIPHER_ALGORITHM, + CipherAlgorithm.AES.name(), + String.class)))); } @Override @@ -469,7 +468,7 @@ public boolean add(final LinkedAccount account) { public Optional getLinkedAccount(final String resource, final String connObjectKeyValue) { return linkedAccounts.stream(). filter(account -> account.getResource().getKey().equals(resource) - && account.getConnObjectKeyValue().equals(connObjectKeyValue)). + && account.getConnObjectKeyValue().equals(connObjectKeyValue)). findFirst(); } diff --git a/core/pom.xml b/core/pom.xml index af9ddb0d4c8..73bca90cabe 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -85,6 +85,7 @@ under the License. persistence-jpa persistence-neo4j spring + cache provisioning-api provisioning-java workflow-api diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java index df72ccd74d5..ae317dd8ea4 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java @@ -32,19 +32,19 @@ public class DefaultCredentialChecker { private static final Logger LOG = LoggerFactory.getLogger(DefaultCredentialChecker.class); private static final String DEFAULT_AES_KEY_ERROR_MESSAGE = - "⚠ The default AES key property is being used. " + "⚠️ WARNING: The default AES key property is being used. " + "This must be changed to avoid a security breach!"; private static final String DEFAULT_JWS_KEY_ERROR_MESSAGE = - "⚠ The default JWKS key property is being used. " + "⚠️ WARNING: The default JWKS key property is being used. " + "This must be changed to avoid a security breach!"; private static final String DEFAULT_ADMIN_PASSWORD_ERROR_MESSAGE = - "⚠ The default adminPassword property is being used. " + "⚠️ WARNING: The default adminPassword property is being used. " + "This must be changed to avoid a security breach!"; private static final String DEFAULT_ANON_KEY_ERROR_MESSAGE = - "⚠ The default anonymousKey property is being used. " + "⚠️ WARNING: The default anonymousKey property is being used. " + "This must be changed to avoid a security breach!"; private final boolean defaultAesKeyInUse; diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java index 711df6540bd..80593895e6c 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java @@ -21,7 +21,6 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.concurrent.TimeUnit; -import java.util.function.LongSupplier; import javax.cache.Cache; import org.apache.syncope.core.spring.security.SecurityProperties; @@ -29,8 +28,6 @@ abstract class AbstractThrottler { protected final SecurityProperties.ThrottleProperties throttle; - protected final LongSupplier clock = System::currentTimeMillis; - protected final Cache attempts; protected AbstractThrottler( @@ -48,6 +45,10 @@ protected boolean isEnabled() { && throttle.getLockSeconds() > 0; } + protected long now() { + return System.currentTimeMillis(); + } + protected Deque prune(final Deque attempts, final long now) { Deque failures = new ArrayDeque<>(attempts); long threshold = now - TimeUnit.SECONDS.toMillis(throttle.getWindowSeconds()); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java index f725e0afba4..0155bb66d78 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java @@ -49,25 +49,26 @@ public void checkAllowed(final String domain, final String username) { } String key = key(domain, username); - long now = clock.getAsLong(); - Long retryAfter = attempts.invoke(key, (entry, args) -> { - if (!entry.exists()) { - return null; - } - - ThrottlerAttempts state = entry.getValue(); - if (state.blockedUntil() > now) { - return retryAfterSeconds(state.blockedUntil(), now); - } + long now = now(); - Deque failures = prune(state.failures(), now); - if (failures.isEmpty()) { - entry.remove(); + Long retryAfter = null; + synchronized (attempts) { + ThrottlerAttempts state = attempts.get(key); + if (state == null) { + retryAfter = null; } else { - entry.setValue(new ThrottlerAttempts(failures, state.blockedUntil())); + if (state.blockedUntil() > now) { + retryAfter = retryAfterSeconds(state.blockedUntil(), now); + } else { + Deque failures = prune(state.failures(), now); + if (failures.isEmpty()) { + attempts.remove(key); + } else { + attempts.put(key, new ThrottlerAttempts(failures, state.blockedUntil())); + } + } } - return null; - }); + } if (retryAfter != null) { throw new AuthenticationThrottleException(retryAfter); } @@ -82,23 +83,27 @@ public void recordFailure(final String domain, final String username) { return; } - long now = clock.getAsLong(); - Long retryAfter = attempts.invoke(key(domain, username), (entry, args) -> { - ThrottlerAttempts state = entry.exists() - ? entry.getValue() - : new ThrottlerAttempts(); + String key = key(domain, username); + long now = now(); + + Long retryAfter = null; + synchronized (attempts) { + ThrottlerAttempts state = attempts.get(key); + if (state == null) { + state = new ThrottlerAttempts(); + } + Deque failures = prune(state.failures(), now); failures.addLast(now); if (failures.size() >= throttle.getMaxAttempts()) { long blockedUntil = now + TimeUnit.SECONDS.toMillis(throttle.getLockSeconds()); - entry.setValue(new ThrottlerAttempts(failures, blockedUntil)); - return retryAfterSeconds(blockedUntil, now); + attempts.put(key, new ThrottlerAttempts(failures, blockedUntil)); + retryAfter = retryAfterSeconds(blockedUntil, now); + } else { + attempts.put(key, new ThrottlerAttempts(failures, state.blockedUntil())); } - - entry.setValue(new ThrottlerAttempts(failures, state.blockedUntil())); - return null; - }); + } if (retryAfter != null) { throw new AuthenticationThrottleException(retryAfter); } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java index 9795cfae42d..d811b78c786 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java @@ -60,48 +60,48 @@ public void recordAndCheck(final String domain, final String username, final Str return; } - long now = clock.getAsLong(); String attemptKey = key(domain, username, clientAddress); String attemptKeyId = attemptKeyId(attemptKey); - PasswordResetThrottleException blocked = attempts.invoke( - attemptKey, - (entry, args) -> { - ThrottlerAttempts state = entry.exists() - ? entry.getValue() - : new ThrottlerAttempts(); + long now = now(); - if (state.blockedUntil() > now) { - PasswordResetThrottleException exception = blocked(state.blockedUntil(), now); - LOG.debug( - "Password reset request throttled for attempt key [{}]; retry after [{}] seconds", - attemptKeyId, - exception.getRetryAfterSeconds()); - return exception; - } + PasswordResetThrottleException blocked = null; + synchronized (attempts) { + ThrottlerAttempts state = attempts.get(attemptKey); + if (state == null) { + state = new ThrottlerAttempts(); + } - Deque failures = prune(state.failures(), now); - failures.addLast(now); - if (failures.size() > throttle.getMaxAttempts()) { - long blockedUntil = now + TimeUnit.SECONDS.toMillis(throttle.getLockSeconds()); - entry.setValue(new ThrottlerAttempts(failures, blockedUntil)); - LOG.warn( - "Password reset request throttling activated for attempt key [{}]; " - + "attempts [{}], max attempts [{}], lock seconds [{}]", - attemptKeyId, - failures.size(), - throttle.getMaxAttempts(), - throttle.getLockSeconds()); - return blocked(blockedUntil, now); - } + if (state.blockedUntil() > now) { + blocked = blocked(state.blockedUntil(), now); + LOG.debug( + "Password reset request throttled for attempt key [{}]; retry after [{}] seconds", + attemptKeyId, + blocked.getRetryAfterSeconds()); + } else { + Deque failures = prune(state.failures(), now); + failures.addLast(now); - entry.setValue(new ThrottlerAttempts(failures, state.blockedUntil())); + if (failures.size() > throttle.getMaxAttempts()) { + long blockedUntil = now + TimeUnit.SECONDS.toMillis(throttle.getLockSeconds()); + attempts.put(attemptKey, new ThrottlerAttempts(failures, blockedUntil)); + LOG.warn( + "Password reset request throttling activated for attempt key [{}]; " + + "attempts [{}], max attempts [{}], lock seconds [{}]", + attemptKeyId, + failures.size(), + throttle.getMaxAttempts(), + throttle.getLockSeconds()); + blocked = blocked(blockedUntil, now); + } else { + attempts.put(attemptKey, new ThrottlerAttempts(failures, state.blockedUntil())); LOG.trace( "Password reset request failure recorded for attempt key [{}]; attempts [{}/{}]", attemptKeyId, failures.size(), throttle.getMaxAttempts()); - return null; - }); + } + } + } if (blocked != null) { throw blocked; } diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java index 8f2a0c17959..77bb80215a3 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java @@ -32,7 +32,6 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.spring.security.SecurityProperties; import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; class AuthenticationThrottlerTest { @@ -61,8 +60,13 @@ private static AuthenticationThrottler throttler( cache.clear(); } - AuthenticationThrottler throttler = new AuthenticationThrottler(securityProperties, cache); - ReflectionTestUtils.setField(throttler, "clock", clock); + AuthenticationThrottler throttler = new AuthenticationThrottler(securityProperties, cache) { + + @Override + protected long now() { + return clock.getAsLong(); + } + }; return throttler; } diff --git a/core/starter/pom.xml b/core/starter/pom.xml index c973d6c0247..c22ba64a8d5 100644 --- a/core/starter/pom.xml +++ b/core/starter/pom.xml @@ -58,10 +58,6 @@ under the License. org.springframework.boot spring-boot-starter-cache - - com.github.ben-manes.caffeine - jcache - org.springframework.boot diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties index 27c75d568ed..55b64fe55a6 100644 --- a/core/starter/src/main/resources/core.properties +++ b/core/starter/src/main/resources/core.properties @@ -45,8 +45,6 @@ service.discovery.address=http://localhost:8080/syncope/rest/ # acceptable values: POSTGRESQL | MYSQL | MARIADB | ORACLE persistence.db-type=POSTGRESQL -persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider - persistence.domain[0].key=Master persistence.domain[0].jdbcDriver=org.postgresql.Driver persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java index 664db44d1d2..678bf5c6c01 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.mock; import java.time.OffsetDateTime; +import javax.cache.CacheManager; +import javax.cache.Caching; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; @@ -128,4 +130,9 @@ public NotificationManager notificationManager() { public ConnectorManager connectorManager() { return mock(ConnectorManager.class); } + + @Bean + public CacheManager cacheManager() { + return Caching.getCachingProvider().getCacheManager(); + } } diff --git a/docker/core/pom.xml b/docker/core/pom.xml index 8d61db9d166..77d4e3e9050 100644 --- a/docker/core/pom.xml +++ b/docker/core/pom.xml @@ -111,6 +111,40 @@ under the License. ${project.version} + + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${project.version} + wrap + zip + test + + + org.apache.syncope.core.cache + syncope-core-cache-hazelcast + ${project.version} + wrap + zip + test + + + org.apache.syncope.core.cache + syncope-core-cache-ehcache + ${project.version} + wrap + zip + test + + + org.apache.syncope.core.cache + syncope-core-cache-infinispan + ${project.version} + wrap + zip + test + + org.postgresql @@ -172,6 +206,11 @@ under the License. + + + + + diff --git a/docker/core/src/main/resources/Dockerfile b/docker/core/src/main/resources/Dockerfile index 62c065d62fe..e99b9a6e461 100644 --- a/docker/core/src/main/resources/Dockerfile +++ b/docker/core/src/main/resources/Dockerfile @@ -20,13 +20,14 @@ LABEL org.opencontainers.image.authors="dev@syncope.apache.org" RUN apk update && apk add bash -RUN mkdir /opt/syncope /opt/syncope/bin /opt/syncope/bundles /opt/syncope/conf /opt/syncope/lib /opt/syncope/log +RUN mkdir /opt/syncope /opt/syncope/bin /opt/syncope/bundles /opt/syncope/conf /opt/syncope/lib /opt/syncope/log /opt/syncope/cache COPY *.properties *.xml saml.keystore.jks /opt/syncope/conf/ COPY bundles/*.jar /opt/syncope/bundles/ COPY lib/mariadb*.jar lib/mysql*jar lib/ojdbc*jar lib/postgresql*jar /opt/syncope/lib/ +COPY lib/syncope-core-cache-*zip /opt/syncope/cache COPY lib/syncope-docker-core-*jar /opt/syncope/lib/syncope.jar ENV SPRING_PROFILES_ACTIVE=docker diff --git a/docker/core/src/main/resources/core-docker.properties b/docker/core/src/main/resources/core-docker.properties index 2749e0994c5..cbadd111aeb 100644 --- a/docker/core/src/main/resources/core-docker.properties +++ b/docker/core/src/main/resources/core-docker.properties @@ -24,8 +24,6 @@ security.productionMode=${PRODUCTION_MODE:true} security.anonymousUser=${ANONYMOUS_USER:anonymous} security.anonymousKey=${ANONYMOUS_KEY:anonymousKey} -persistence.cacheProvider=${HIBERNATE_JCACHE_PROVIDER} - spring.lifecycle.timeout-per-shutdown-phase=30s spring.threads.virtual.enabled=true diff --git a/docker/core/src/main/resources/startup.sh b/docker/core/src/main/resources/startup.sh index 3a552db7b06..09191582b60 100755 --- a/docker/core/src/main/resources/startup.sh +++ b/docker/core/src/main/resources/startup.sh @@ -17,4 +17,26 @@ # specific language governing permissions and limitations # under the License. +cd /opt/syncope/cache +if [ $JCACHE = "ehcache" ]; then + mkdir ehcache && cd ehcache && unzip ../syncope-core-cache-ehcache-wrap.zip + LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/ehcache" +elif [[ $JCACHE == hazelcast* ]]; then + mkdir hazelcast && cd hazelcast && unzip ../syncope-core-cache-hazelcast-wrap.zip + LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/hazelcast" + + PROVIDER_TYPE="member" + if [ $JCACHE = "hazelcast_client" ]; then + PROVIDER_TYPE="client" + fi + JAVA_OPTS="$JAVA_OPTS -Dhazelcast.jcache.provider.type=$PROVIDER_TYPE -Dhazelcast.logging.type=slf4j --add-modules java.se --add-exports java.base/jdk.internal.ref=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.management/sun.management=ALL-UNNAMED --add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED" +elif [ $JCACHE = "infinispan" ]; then + mkdir infinispan && cd infinispan && unzip ../syncope-core-cache-infinispan-wrap.zip + LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/infinispan" +else + mkdir caffeine && cd caffeine && unzip ../syncope-core-cache-caffeine-wrap.zip + LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/caffeine" +fi +cd - + exec java $JAVA_OPTS -jar /opt/syncope/lib/syncope.jar diff --git a/docker/src/main/resources/docker-compose/docker-compose-all.yml b/docker/src/main/resources/docker-compose/docker-compose-all.yml index 75f0feef1b6..a69b11424f9 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-all.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml @@ -48,7 +48,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} diff --git a/docker/src/main/resources/docker-compose/docker-compose-ha.yml b/docker/src/main/resources/docker-compose/docker-compose-ha.yml index 1897c0723d9..840b412d72e 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-ha.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-ha.yml @@ -35,13 +35,13 @@ services: - "18080:8080" restart: always environment: + JCACHE: hazelcast_member SPRING_PROFILES_ACTIVE: docker,postgresql,saml2 DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified DB_USER: syncope DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: tcp(Addresses=syncope1;syncope2) KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} @@ -57,13 +57,13 @@ services: - "18081:8080" restart: always environment: + JCACHE: hazelcast_member SPRING_PROFILES_ACTIVE: docker,postgresql DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified DB_USER: syncope DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: tcp(Addresses=syncope1;syncope2) KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} diff --git a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml index cc2e5df6c26..866a91ea0e3 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml @@ -43,7 +43,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} diff --git a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml index eee9de85bee..d464753b632 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml @@ -43,7 +43,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} diff --git a/docker/src/main/resources/docker-compose/docker-compose-oracle.yml b/docker/src/main/resources/docker-compose/docker-compose-oracle.yml index 00d72705bd4..4255ae77462 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-oracle.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-oracle.yml @@ -42,7 +42,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} diff --git a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml index 4fbaf7e1ad3..3329f76dc5b 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml @@ -40,7 +40,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${KEYMASTER_USERNAME} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD} diff --git a/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml b/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml index 74efb05717f..ebde7fd2fdd 100644 --- a/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml +++ b/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml @@ -25,7 +25,7 @@ data: DB_PASSWORD: {{.Values.syncopeEnvironment.userCreds}} DB_POOL_MAX: "{{.Values.syncopeEnvironment.dbPoolMax}}" DB_POOL_MIN: "{{.Values.syncopeEnvironment.dbPoolMin}}" - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine --- apiVersion: extensions/v1beta1 kind: Deployment diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 3650a0e83ae..6525cf5be89 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -64,6 +64,12 @@ under the License. ${project.version} + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${project.version} + + org.apache.syncope.core syncope-core-self-keymaster-starter @@ -1223,10 +1229,6 @@ under the License. payara-it - - embedded,payara - - org.apache.syncope.core @@ -1241,13 +1243,16 @@ under the License. org.apache.tomcat.embed tomcat-embed-el - - com.github.ben-manes.caffeine - jcache - + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${project.version} + provided + + org.glassfish jakarta.faces diff --git a/fit/core-reference/src/main/resources/core-payara.properties b/fit/core-reference/src/main/resources/core-payara.properties deleted file mode 100644 index db449da9333..00000000000 --- a/fit/core-reference/src/main/resources/core-payara.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -persistence.cacheProvider=com.hazelcast.cache.HazelcastCachingProvider diff --git a/src/main/asciidoc/getting-started/obtain.adoc b/src/main/asciidoc/getting-started/obtain.adoc index 69e109a68de..8f286193538 100644 --- a/src/main/asciidoc/getting-started/obtain.adoc +++ b/src/main/asciidoc/getting-started/obtain.adoc @@ -102,10 +102,9 @@ Environment variables: * `DB_PASSWORD`: password for internal storage authentication * `DB_POOL_MAX`: internal storage connection pool: ceiling * `DB_POOL_MIN`: internal storage connection pool: floor -* `HIBERNATE_JCACHE_PROVIDER`: configure multiple instances, with high availability; valid values are the ones accepted by -Hibernate for -https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching-provider-jcache-cache-manager[JCache CacheManager^] -including the default value `com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider` (single instance) +* `JCACHE`: configure https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching-provider-jcache-cache-manager[JCache provider^] +for Hibernate, defaults to https://github.com/ben-manes/caffeine[Caffeine^] if no value is provided; allowed values: +`caffeine|ehcache|hazelcast_client|hazelcast_member|infinispan` ===== Console @@ -180,7 +179,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${ANONYMOUS_USER} KEYMASTER_PASSWORD: ${ANONYMOUS_KEY} @@ -265,7 +264,7 @@ services: DB_PASSWORD: syncope DB_POOL_MAX: 20 DB_POOL_MIN: 5 - HIBERNATE_JCACHE_PROVIDER: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider + JCACHE: caffeine KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} diff --git a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc index 328ce57ac72..65679d6c031 100644 --- a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc +++ b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc @@ -23,12 +23,12 @@ certain <>. Some attribute repositories are provided: -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-JDBC.html[Database^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-LDAP.html[LDAP^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Stub.html[Stub^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Syncope.html[Syncope^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-AzureAD.html[Azure Active Directory^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Okta.html[Okta^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-JDBC.html[Database^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-LDAP.html[LDAP^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Stub.html[Stub^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Syncope.html[Syncope^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-AzureAD.html[Azure Active Directory^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Okta.html[Okta^] [TIP] ==== @@ -51,4 +51,4 @@ class. [NOTE] Attribute Repositories are dynamically translated into -https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution.html[CAS Attribute Resolution^] configuration. +https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution.html[CAS Attribute Resolution^] configuration. diff --git a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc index 4623370f6c3..880826b3a3f 100644 --- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc +++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc @@ -24,24 +24,24 @@ technology or repository, in the context of a certain <> integrated with <>, the following information is tracked for <> and <>: -* https://apereo.github.io/cas/7.3.x/authentication/Surrogate-Authentication.html[Surrogate Authentication^] -* https://apereo.github.io/cas/7.3.x/mfa/GoogleAuthenticator-Authentication.html[Google Authenticator Authentication^] -* https://apereo.github.io/cas/7.3.x/mfa/Multifactor-TrustedDevice-Authentication.html[Multifactor Authentication Trusted Devices^] -* https://apereo.github.io/cas/7.3.x/mfa/FIDO2-WebAuthn-Authentication.html[FIDO2 WebAuthn (Passkey) Multifactor Authentication^] -* https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-Consent.html[Attribute Consent^] +* https://apereo.github.io/cas/8.0.x/authentication/Surrogate-Authentication.html[Surrogate Authentication^] +* https://apereo.github.io/cas/8.0.x/mfa/GoogleAuthenticator-Authentication.html[Google Authenticator Authentication^] +* https://apereo.github.io/cas/8.0.x/mfa/Multifactor-TrustedDevice-Authentication.html[Multifactor Authentication Trusted Devices^] +* https://apereo.github.io/cas/8.0.x/mfa/FIDO2-WebAuthn-Authentication.html[FIDO2 WebAuthn (Passkey) Multifactor Authentication^] +* https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-Consent.html[Attribute Consent^] diff --git a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc index 4a1156f0773..f96f6265043 100644 --- a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc +++ b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc @@ -34,17 +34,17 @@ When defining a client application, the following parameters shall be specified: . name - regular expression to match requests . description - optional textual description . username attribute provider, mapping to -https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-PrincipalId-Attribute.html[CAS Attribute-based Principal Id^] +https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-PrincipalId-Attribute.html[CAS Attribute-based Principal Id^] . <> . <> . <> . <> . additional properties . logout type, mapping to -https://apereo.github.io/cas/7.3.x/installation/Logout-Single-Signout.html#slo-requests[the equivalent CAS setting^] +https://apereo.github.io/cas/8.0.x/installation/Logout-Single-Signout.html#slo-requests[the equivalent CAS setting^] More parameters are required to be specified depending on the actual client application type. [NOTE] Client Applications are dynamically translated into -https://apereo.github.io/cas/7.3.x/services/Service-Management.html[CAS Services^]. +https://apereo.github.io/cas/8.0.x/services/Service-Management.html[CAS Services^]. diff --git a/src/main/asciidoc/reference-guide/concepts/domains.adoc b/src/main/asciidoc/reference-guide/concepts/domains.adoc index 3eea561038b..379cbe29641 100644 --- a/src/main/asciidoc/reference-guide/concepts/domains.adoc +++ b/src/main/asciidoc/reference-guide/concepts/domains.adoc @@ -27,7 +27,7 @@ External Resources, Policies, Tasks, etc. from different domains (e.g. tenants) By default, a single `Master` domain is defined, which also bears the configuration for additional domains. Every domain besides `Master` is mapped one-to-one with a -https://apereo.github.io/cas/7.3.x/multitenancy/Multitenancy-Overview.html[CAS tenant^] having the same identifier; this +https://apereo.github.io/cas/8.0.x/multitenancy/Multitenancy-Overview.html[CAS tenant^] having the same identifier; this allows for <> configuration to relate only to the given domain's database instance. [.text-center] diff --git a/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc b/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc index 5807ed49caa..e44ef78fc6d 100644 --- a/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc +++ b/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc @@ -23,10 +23,10 @@ according to rules defined by a single configured password management module (on Some password management modules are provided: -* https://apereo.github.io/cas/7.3.x/password_management/Password-Management-LDAP.html[LDAP^] -* https://apereo.github.io/cas/7.3.x/password_management/Password-Management-JDBC.html[JDBC^] -* https://apereo.github.io/cas/7.3.x/password_management/Password-Management-REST.html[REST^] -* https://apereo.github.io/cas/7.3.x/password_management/Password-Management-ApacheSyncope.html[Syncope^] +* https://apereo.github.io/cas/8.0.x/password_management/Password-Management-LDAP.html[LDAP^] +* https://apereo.github.io/cas/8.0.x/password_management/Password-Management-JDBC.html[JDBC^] +* https://apereo.github.io/cas/8.0.x/password_management/Password-Management-REST.html[REST^] +* https://apereo.github.io/cas/8.0.x/password_management/Password-Management-ApacheSyncope.html[Syncope^] [TIP] ==== @@ -49,4 +49,4 @@ class. [NOTE] Password Management is dynamically translated into -https://apereo.github.io/cas/7.3.x/password_management/Password-Management.html[CAS Password Management^] configuration. +https://apereo.github.io/cas/8.0.x/password_management/Password-Management.html[CAS Password Management^] configuration. diff --git a/src/main/asciidoc/reference-guide/concepts/policies.adoc b/src/main/asciidoc/reference-guide/concepts/policies.adoc index dd84c70dfbf..2d3ae151b21 100644 --- a/src/main/asciidoc/reference-guide/concepts/policies.adoc +++ b/src/main/asciidoc/reference-guide/concepts/policies.adoc @@ -329,7 +329,7 @@ endif::[] [NOTE] Access Policy instances are dynamically translated into -https://apereo.github.io/cas/7.3.x/services/Configuring-Service-Access-Strategy.html#configure-service-access-strategy[CAS Service Access Strategy^]. +https://apereo.github.io/cas/8.0.x/services/Configuring-Service-Access-Strategy.html#configure-service-access-strategy[CAS Service Access Strategy^]. [[policies-attribute-release]] ==== Attribute Release @@ -341,7 +341,7 @@ values. [NOTE] Attribute Release Policy instances are dynamically translated into -https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-Policies.html#attribute-release-policies[CAS Attribute Release Policy^]. +https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-Policies.html#attribute-release-policies[CAS Attribute Release Policy^]. [[policies-authentication]] ==== Authentication @@ -368,7 +368,7 @@ be considered successful. [NOTE] Authentication Policy instances are dynamically translated into -https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Policy.html#authentication-policy[CAS Authentication Policy^]. +https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Policy.html#authentication-policy[CAS Authentication Policy^]. [[policies-propagation]] ==== Propagation @@ -487,4 +487,4 @@ Ticket Expiration policies control the duration of various types of WA sessions. [NOTE] Ticket Expiration Policy instances are dynamically translated into -https://apereo.github.io/cas/7.3.x/ticketing/Configuring-Ticket-Expiration-Policy.html[their CAS equivalent^]. +https://apereo.github.io/cas/8.0.x/ticketing/Configuring-Ticket-Expiration-Policy.html[their CAS equivalent^]. diff --git a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc index 101ff82ae80..5000cfc488a 100644 --- a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc +++ b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc @@ -21,31 +21,86 @@ ==== Core HA When deploying multiple Syncope <> instances with a single database or database cluster, it is of -fundamental importance that the https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching[second-level cache^] +fundamental importance that the https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching[second-level cache^] is correctly configured for Hibernate ORM. Such configuration, in fact, allows the Hibernate ORM cache to remain synchronized across multiple JVMs, thus enforcing data consistency across all Syncope Core instances. -The default configuration in `core.properties` is +The supported https://github.com/jsr107/jsr107spec[JCache^] provider is configured according to the implementation found +in the classpath. -[source] -.... -persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider -.... +[WARNING] +Ensure to define only one of the following providers to avoid conflicts. -which is suited for single JVM installations; with multiple instances, more options are available depending on the -configured https://github.com/jsr107/jsr107spec[JCache^] implementation, such as, for example, -https://docs.hazelcast.com/hazelcast/5.6/jcache/overview[Hazelcast^], -https://www.ehcache.org/documentation/3.0/107.html[Ehcache], -https://infinispan.org/docs/stable/titles/hibernate/hibernate.html[Infinispan^] or -https://ignite.apache.org/[Apache Ignite^]. +[core-ha-caffeine] +===== Caffeine -Additional, implementation-specific configuration files can be set up via `persistence.cacheURI`. +By default, the following dependency is set in `core/pom.xml`. + +[source,xml,subs="verbatim,attributes"] +---- + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${syncope.version} + +---- + +[WARNING] +https://github.com/ben-manes/caffeine[Caffeine^] does support only single JVM installations. + +[core-ha-ehcache] +===== Ehcache + +Set the following dependency is in `core/pom.xml` to enable https://www.ehcache.org/documentation/3.11/107.html[Ehcache^]: + +[source,xml,subs="verbatim,attributes"] +---- + + org.apache.syncope.core.cache + syncope-core-cache-caffeine + ${syncope.version} + +---- + +[TIP] +Adding `ehcache.xml` in the classpath will allow to fine-tune Ehcache's https://www.ehcache.org/documentation/3.11/xml.html[configuration^]. + +[core-ha-hazelcast] +===== Hazelcast + +Set the following dependency is in `core/pom.xml` to enable https://docs.hazelcast.com/hazelcast/5.7/jcache/overview[Hazelcast^]: + +[source,xml,subs="verbatim,attributes"] +---- + + org.apache.syncope.core.cache + syncope-core-cache-hazelcast + ${syncope.version} + +---- + +[TIP] +Adding `hazelcast.xml` in the classpath will allow to fine-tune Hazelcast's https://docs.hazelcast.com/hazelcast/5.7/configuration/understanding-configuration[configuration^]. + +[core-ha-infinispan] +===== Infinispan + +Set the following dependency is in `core/pom.xml` to enable https://infinispan.org/docs/stable/titles/embedding/embedding.html[Infinispan^]: + +[source,xml,subs="verbatim,attributes"] +---- + + org.apache.syncope.core.cache + syncope-core-cache-infinispan + ${syncope.version} + +---- ==== WA HA When multiple Syncope <> instances are needed to support authentication and authorization requirements, -a distributed https://apereo.github.io/cas/7.3.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry[Ticket Registry^] +a distributed https://apereo.github.io/cas/8.0.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry[Ticket Registry^] is required, such has the ones based on -https://apereo.github.io/cas/7.3.x/ticketing/Hazelcast-Ticket-Registry.html[Hazelcast^] -or https://apereo.github.io/cas/7.3.x/ticketing/Ignite-Ticket-Registry.html[Apache Ignite^]. +https://apereo.github.io/cas/8.0.x/ticketing/Hazelcast-Ticket-Registry.html[Hazelcast^] +or https://apereo.github.io/cas/8.0.x/ticketing/Ignite-Ticket-Registry.html[Apache Ignite^]. diff --git a/src/main/asciidoc/reference-guide/usage/actuator.adoc b/src/main/asciidoc/reference-guide/usage/actuator.adoc index 478b348d1ef..10b8eeda8d3 100644 --- a/src/main/asciidoc/reference-guide/usage/actuator.adoc +++ b/src/main/asciidoc/reference-guide/usage/actuator.adoc @@ -40,7 +40,7 @@ The pre-defined `health` endpoint is typically used for liveness and readiness p |=== | `entityCache` -a| Allows to work with https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching-statistics[JPA cache statistics^] +a| Allows to work with https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching-statistics[JPA cache statistics^] * `GET` - shows JPA cache statistics * `POST {ENABLE,DISABLE,RESET}` - performs the requested operation onto JPA cache @@ -60,15 +60,15 @@ a| Allows to work with the various jobs defined after <> and <>. |=== | `ssoSessions` -| https://apereo.github.io/cas/7.3.x/authentication/Configuring-SSO.html#actuator-endpoints[More details^] +| https://apereo.github.io/cas/8.0.x/authentication/Configuring-SSO.html#actuator-endpoints[More details^] | `registeredServices` -| https://apereo.github.io/cas/7.3.x/services/Service-Management.html#actuator-endpoints[More details^] +| https://apereo.github.io/cas/8.0.x/services/Service-Management.html#actuator-endpoints[More details^] | `authenticationHandlers` -| https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Components.html#actuator-endpoints[More details^] +| https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Components.html#actuator-endpoints[More details^] | `authenticationPolicies` -| https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Policy.html#actuator-endpoints[More details^] +| https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Policy.html#actuator-endpoints[More details^] | `resolveAttributes` -| https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution.html#actuator-endpoints[More details^] +| https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution.html#actuator-endpoints[More details^] |=== diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc b/src/main/asciidoc/reference-guide/usage/customization.adoc index f46742a1422..1280b54186a 100644 --- a/src/main/asciidoc/reference-guide/usage/customization.adoc +++ b/src/main/asciidoc/reference-guide/usage/customization.adoc @@ -275,7 +275,7 @@ components: ===== Customize Hibernate ORM settings Hibernate ORM is at the core of the <> layer; its configuration can be tweaked under several -aspects - including https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching[caching^] for +aspects - including https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching[caching^] for example, to best suit the various environments. The main configuration classes are: diff --git a/src/main/asciidoc/reference-guide/usage/metrics.adoc b/src/main/asciidoc/reference-guide/usage/metrics.adoc index 01baccff3da..c3137f69456 100644 --- a/src/main/asciidoc/reference-guide/usage/metrics.adoc +++ b/src/main/asciidoc/reference-guide/usage/metrics.adoc @@ -52,4 +52,4 @@ This can be enabled by adding the following dependency to `wa/pom.xml`: ---- -For further options and configuration, refer to https://apereo.github.io/cas/7.3.x/monitoring/Configuring-Metrics.html[CAS documentation^]. +For further options and configuration, refer to https://apereo.github.io/cas/8.0.x/monitoring/Configuring-Metrics.html[CAS documentation^].