[java-oidfed-common] 02/02: Improved value handling for federation constraints
Codeberg
noreply at shibboleth.net
Wed Sep 30 17:33:09 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/4490987664a64d763becb4becd7b50c11a9ef7fd
commit 4490987664a64d763becb4becd7b50c11a9ef7fd
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Sep 30 18:00:17 2026 +0300
Improved value handling for federation constraints
- Null-checks before the value is passed to the constraint implementations
- Unit tests (missing for allowed_entity_types)
---
.../impl/AbstractFederationPolicyConstraint.java | 14 +-
.../impl/DefaultAllowedEntityTypesConstraint.java | 14 +-
.../impl/DefaultMaxPathLengthConstraint.java | 10 +-
.../impl/DefaultNamingConstraintsConstraint.java | 31 +++--
.../constraints/impl/AbstractConstraintTest.java | 53 +++++++
.../impl/DefaultMaxPathLengthConstraintTest.java | 79 +++++++++++
.../DefaultNamingConstraintsConstraintTest.java | 154 +++++++++++++++++++++
7 files changed, 321 insertions(+), 34 deletions(-)
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractFederationPolicyConstraint.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractFederationPolicyConstraint.java
index 20c26da..979eb9d 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractFederationPolicyConstraint.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractFederationPolicyConstraint.java
@@ -45,15 +45,15 @@ public abstract class AbstractFederationPolicyConstraint<T extends Object>
public boolean validate(@Nullable final Object constraint, @Nonnull final SubordinateStatement subordinateStatement,
@Nonnull @Live final List<EntityStatement<?>> trustChain) {
checkComponentActive();
+ if (constraint == null) {
+ log.debug("No constraint value provided: null is not allowed by syntax");
+ return false;
+ }
try {
log.trace("Attempting to parse raw constraint value: {}", constraint);
- final T constraintData = parseConstraint(constraint);
- if (constraintData != null) {
- return doValidate(constraintData, subordinateStatement, trustChain);
- } else {
- return true;
- }
+ return doValidate(parseConstraint(constraint), subordinateStatement, trustChain);
} catch (final ConstraintViolationException e) {
+ log.trace("Exception caught when validating the constraint {}", constraint, e);
return false;
}
}
@@ -65,7 +65,7 @@ public abstract class AbstractFederationPolicyConstraint<T extends Object>
* @return parsed constraint data
* @throws ConstraintViolationException if the parsing was not successful
*/
- @Nullable protected abstract T parseConstraint(@Nullable final Object constraint)
+ @Nonnull protected abstract T parseConstraint(@Nonnull final Object constraint)
throws ConstraintViolationException;
/**
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultAllowedEntityTypesConstraint.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultAllowedEntityTypesConstraint.java
index b1ddfb3..ff054af 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultAllowedEntityTypesConstraint.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultAllowedEntityTypesConstraint.java
@@ -74,15 +74,17 @@ public class DefaultAllowedEntityTypesConstraint extends AbstractFederationPolic
}
/** {@inheritDoc} */
- @Override @Nullable
- protected List<String> parseConstraint(@Nullable final Object constraint)
+ @Override @Nonnull
+ protected List<String> parseConstraint(@Nonnull final Object constraint)
throws ConstraintViolationException {
if (constraint instanceof List<?> list) {
- return list.stream().filter(String.class::isInstance).map(String::valueOf).toList();
- } else if (constraint != null) {
- throw new ConstraintViolationException("Unexpected value type for allowed_entity_types: " + constraint);
+ final List<String> result = list.stream().filter(String.class::isInstance).map(String::valueOf).toList();
+ if (result.size() == list.size()) {
+ return result;
+ }
}
- return null;
+ log.debug("Unexpected value type for allowed_entity_types: {}", constraint);
+ throw new ConstraintViolationException("Unexpected value type for allowed_entity_types: " + constraint);
}
/** {@inheritDoc} */
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraint.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraint.java
index 53600fd..0bc8fd4 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraint.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraint.java
@@ -17,7 +17,6 @@ package net.shibboleth.oidfed.metadata.constraints.impl;
import java.util.List;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import org.slf4j.Logger;
@@ -35,15 +34,14 @@ public class DefaultMaxPathLengthConstraint extends AbstractFederationPolicyCons
@Nonnull private final Logger log = LoggerFactory.getLogger(DefaultMaxPathLengthConstraint.class);
/** {@inheritDoc} */
- @Override @Nullable
- protected Integer parseConstraint(@Nullable final Object constraint)
+ @Override @Nonnull
+ protected Integer parseConstraint(@Nonnull final Object constraint)
throws ConstraintViolationException {
if (constraint instanceof Integer integer) {
return integer;
- } else if (constraint != null) {
- throw new ConstraintViolationException("Unexpected value type for max_path_length: " + constraint);
}
- return null;
+ log.debug("Unexpected value type for max_path_length: {}", constraint);
+ throw new ConstraintViolationException("Unexpected value type for max_path_length: " + constraint);
}
/** {@inheritDoc} */
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraint.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraint.java
index 167c85b..210d207 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraint.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraint.java
@@ -17,7 +17,6 @@ package net.shibboleth.oidfed.metadata.constraints.impl;
import java.net.URI;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -46,18 +45,19 @@ public class DefaultNamingConstraintsConstraint extends AbstractFederationPolicy
@Nonnull private final Logger log = LoggerFactory.getLogger(DefaultNamingConstraintsConstraint.class);
/** {@inheritDoc} */
- @Override @Nullable
- protected Map<String, List<String>> parseConstraint(@Nullable final Object constraint)
+ @Override @Nonnull
+ protected Map<String, List<String>> parseConstraint(@Nonnull final Object constraint)
throws ConstraintViolationException {
if (constraint instanceof Map<?,?> map) {
- return map.keySet().stream()
+ final Map<String, List<String>> result = map.keySet().stream()
.filter(key -> "permitted".equals(key) || "excluded".equals(key))
.map(String::valueOf)
.collect(Collectors.toMap(key -> key, key -> parseListOfStrings(key, map.get(key))));
- } else if (constraint != null) {
- throw new ConstraintViolationException("Unexpected value type for naming_constraints: " + constraint);
+ assert result != null;
+ return result;
}
- return null;
+ log.debug("Unexpected value type for naming_constraints: {}", constraint);
+ throw new ConstraintViolationException("Unexpected value type for naming_constraints: " + constraint);
}
/**
@@ -65,18 +65,19 @@ public class DefaultNamingConstraintsConstraint extends AbstractFederationPolicy
*
* @param key the key (used solely in a potential exception)
* @param raw the value to be parsed
- * @return the value as list of strings, or null if the input was null
- * @throws ConstraintViolationException if a non-null value could not be parsed
+ * @return the value as list of strings
+ * @throws ConstraintViolationException if a non-null value could not be parsed or if a value was null
*/
- @Nullable private List<String> parseListOfStrings(@Nullable final String key, @Nullable final Object raw)
+ @Nonnull private List<String> parseListOfStrings(@Nullable final String key, @Nullable final Object raw)
throws ConstraintViolationException {
if (raw instanceof List<?> list) {
- final List<String> result = list.stream().filter(Objects::nonNull).map(String::valueOf).toList();
- return result;
- } else if (raw != null) {
- throw new ConstraintViolationException("The value for " + key + " is not a list: " + raw);
+ final List<String> result = list.stream().filter(String.class::isInstance).map(String::valueOf).toList();
+ if (result.size() == list.size()) {
+ return result;
+ }
}
- return null;
+ log.debug("The value for {} is not a list of strings: {}", key, raw);
+ throw new ConstraintViolationException("The value for " + key + " is not a list of strings: " + raw);
}
/** {@inheritDoc} */
diff --git a/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractConstraintTest.java b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractConstraintTest.java
new file mode 100644
index 0000000..58e6faa
--- /dev/null
+++ b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/AbstractConstraintTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed 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.
+ */
+
+package net.shibboleth.oidfed.metadata.constraints.impl;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.shibboleth.oidfed.metadata.EntityConfiguration;
+import net.shibboleth.oidfed.metadata.EntityStatement;
+import net.shibboleth.oidfed.metadata.SubordinateStatement;
+
+/**
+ * Abstract test class constraints.
+ */
+public class AbstractConstraintTest {
+
+ protected List<EntityStatement<?>> initTrustChain(final EntityConfiguration entity,
+ final SubordinateStatement... subordinates) {
+ final EntityConfiguration anchor = mock(EntityConfiguration.class);
+ final List<EntityStatement<?>> trustChain = new ArrayList<>();
+ trustChain.add(entity);
+ List.of(subordinates).forEach(subordinate -> trustChain.add(subordinate));
+ trustChain.add(anchor);
+ return trustChain;
+ }
+
+ protected EntityConfiguration initEntityConfiguration(final String entityId) {
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ when(entity.getSubject()).thenReturn(entityId);
+ return entity;
+ }
+
+ protected SubordinateStatement initSubordinateStatement(final String entityId) {
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ when(subordinate.getSubject()).thenReturn(entityId);
+ return subordinate;
+ }
+}
diff --git a/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraintTest.java b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraintTest.java
new file mode 100644
index 0000000..150ea96
--- /dev/null
+++ b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultMaxPathLengthConstraintTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed 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.
+ */
+
+package net.shibboleth.oidfed.metadata.constraints.impl;
+
+import static org.mockito.Mockito.mock;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.oidfed.metadata.EntityConfiguration;
+import net.shibboleth.oidfed.metadata.SubordinateStatement;
+import net.shibboleth.shared.component.ComponentInitializationException;
+
+/**
+ * Unit tests for {@linkDefaultMaxPathLengthConstraint}.
+ */
+ at SuppressWarnings("null")
+public class DefaultMaxPathLengthConstraintTest extends AbstractConstraintTest {
+
+ DefaultMaxPathLengthConstraint constraint;
+
+ @BeforeMethod
+ public void init() throws ComponentInitializationException {
+ constraint = new DefaultMaxPathLengthConstraint();
+ constraint.setId("mockId");
+ constraint.initialize();
+ }
+
+ @Test
+ public void validate_nullInput_shouldReturnFalse() {
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(null, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_wrongInput_shouldReturnFalse() {
+ final var constraints = "should_not_be_string";
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_shouldReturnTrue() {
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertTrue(constraint.validate(1, subordinate, initTrustChain(entity, subordinate)));
+ Assert.assertTrue(constraint.validate(0, subordinate, initTrustChain(entity, subordinate)));
+ Assert.assertTrue(constraint.validate(2, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.federation.local"),
+ initSubordinateStatement("https://sub2.federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.other-federation.local"))));
+ }
+
+ @Test
+ public void validate_invalidInput_shouldReturnFalse() {
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(1, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.federation.local"),
+ initSubordinateStatement("https://sub2.federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.other-federation.local"))));
+ }
+
+}
diff --git a/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraintTest.java b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraintTest.java
new file mode 100644
index 0000000..4632b63
--- /dev/null
+++ b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/constraints/impl/DefaultNamingConstraintsConstraintTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed 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.
+ */
+
+package net.shibboleth.oidfed.metadata.constraints.impl;
+
+import static org.mockito.Mockito.mock;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import net.shibboleth.oidfed.metadata.EntityConfiguration;
+import net.shibboleth.oidfed.metadata.SubordinateStatement;
+import net.shibboleth.shared.component.ComponentInitializationException;
+
+/**
+ * Unit tests for {@link DefaultNamingConstraintsConstraint}.
+ */
+ at SuppressWarnings("null")
+public class DefaultNamingConstraintsConstraintTest extends AbstractConstraintTest {
+
+ DefaultNamingConstraintsConstraint constraint;
+
+ @BeforeMethod
+ public void init() throws ComponentInitializationException {
+ constraint = new DefaultNamingConstraintsConstraint();
+ constraint.setId("mockId");
+ constraint.initialize();
+ }
+
+ @Test
+ public void validate_nullInput_shouldReturnFalse() {
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(null, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_wrongInput_shouldReturnFalse() {
+ final var constraints = List.of("should", "not", "be", "list");
+ final EntityConfiguration entity = mock(EntityConfiguration.class);
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_wrongInput_nullPermitted_shouldReturnFalse() {
+ final var constraints = new HashMap<String, List<String>>();
+ constraints.put("permitted", null);
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_wrongInput_nullExcluded_shouldReturnFalse() {
+ final var constraints = new HashMap<String, List<String>>();
+ constraints.put("excluded", null);
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_leafPassingPermitted_shouldReturnTrue() {
+ final var constraints = Map.of("permitted", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertTrue(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_longerChainPassingPermitted_shouldReturnTrue() {
+ final var constraints = Map.of("permitted", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertTrue(constraint.validate(constraints, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.federation.local"),
+ initSubordinateStatement("https://sub2.federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.other-federation.local"))));
+ }
+
+ @Test
+ public void validate_validInput_notPassingPermitted_shouldReturnFalse() {
+ final var constraints = Map.of("permitted", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.other-federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_subordinateNotPassingPermitted_shouldReturnFalse() {
+ final var constraints = Map.of("permitted", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.other-federation.local"),
+ initSubordinateStatement("https://sub2.federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.other-federation.local"))));
+ }
+
+ @Test
+ public void validate_validInput_passingExcluded_shouldReturnTrue() {
+ final var constraints = Map.of("excluded", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.other-federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertTrue(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_longerChainPassingExcluded_shouldReturnTrue() {
+ final var constraints = Map.of("excluded", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.other-federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertTrue(constraint.validate(constraints, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.other-federation.local"),
+ initSubordinateStatement("https://sub2.other-federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.federation.local"))));
+ }
+
+ @Test
+ public void validate_validInput_notPassingExcluded_shouldReturnFalse() {
+ final var constraints = Map.of("excluded", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate, initTrustChain(entity, subordinate)));
+ }
+
+ @Test
+ public void validate_validInput_subordinateNotPassingExcluded_shouldReturnFalse() {
+ final var constraints = Map.of("excluded", List.of(".federation.local"));
+ final EntityConfiguration entity = initEntityConfiguration("https://entity.other-federation.local");
+ final SubordinateStatement subordinate = mock(SubordinateStatement.class);
+ Assert.assertFalse(constraint.validate(constraints, subordinate,
+ initTrustChain(entity, initSubordinateStatement("https://sub1.federation.local"),
+ initSubordinateStatement("https://sub2.other-federation.local"), subordinate,
+ initSubordinateStatement("https://nomatter.federation.local"))));
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list