[java-oidfed-common] 01/02: Add property to control maximum trust chain length to be locally resolved
Codeberg
noreply at shibboleth.net
Fri Sep 18 06:59:24 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-oidfed-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidfed-common/commit/4f81e53561f037f32dbd61c7925baa456734f838
commit 4f81e53561f037f32dbd61c7925baa456734f838
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Sep 18 09:36:44 2026 +0300
Add property to control maximum trust chain length to be locally resolved
- idp.oidfed.cache.trustChain.maximumChainLength, defaults to 5
- Cover in unit tests
---
.../META-INF/net.shibboleth.idp/postconfig.xml | 7 +++
.../DefaultTrustChainFetchingStrategy.java | 37 +++++++++++++--
.../oidfed/conf/oidfed/oidfed.properties | 6 +++
.../DefaultTrustChainFetchingStrategyTest.java | 54 ++++++++++++++++++++--
.../src/test/resources/logback-test.xml | 20 ++++++++
5 files changed, 116 insertions(+), 8 deletions(-)
diff --git a/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 5a58927..f22ca63 100644
--- a/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -405,6 +405,13 @@
</constructor-arg>
</bean>
</property>
+ <property name="criteriaToMaximumChainLengthStrategy">
+ <bean parent="shibboleth.Functions.Constant">
+ <constructor-arg>
+ <bean class="java.lang.Integer" factory-method="parseInt" c:_0="%{idp.oidfed.cache.trustChain.maximumChainLength:5}" />
+ </constructor-arg>
+ </bean>
+ </property>
</bean>
</property>
</bean>
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
index a6069b4..bf2b825 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
@@ -81,6 +81,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
/** Strategy to fetch lifetime for container with invalid contents. */
@NonnullAfterInit private Function<CriteriaSet, Duration> criteriaToInvalidContainerLifetimeStrategy;
+ /** Strategy to fetch maximum length for trust chains. */
+ @NonnullAfterInit private Function<CriteriaSet, Integer> criteriaToMaximumChainLengthStrategy;
+
/**
* Set the strategy for fetching entity ID from the criteria set.
*
@@ -142,11 +145,21 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
public void setCriteriaToInvalidContainerLifetimeStrategy(
@Nonnull final Function<CriteriaSet, Duration> strategy) {
checkSetterPreconditions();
-
criteriaToInvalidContainerLifetimeStrategy =
Constraint.isNotNull(strategy, "Criteria to invalid container lifetime strategy cannot be null");
}
+ /**
+ * Set the strategy to fetch maximum length for trust chains.
+ *
+ * @param strategy maximum length strategy
+ */
+ public void setCriteriaToMaximumChainLengthStrategy(@Nonnull final Function<CriteriaSet, Integer> strategy) {
+ checkSetterPreconditions();
+ criteriaToMaximumChainLengthStrategy =
+ Constraint.isNotNull(strategy, "Criteria to maximum trust chain length strategy cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -170,6 +183,10 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
throw new ComponentInitializationException(
"Criteria to invalid container lifetime strategy cannot be null");
}
+ if (criteriaToMaximumChainLengthStrategy == null) {
+ throw new ComponentInitializationException(
+ "Criteria to maximum trust chain length strategy cannot be null");
+ }
}
/** {@inheritDoc} */
@@ -211,7 +228,11 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
} else {
entityConfiguration = subjectStatementCriterion.getValue() instanceof EntityConfiguration ec ? ec : null;
}
-
+ final Integer maximumLength = criteriaToMaximumChainLengthStrategy.apply(criteria);
+ if (maximumLength == null || maximumLength.intValue() < 3) {
+ log.warn("Could not fetch valid maximum trust chain length, it must be greater than 2: {}", maximumLength);
+ return null;
+ }
log.trace("Entity configuration found to build the trust chains on: {}", entityConfiguration != null);
if (entityConfiguration == null) {
return null;
@@ -225,7 +246,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
return null;
}
final List<TrustChainWrapper> populatedWrappers = populateChain(
- CollectionSupport.listOf(initialWrapper), preSelectedChain);
+ CollectionSupport.listOf(initialWrapper), preSelectedChain, maximumLength.intValue());
final List<List<EntityStatement<?>>> result = populatedWrappers.stream()
.filter(wrapper -> wrapper.isComplete())
.map(wrapper -> wrapper.getTrustChain())
@@ -266,11 +287,12 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
*
* @param entities the list of trust chains being populated.
* @param preSelectedChain the pre-selected trust chain (containing entity IDs as String).
+ * @param maxChainLength the maximum length of trust chain, longer than this limit will be ignored
* @return the list of trust chains being populated
*/
@Nonnull @NonnullElements private List<TrustChainWrapper> populateChain(
@Nonnull @NonnullElements final List<TrustChainWrapper> entities,
- @Nonnull final List<String> preSelectedChain) {
+ @Nonnull final List<String> preSelectedChain, final int maxChainLength) {
final List<TrustChainWrapper> result = new ArrayList<>();
boolean hints = false;
for (final TrustChainWrapper chainWrapper : entities) {
@@ -288,6 +310,11 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
final List<String> authorityHints = entityStatement instanceof EntityConfiguration configuration ?
configuration.getParsedPayload().getAuthorityHints() : null;
if (authorityHints != null && !authorityHints.isEmpty()) {
+ if (chain.size() == maxChainLength) {
+ log.info("Maximum trust chain length limit has been met, ignoring authority hints {} of {}"
+ + ", leaf {}", authorityHints, entityStatement.getSubject(), chain.get(0).getSubject());
+ continue;
+ }
final List<Pair<EntityConfiguration, SubordinateStatement>> authorities = authorityHints.stream()
.filter(id -> verifyNoLoop(chain, id))
.filter(id -> verifyPreSelected(chain, id, preSelectedChain))
@@ -306,7 +333,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
}
}
if (hints) {
- return populateChain(result, preSelectedChain);
+ return populateChain(result, preSelectedChain, maxChainLength);
}
return result;
}
diff --git a/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties b/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
index 1ad624e..53beafa 100644
--- a/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
+++ b/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
@@ -54,6 +54,12 @@ idp.oidfed.entityConfiguration.authorityHints = https://your.authority.example.o
#idp.oidfed.cache.subordinateStatement.maxJwtLifetime = PT24H
#idp.oidfed.cache.subordinateStatement.critClaims = %{idp.oidfed.cache.default.critClaims:}
+# Maximum trust chain length to be locally resolved - must be greater than 2
+# Example lengths:
+# - only trust anchor: 3 (leaf configuration, subordinate statement about leaf, anchor configuration)
+# - trust anchor and intermediate: 4 (leaf configuration, subordinate statements about leaf and intermediate, anchor configuration)
+#idp.oidfed.cache.trustChain.maximumChainLength = 5
+
#idp.oidfed.cache.trustChain.cleanupTaskInterval = PT5M
#idp.oidfed.cache.trustChain.customFilterStrategies =
#idp.oidfed.cache.trustChain.validContainerLifetime = PT5M
diff --git a/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/test/DefaultTrustChainFetchingStrategyTest.java b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
similarity index 85%
rename from oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/test/DefaultTrustChainFetchingStrategyTest.java
rename to oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
index a10cb6b..cf57464 100644
--- a/oidfed-common-conf-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/test/DefaultTrustChainFetchingStrategyTest.java
+++ b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package net.shibboleth.oidfed.metadata.cache.trustchain.test;
+package net.shibboleth.oidfed.metadata.cache.trustchain;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -37,8 +37,6 @@ import net.shibboleth.oidfed.metadata.cache.configuration.EntityConfigurationCon
import net.shibboleth.oidfed.metadata.cache.local.LocalKeyContainer;
import net.shibboleth.oidfed.metadata.cache.subordinate.SubordinateStatementCacheIdentifier;
import net.shibboleth.oidfed.metadata.cache.subordinate.SubordinateStatementContainer;
-import net.shibboleth.oidfed.metadata.cache.trustchain.DefaultTrustChainFetchingStrategy;
-import net.shibboleth.oidfed.metadata.cache.trustchain.TrustChainsContainer;
import net.shibboleth.oidfed.metadata.payload.EntityConfigurationPayload;
import net.shibboleth.oidfed.metadata.util.EntityStatementHelper;
import net.shibboleth.shared.collection.CollectionSupport;
@@ -59,6 +57,7 @@ public class DefaultTrustChainFetchingStrategyTest {
MetadataCache<Map<String, LocalKeyContainer>> localTrustAnchorsCache;
Function<CriteriaSet, Duration> criteriaToValidContainerLifetimeStrategy;
Function<CriteriaSet, Duration> criteriaToInvalidContainerLifetimeStrategy;
+ Function<CriteriaSet, Integer> criteriaToMaximumChainLengthStrategy;
@SuppressWarnings("unchecked")
public void initMocks() {
@@ -68,6 +67,7 @@ public class DefaultTrustChainFetchingStrategyTest {
localTrustAnchorsCache = mock(MetadataCache.class);
criteriaToValidContainerLifetimeStrategy = mock(Function.class);
criteriaToInvalidContainerLifetimeStrategy = mock(Function.class);
+ criteriaToMaximumChainLengthStrategy = mock(Function.class);
}
@BeforeMethod
@@ -80,6 +80,7 @@ public class DefaultTrustChainFetchingStrategyTest {
function.setLocalTrustAnchorsCache(localTrustAnchorsCache);
function.setCriteriaToValidContainerLifetimeStrategy(criteriaToValidContainerLifetimeStrategy);
function.setCriteriaToInvalidContainerLifetimeStrategy(criteriaToInvalidContainerLifetimeStrategy);
+ function.setCriteriaToMaximumChainLengthStrategy(criteriaToMaximumChainLengthStrategy);
function.setId("mockFunction");
try {
function.initialize();
@@ -97,6 +98,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void noValidLifetimeCriteria_returnsNull() {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(null);
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
Assert.assertEquals(function.apply(new CriteriaSet()), null);
}
@@ -104,6 +106,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void noInvalidLifetimeCriteria_returnsNull() {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(null);
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
Assert.assertEquals(function.apply(new CriteriaSet()), null);
}
@@ -111,6 +114,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void noEntityConfigurationResolved_returnsNull() throws MetadataCacheException {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.emptyList());
Assert.assertEquals(function.apply(new CriteriaSet()), null);
}
@@ -119,6 +123,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void entityConfigurationException_returnsNull() throws MetadataCacheException {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
when(entityConfigurationCache.get(any())).thenThrow(MetadataCacheException.class);
Assert.assertEquals(function.apply(new CriteriaSet()), null);
}
@@ -127,6 +132,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void entityConfigurationNoHints_returnsNull() throws MetadataCacheException {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer("mockEntityId", CollectionSupport.emptyList());
when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.listOf(ecContainer));
@@ -137,6 +143,7 @@ public class DefaultTrustChainFetchingStrategyTest {
public void entityConfigurationOneUnresolvableHint_returnsEmptyContainer() throws MetadataCacheException {
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer("mockEntityId", CollectionSupport.listOf("https://federation.local/immediate"));
when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.listOf(ecContainer));
@@ -150,6 +157,7 @@ public class DefaultTrustChainFetchingStrategyTest {
final String anchor = "https://federation.local/immediate";
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
final EntityConfigurationContainer authorityContainer =
@@ -177,6 +185,7 @@ public class DefaultTrustChainFetchingStrategyTest {
final String anchor = "https://federation.local/anchor";
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(immediate, "https://federation.local/other"));
final EntityConfigurationContainer immediateContainer =
@@ -206,6 +215,7 @@ public class DefaultTrustChainFetchingStrategyTest {
final String anchor = "https://federation.local/anchor";
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(immediate));
final EntityConfigurationContainer immediateContainer =
@@ -232,6 +242,40 @@ public class DefaultTrustChainFetchingStrategyTest {
List.of(leaf, immediate, anchor));
}
+ @SuppressWarnings("unchecked")
+ @Test
+ public void entityConfigurationOneResolvableLocalAuthorityWithWorkingHint_maxlnght3_returnsContainerWithTwoChains()
+ throws MetadataCacheException {
+ final String leaf = "https://federation.local/leaf";
+ final String immediate = "https://federation.local/immediate";
+ final String anchor = "https://federation.local/anchor";
+ when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(3);
+ final EntityConfigurationContainer ecContainer =
+ ecContainer(leaf, CollectionSupport.listOf(immediate));
+ final EntityConfigurationContainer immediateContainer =
+ ecContainer(immediate, CollectionSupport.listOf(anchor));
+ final EntityConfigurationContainer authorityContainer =
+ ecContainer(anchor, CollectionSupport.emptyList());
+ when(entityConfigurationCache.get(any()))
+ .thenReturn(CollectionSupport.listOf(ecContainer), CollectionSupport.listOf(immediateContainer),
+ CollectionSupport.listOf(authorityContainer), CollectionSupport.emptyList());
+ final SubordinateStatementContainer ssContainer1 = ssContainer(leaf, immediate);
+ final SubordinateStatementContainer ssContainer2 = ssContainer(immediate, anchor);
+ when(subordinateStatementCache.get(any())).thenReturn(CollectionSupport.listOf(ssContainer1),
+ CollectionSupport.listOf(ssContainer2), CollectionSupport.emptyList());
+ when(localTrustAnchorsCache.get(any())).thenReturn(
+ CollectionSupport.listOf(CollectionSupport.singletonMap(immediate, mock(LocalKeyContainer.class))),
+ CollectionSupport.emptyList());
+ final TrustChainsContainer result = function.apply(new CriteriaSet());
+ Assert.assertNotNull(result);
+ assert result != null;
+ Assert.assertEquals(result.getTrustChains().size(), 1);
+ Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(0)),
+ List.of(leaf, immediate));
+ }
+
@SuppressWarnings("unchecked")
@Test
public void entityConfigurationOneResolvableHint_matchPreSelected_returnsContainerWithOneChain()
@@ -242,6 +286,7 @@ public class DefaultTrustChainFetchingStrategyTest {
new PreSelectedTrustChainCriterion(CollectionSupport.listOf(leaf, anchor));
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
final EntityConfigurationContainer authorityContainer =
@@ -270,6 +315,7 @@ public class DefaultTrustChainFetchingStrategyTest {
new PreSelectedTrustChainCriterion(CollectionSupport.listOf(leaf, "https://federation.local/not"));
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
final EntityConfigurationContainer authorityContainer =
@@ -291,6 +337,7 @@ public class DefaultTrustChainFetchingStrategyTest {
final String anchor2 = "https://federation.local/immediate2";
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(anchor1, anchor2));
final EntityConfigurationContainer authorityContainer1 =
@@ -322,6 +369,7 @@ public class DefaultTrustChainFetchingStrategyTest {
final String anchor2 = "https://federation.local/immediate2";
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
final EntityConfigurationContainer ecContainer =
ecContainer(leaf, CollectionSupport.listOf(anchor1));
final EntityConfigurationContainer authorityContainer1 =
diff --git a/oidfed-common-impl/src/test/resources/logback-test.xml b/oidfed-common-impl/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..83cf0c5
--- /dev/null
+++ b/oidfed-common-impl/src/test/resources/logback-test.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<configuration>
+ <logger name="PROTOCOL_MESSAGE" level="TRACE"/>
+ <logger name="net.shibboleth.oidfed" level="TRACE"/>
+ <logger name="net.shibboleth" level="DEBUG"/>
+ <logger name="org.springframework" level="DEBUG"/>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%level [%logger:%line] - %msg%n</pattern>
+ <charset>UTF-8</charset>
+ </encoder>
+ </appender>
+
+ <root level="WARN">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+</configuration>
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list