You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2026/07/10 09:53:33 UTC
(cxf) branch 4.1.x-fixes updated (426f2d1e46d -> f2ac92f01e5)
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a change to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
from 426f2d1e46d Put log messages and SOAP Fault messages to a resource bundle so that downstream intergations of CXF can hint about their own configuration parameters (#3300)
new 22995b3e5ff Fix bug with SAML clientAddress (#3298)
new 417584b2780 Plug URIResolver into EndpointReferenceUtils (#3296)
new 83adf63fa0d Disallow a dynamic registration client from requesting scopes without validation (#3297)
new f2ac92f01e5 Recording .gitmergeinfo Changes
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.gitmergeinfo | 1 +
.../cxf/ws/addressing/EndpointReferenceUtils.java | 19 ++-
.../ws/addressing/EndpointReferenceUtilsTest.java | 141 +++++++++++++++++++++
rt/rs/security/oauth-parent/oauth2-saml/pom.xml | 5 +
.../security/oauth2/saml/SamlOAuthValidator.java | 2 +-
.../oauth2/saml/SamlOAuthValidatorTest.java | 127 +++++++++++++++++++
.../services/DynamicRegistrationService.java | 30 ++++-
.../services/DynamicRegistrationServiceTest.java | 94 ++++++++++++++
.../jaxrs/security/oauth2/tls/serverTls.xml | 5 +
9 files changed, 411 insertions(+), 13 deletions(-)
create mode 100644 core/src/test/java/org/apache/cxf/ws/addressing/EndpointReferenceUtilsTest.java
create mode 100644 rt/rs/security/oauth-parent/oauth2-saml/src/test/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidatorTest.java
create mode 100644 rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationServiceTest.java
(cxf) 01/04: Fix bug with SAML clientAddress (#3298)
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 22995b3e5ff623f82f4f07702a0b6213592fe7c5
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Fri Jul 10 10:34:50 2026 +0100
Fix bug with SAML clientAddress (#3298)
(cherry picked from commit 8ab0d8bc99f83fb2cae973e0202ef9176589defa)
---
rt/rs/security/oauth-parent/oauth2-saml/pom.xml | 5 +
.../security/oauth2/saml/SamlOAuthValidator.java | 2 +-
.../oauth2/saml/SamlOAuthValidatorTest.java | 127 +++++++++++++++++++++
3 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/rt/rs/security/oauth-parent/oauth2-saml/pom.xml b/rt/rs/security/oauth-parent/oauth2-saml/pom.xml
index ae012cb1c96..77243f11a22 100644
--- a/rt/rs/security/oauth-parent/oauth2-saml/pom.xml
+++ b/rt/rs/security/oauth-parent/oauth2-saml/pom.xml
@@ -47,5 +47,10 @@
<artifactId>cxf-rt-rs-security-xml</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java
index 27ccf9ebfb7..94a15e2a564 100644
--- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java
+++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java
@@ -58,7 +58,7 @@ public class SamlOAuthValidator {
}
public void setClientAddress(String value) {
- issuer = value;
+ clientAddress = value;
}
public void validate(Message message, SamlAssertionWrapper wrapper) {
diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/test/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidatorTest.java b/rt/rs/security/oauth-parent/oauth2-saml/src/test/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidatorTest.java
new file mode 100644
index 00000000000..a127ae9bfb2
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-saml/src/test/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidatorTest.java
@@ -0,0 +1,127 @@
+/**
+ * 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.
+ */
+package org.apache.cxf.rs.security.oauth2.saml;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import jakarta.ws.rs.NotAuthorizedException;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.bean.SubjectBean;
+import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
+import org.apache.wss4j.common.saml.bean.Version;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+import org.apache.wss4j.dom.engine.WSSConfig;
+
+import org.junit.Test;
+
+public class SamlOAuthValidatorTest {
+
+ static {
+ WSSConfig.init();
+ }
+
+ @Test
+ public void testValidateWithMatchingClientAddress() throws Exception {
+ String tokenServiceAddress = "https://service.example.org/token";
+ String clientAddress = "192.0.2.10";
+
+ SamlOAuthValidator validator = new SamlOAuthValidator();
+ validator.setAccessTokenServiceAddress(tokenServiceAddress);
+ validator.setClientAddress(clientAddress);
+
+ SamlAssertionWrapper wrapper = createAssertion(tokenServiceAddress, clientAddress);
+
+ // This should pass for a valid assertion but currently fails because
+ // setClientAddress incorrectly updates issuer instead of clientAddress.
+ validator.validate(null, wrapper);
+ }
+
+ @Test(expected = NotAuthorizedException.class)
+ public void testValidateWithMismatchingClientAddress() throws Exception {
+ String tokenServiceAddress = "https://service.example.org/token";
+
+ SamlOAuthValidator validator = new SamlOAuthValidator();
+ validator.setAccessTokenServiceAddress(tokenServiceAddress);
+ validator.setClientAddress("192.0.2.10");
+
+ SamlAssertionWrapper wrapper = createAssertion(tokenServiceAddress, "192.0.2.20");
+ validator.validate(null, wrapper);
+ }
+
+ private SamlAssertionWrapper createAssertion(String recipient, String clientAddress) throws Exception {
+ SubjectConfirmationDataBean confirmationData = new SubjectConfirmationDataBean();
+ confirmationData.setRecipient(recipient);
+ confirmationData.setAddress(clientAddress);
+ confirmationData.setNotAfter(Instant.now().plus(Duration.ofMinutes(5)));
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setNotBefore(Instant.now().minusSeconds(30));
+ conditions.setNotAfter(Instant.now().plus(Duration.ofMinutes(5)));
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.setAudienceURIs(Collections.singletonList(recipient));
+ conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(new TestSaml2CallbackHandler(conditions, confirmationData), samlCallback);
+ return new SamlAssertionWrapper(samlCallback);
+ }
+
+ private static class TestSaml2CallbackHandler implements CallbackHandler {
+ private final ConditionsBean conditions;
+ private final SubjectConfirmationDataBean confirmationData;
+
+ TestSaml2CallbackHandler(ConditionsBean conditions, SubjectConfirmationDataBean confirmationData) {
+ this.conditions = conditions;
+ this.confirmationData = confirmationData;
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (!(callback instanceof SAMLCallback)) {
+ throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
+ }
+
+ SAMLCallback samlCallback = (SAMLCallback)callback;
+ samlCallback.setSamlVersion(Version.SAML_20);
+ samlCallback.setIssuer("https://issuer.example.org");
+ samlCallback.setConditions(conditions);
+
+ SubjectBean subject = new SubjectBean(
+ "alice",
+ "service.example.org",
+ SAML2Constants.CONF_BEARER
+ );
+ subject.setSubjectConfirmationData(confirmationData);
+ samlCallback.setSubject(subject);
+ }
+ }
+ }
+}
(cxf) 04/04: Recording .gitmergeinfo Changes
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit f2ac92f01e59d91cc000d18b83afe1f4a110fda0
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Jul 10 10:40:21 2026 +0100
Recording .gitmergeinfo Changes
---
.gitmergeinfo | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitmergeinfo b/.gitmergeinfo
index 1406e138d70..10fe2629b65 100644
--- a/.gitmergeinfo
+++ b/.gitmergeinfo
@@ -117,6 +117,7 @@ M 3db372db49ea82f9198f572cc315212210fc65a7
M 3db651449ead313e80a704937625a93bae782877
M 3df960a32545b65207fd0b8f7ef38ca186c8cf93
M 3e602e82462a880a7ac0844806e880a49490ffdc
+M 3fa2ffd04bc814d2fa9ab723d9b6c7f9fad35ed8
M 3fd25230dd758f75d4700280ab0db20f3dbf7424
M 40bbcd8acfe0a60e4631aa69733ed33c5e2e4dd8
M 42b912b75a4c5a1537aa1f11a2eb1a2d712c25c3
(cxf) 02/04: Plug URIResolver into EndpointReferenceUtils (#3296)
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 417584b2780f3bac011813f782e856d44b090f72
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Fri Jul 10 10:35:33 2026 +0100
Plug URIResolver into EndpointReferenceUtils (#3296)
(cherry picked from commit 2a508d5235c0b9a2de1bb96c801365ad58ff61d5)
---
.../cxf/ws/addressing/EndpointReferenceUtils.java | 19 ++-
.../ws/addressing/EndpointReferenceUtilsTest.java | 141 +++++++++++++++++++++
2 files changed, 149 insertions(+), 11 deletions(-)
diff --git a/core/src/main/java/org/apache/cxf/ws/addressing/EndpointReferenceUtils.java b/core/src/main/java/org/apache/cxf/ws/addressing/EndpointReferenceUtils.java
index d6bf974f51f..90bb8bc9230 100644
--- a/core/src/main/java/org/apache/cxf/ws/addressing/EndpointReferenceUtils.java
+++ b/core/src/main/java/org/apache/cxf/ws/addressing/EndpointReferenceUtils.java
@@ -20,7 +20,6 @@
package org.apache.cxf.ws.addressing;
import java.io.ByteArrayInputStream;
-import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
@@ -72,6 +71,7 @@ import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.helpers.LoadingByteArrayOutputStream;
import org.apache.cxf.resource.ExtendedURIResolver;
import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.resource.URIResolver;
import org.apache.cxf.service.model.SchemaInfo;
import org.apache.cxf.service.model.ServiceInfo;
import org.apache.cxf.staxutils.StaxUtils;
@@ -550,20 +550,17 @@ public final class EndpointReferenceUtils {
&& !schemaSourcesMap.containsKey(sch.getSourceURI() + ':'
+ sch.getTargetNamespace())) {
- InputStream ins = null;
- try {
- URL url = new URL(sch.getSourceURI());
- ins = url.openStream();
+ LoadingByteArrayOutputStream out = new LoadingByteArrayOutputStream();
+ try (URIResolver resolver = new URIResolver(sch.getSourceURI())) {
+ if (resolver.getInputStream() == null) {
+ sch.write(out);
+ } else {
+ IOUtils.copyAndCloseInput(resolver.getInputStream(), out);
+ }
} catch (Exception e) {
//ignore, we'll just use what we have. (though
//bugs in XmlSchema could make this less useful)
- }
-
- LoadingByteArrayOutputStream out = new LoadingByteArrayOutputStream();
- if (ins == null) {
sch.write(out);
- } else {
- IOUtils.copyAndCloseInput(ins, out);
}
schemaSourcesMap.put(sch.getSourceURI() + ':'
diff --git a/core/src/test/java/org/apache/cxf/ws/addressing/EndpointReferenceUtilsTest.java b/core/src/test/java/org/apache/cxf/ws/addressing/EndpointReferenceUtilsTest.java
new file mode 100644
index 00000000000..8c229a0a168
--- /dev/null
+++ b/core/src/test/java/org/apache/cxf/ws/addressing/EndpointReferenceUtilsTest.java
@@ -0,0 +1,141 @@
+/**
+ * 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.
+ */
+
+package org.apache.cxf.ws.addressing;
+
+import java.io.ByteArrayInputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.xml.validation.Schema;
+
+import org.w3c.dom.Document;
+
+import org.apache.cxf.resource.URIResolver;
+import org.apache.cxf.service.model.SchemaInfo;
+import org.apache.cxf.service.model.ServiceInfo;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.ws.commons.schema.XmlSchema;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class EndpointReferenceUtilsTest {
+
+ @Test
+ public void testGetSchemaCanOpenSourceUriAllowedByDefaultSchemes() throws Exception {
+ try (LocalHttpProbeServer probeServer = new LocalHttpProbeServer()) {
+ String sourceUri = "http://127.0.0.1:" + probeServer.getPort() + "/schema.xsd";
+ ServiceInfo serviceInfo = createServiceInfo(sourceUri);
+
+ Schema schema = EndpointReferenceUtils.getSchema(serviceInfo, null);
+ assertNotNull(schema);
+
+ probeServer.awaitCompletion();
+ assertTrue("Default allowed schemes should permit opening sourceURI", probeServer.wasConnected());
+ }
+ }
+
+ @Test
+ public void testGetSchemaDoesNotOpenDisallowedFtpSourceUri() throws Exception {
+ try (LocalHttpProbeServer probeServer = new LocalHttpProbeServer()) {
+ String sourceUri = "ftp://127.0.0.1:" + probeServer.getPort() + "/schema.xsd";
+
+ assertFalse("ftp scheme should be disallowed by default",
+ URIResolver.getAllowedSchemes().contains("ftp"));
+
+ ServiceInfo serviceInfo = createServiceInfo(sourceUri);
+ Schema schema = EndpointReferenceUtils.getSchema(serviceInfo, null);
+ assertNotNull(schema);
+
+ probeServer.awaitCompletion();
+ assertFalse("Disallowed sourceURI should not be opened", probeServer.wasConnected());
+ }
+ }
+
+ private ServiceInfo createServiceInfo(String sourceUri) throws Exception {
+ String namespace = "urn:test:endpoint:reference:utils";
+ String schemaText =
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema' "
+ + "targetNamespace='" + namespace + "' elementFormDefault='qualified'>"
+ + "<xsd:element name='value' type='xsd:string'/>"
+ + "</xsd:schema>";
+
+ Document doc = StaxUtils.read(new ByteArrayInputStream(schemaText.getBytes(StandardCharsets.UTF_8)));
+
+ ServiceInfo serviceInfo = new ServiceInfo();
+ XmlSchema xmlSchema = serviceInfo.getXmlSchemaCollection().read(doc.getDocumentElement(), sourceUri);
+
+ SchemaInfo schemaInfo = new SchemaInfo(namespace);
+ schemaInfo.setSchema(xmlSchema);
+ schemaInfo.setSystemId("memory:/schema.xsd");
+ serviceInfo.addSchema(schemaInfo);
+
+ return serviceInfo;
+ }
+
+ private static final class LocalHttpProbeServer implements AutoCloseable {
+ private final ServerSocket serverSocket;
+ private final AtomicBoolean connected = new AtomicBoolean();
+ private final Thread thread;
+
+ private LocalHttpProbeServer() throws Exception {
+ this.serverSocket = new ServerSocket(0);
+ this.serverSocket.setSoTimeout(750);
+ this.thread = new Thread(this::acceptOneConnection, "EndpointReferenceUtilsTest-HttpProbe");
+ this.thread.setDaemon(true);
+ this.thread.start();
+ }
+
+ private void acceptOneConnection() {
+ try (Socket socket = serverSocket.accept()) {
+ connected.set(true);
+ OutputStream out = socket.getOutputStream();
+ out.write("HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n".getBytes(StandardCharsets.US_ASCII));
+ out.flush();
+ } catch (Exception ex) {
+ // timeout/no connection is expected for this test
+ }
+ }
+
+ private int getPort() {
+ return serverSocket.getLocalPort();
+ }
+
+ private boolean wasConnected() {
+ return connected.get();
+ }
+
+ private void awaitCompletion() throws InterruptedException {
+ thread.join(1500);
+ }
+
+ @Override
+ public void close() throws Exception {
+ serverSocket.close();
+ awaitCompletion();
+ }
+ }
+}
(cxf) 03/04: Disallow a dynamic registration client from requesting scopes without validation (#3297)
Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 83adf63fa0d05c5c834d678a8df976e6b737d647
Author: Colm O hEigeartaigh <co...@users.noreply.github.com>
AuthorDate: Fri Jul 10 10:39:34 2026 +0100
Disallow a dynamic registration client from requesting scopes without validation (#3297)
(cherry picked from commit 3fa2ffd04bc814d2fa9ab723d9b6c7f9fad35ed8)
# Conflicts:
# systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/tls/serverTls-fips.xml
---
.../services/DynamicRegistrationService.java | 30 ++++++-
.../services/DynamicRegistrationServiceTest.java | 94 ++++++++++++++++++++++
.../jaxrs/security/oauth2/tls/serverTls.xml | 5 ++
3 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationService.java
index e9b46b53692..eb0e387a744 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationService.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationService.java
@@ -19,6 +19,7 @@
package org.apache.cxf.rs.security.oauth2.services;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import jakarta.ws.rs.Consumes;
@@ -44,6 +45,7 @@ import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.rs.security.oauth2.common.Client;
import org.apache.cxf.rs.security.oauth2.common.OAuthError;
import org.apache.cxf.rs.security.oauth2.common.UserSubject;
+import org.apache.cxf.rs.security.oauth2.provider.AbstractOAuthDataProvider;
import org.apache.cxf.rs.security.oauth2.provider.ClientRegistrationProvider;
import org.apache.cxf.rs.security.oauth2.utils.AuthorizationUtils;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
@@ -52,6 +54,7 @@ import org.apache.cxf.rt.security.crypto.CryptoUtils;
@Path("register")
public class DynamicRegistrationService {
+ private static final String INVALID_CLIENT_METADATA = "invalid_client_metadata";
private static final String DEFAULT_APPLICATION_TYPE = "web";
private static final Integer DEFAULT_CLIENT_ID_SIZE = 10;
private ClientRegistrationProvider clientProvider;
@@ -60,6 +63,7 @@ public class DynamicRegistrationService {
private MessageContext mc;
private boolean supportRegistrationAccessTokens = true;
private String userRole;
+ private List<String> allowedClientScopes;
@POST
@Consumes("application/json")
@@ -328,7 +332,9 @@ public class DynamicRegistrationService {
// Client Scopes
String scope = request.getScope();
if (!StringUtils.isEmpty(scope)) {
- client.setRegisteredScopes(OAuthUtils.parseScope(scope));
+ List<String> requestedScopes = OAuthUtils.parseScope(scope);
+ validateClientScopes(requestedScopes);
+ client.setRegisteredScopes(requestedScopes);
}
// Client Application URI
String clientUri = request.getClientUri();
@@ -417,6 +423,28 @@ public class DynamicRegistrationService {
this.userRole = userRole;
}
+ public void setAllowedClientScopes(List<String> allowedClientScopes) {
+ this.allowedClientScopes = allowedClientScopes;
+ }
+
+ protected void validateClientScopes(List<String> requestedScopes) {
+ if (requestedScopes == null || requestedScopes.isEmpty()) {
+ return;
+ }
+
+ List<String> allowedScopes = allowedClientScopes;
+ if (allowedScopes == null && clientProvider instanceof AbstractOAuthDataProvider) {
+ allowedScopes = new java.util.ArrayList<>(
+ ((AbstractOAuthDataProvider)clientProvider).getPermissionMap().keySet());
+ }
+
+ if (allowedScopes != null && !new HashSet<>(allowedScopes).containsAll(requestedScopes)) {
+ OAuthError error =
+ new OAuthError(INVALID_CLIENT_METADATA, "Invalid scope metadata");
+ reportInvalidRequestError(error);
+ }
+ }
+
private void reportInvalidRequestError(OAuthError entity) {
reportInvalidRequestError(entity, MediaType.APPLICATION_JSON_TYPE);
}
diff --git a/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationServiceTest.java b/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationServiceTest.java
new file mode 100644
index 00000000000..72fddf6516a
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2/src/test/java/org/apache/cxf/rs/security/oauth2/services/DynamicRegistrationServiceTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.
+ */
+package org.apache.cxf.rs.security.oauth2.services;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import jakarta.ws.rs.BadRequestException;
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.OAuthError;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+public class DynamicRegistrationServiceTest {
+
+ @Test
+ public void testRejectsUnallowedRegisteredScope() {
+ TestDynamicRegistrationService service = new TestDynamicRegistrationService();
+ service.setAllowedClientScopes(Collections.singletonList("read"));
+
+ ClientRegistration request = new ClientRegistration();
+ request.setScope("admin read");
+
+ Client client = createClient();
+
+ BadRequestException ex = assertThrows(BadRequestException.class,
+ () -> service.applyClientRegistration(request, client));
+
+ assertNotNull(ex.getResponse());
+ OAuthError error = (OAuthError)ex.getResponse().getEntity();
+ assertNotNull(error);
+ assertEquals("invalid_client_metadata", error.getError());
+ }
+
+ @Test
+ public void testAcceptsAllowedRegisteredScopes() {
+ TestDynamicRegistrationService service = new TestDynamicRegistrationService();
+ service.setAllowedClientScopes(Arrays.asList("read", "write"));
+
+ ClientRegistration request = new ClientRegistration();
+ request.setScope("read write");
+
+ Client client = createClient();
+ service.applyClientRegistration(request, client);
+
+ assertEquals(Arrays.asList("read", "write"), client.getRegisteredScopes());
+ }
+
+ @Test
+ public void testAcceptsRegisteredScopesWhenAllowlistNotConfigured() {
+ TestDynamicRegistrationService service = new TestDynamicRegistrationService();
+
+ ClientRegistration request = new ClientRegistration();
+ request.setScope("openid");
+
+ Client client = createClient();
+ service.applyClientRegistration(request, client);
+
+ assertEquals(Collections.singletonList("openid"), client.getRegisteredScopes());
+ }
+
+ private static Client createClient() {
+ Client client = new Client("client", "secret", true);
+ client.setAllowedGrantTypes(Collections.singletonList(OAuthConstants.CLIENT_CREDENTIALS_GRANT));
+ return client;
+ }
+
+ private static final class TestDynamicRegistrationService extends DynamicRegistrationService {
+ void applyClientRegistration(ClientRegistration request, Client client) {
+ fromClientRegistrationToClient(request, client);
+ }
+ }
+}
diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/tls/serverTls.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/tls/serverTls.xml
index c88837d1d3e..8bc54574ccd 100644
--- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/tls/serverTls.xml
+++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/tls/serverTls.xml
@@ -109,6 +109,11 @@ under the License.
<bean id="dynRegServiceWithAt" class="org.apache.cxf.rs.security.oauth2.services.DynamicRegistrationService">
<property name="clientProvider" ref="dataProviderJwt"/>
<property name="initialAccessToken" value="123456789"/>
+ <property name="allowedClientScopes">
+ <list>
+ <value>openid</value>
+ </list>
+ </property>
</bean>
<jaxrs:server id="tokenServer1" address="https://localhost:${testutil.ports.jaxrs-oauth2-tls}/oauth2">