You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by bu...@apache.org on 2020/03/07 10:45:46 UTC
[cxf] 01/03: CXF-8229 OAuth 2.0 Dynamic Client Registration: Client
Update Request (#646)
This is an automated email from the ASF dual-hosted git repository.
buhhunyx pushed a commit to branch 3.3.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 36aa9716829eb4770df73fa02cec3df639f760a2
Author: Alexey Markevich <bu...@gmail.com>
AuthorDate: Fri Mar 6 23:43:31 2020 +0300
CXF-8229 OAuth 2.0 Dynamic Client Registration: Client Update Request (#646)
* CXF-8229 OAuth 2.0 Dynamic Client Registration: Client Update Request
* add negative test cases
(cherry picked from commit 169be66c11851d723e4a8d93f752e0b3dff1fa60)
---
.../services/DynamicRegistrationService.java | 50 +++++---
.../cxf/rs/security/oauth2/utils/OAuthUtils.java | 2 +-
.../security/oidc/OIDCDynamicRegistrationTest.java | 141 +++++++++++++--------
3 files changed, 120 insertions(+), 73 deletions(-)
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 12155d9..9b50475 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
@@ -128,8 +128,13 @@ public class DynamicRegistrationService {
@PUT
@Path("{clientId}")
@Consumes("application/json")
- public Response updateClientRegistration(@PathParam("clientId") String clientId) {
- return Response.ok().build();
+ @Produces("application/json")
+ public ClientRegistration updateClientRegistration(@PathParam("clientId") String clientId,
+ ClientRegistration request) {
+ Client client = readClient(clientId);
+ fromClientRegistrationToClient(request, client);
+ clientProvider.setClient(client);
+ return fromClientToClientRegistration(client);
}
@DELETE
@@ -278,16 +283,35 @@ public class DynamicRegistrationService {
// Client Registration Time
newClient.setRegisteredAt(System.currentTimeMillis() / 1000L);
+ fromClientRegistrationToClient(request, newClient);
+
+ SecurityContext sc = mc.getSecurityContext();
+ if (sc != null && sc.getUserPrincipal() != null && sc.getUserPrincipal().getName() != null) {
+ UserSubject subject = new UserSubject(sc.getUserPrincipal().getName());
+ newClient.setResourceOwnerSubject(subject);
+ }
+
+ newClient.setRegisteredDynamically(true);
+ return newClient;
+ }
+
+ protected void fromClientRegistrationToClient(ClientRegistration request, Client client) {
+ final List<String> grantTypes = client.getAllowedGrantTypes();
+
// Client Redirect URIs
List<String> redirectUris = request.getRedirectUris();
if (redirectUris != null) {
+ String appType = request.getApplicationType();
+ if (appType == null) {
+ appType = DEFAULT_APPLICATION_TYPE;
+ }
for (String uri : redirectUris) {
validateRequestUri(uri, appType, grantTypes);
}
- newClient.setRedirectUris(redirectUris);
+ client.setRedirectUris(redirectUris);
}
- if (newClient.getRedirectUris().isEmpty()
+ if (client.getRedirectUris().isEmpty()
&& (grantTypes.contains(OAuthConstants.AUTHORIZATION_CODE_GRANT)
|| grantTypes.contains(OAuthConstants.IMPLICIT_GRANT))) {
// Throw an error as we need a redirect URI for these grants.
@@ -299,39 +323,31 @@ public class DynamicRegistrationService {
// Client Resource Audience URIs
List<String> resourceUris = request.getResourceUris();
if (resourceUris != null) {
- newClient.setRegisteredAudiences(resourceUris);
+ client.setRegisteredAudiences(resourceUris);
}
// Client Scopes
String scope = request.getScope();
if (!StringUtils.isEmpty(scope)) {
- newClient.setRegisteredScopes(OAuthUtils.parseScope(scope));
+ client.setRegisteredScopes(OAuthUtils.parseScope(scope));
}
// Client Application URI
String clientUri = request.getClientUri();
if (clientUri != null) {
- newClient.setApplicationWebUri(clientUri);
+ client.setApplicationWebUri(clientUri);
}
// Client Logo URI
String clientLogoUri = request.getLogoUri();
if (clientLogoUri != null) {
- newClient.setApplicationLogoUri(clientLogoUri);
+ client.setApplicationLogoUri(clientLogoUri);
}
//TODO: check other properties
// Add more typed properties like tosUri, policyUri, etc to Client
// or set them as Client extra properties
-
- SecurityContext sc = mc.getSecurityContext();
- if (sc != null && sc.getUserPrincipal() != null && sc.getUserPrincipal().getName() != null) {
- UserSubject subject = new UserSubject(sc.getUserPrincipal().getName());
- newClient.setResourceOwnerSubject(subject);
- }
-
- newClient.setRegisteredDynamically(true);
- return newClient;
}
+
protected boolean isPasswordRequired(List<String> grantTypes, String tokenEndpointAuthMethod) {
if (grantTypes.contains(OAuthConstants.IMPLICIT_GRANT)) {
return false;
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java
index 356f599..6a2ca81 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java
@@ -443,6 +443,6 @@ public final class OAuthUtils {
}
public static String convertListOfScopesToString(List<String> registeredScopes) {
- return String.join(", ", registeredScopes);
+ return String.join(" ", registeredScopes);
}
}
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCDynamicRegistrationTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCDynamicRegistrationTest.java
index 74ff6bd..ea5393f 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCDynamicRegistrationTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCDynamicRegistrationTest.java
@@ -23,6 +23,7 @@ import java.net.URL;
import java.util.Collections;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.provider.json.JsonMapObjectProvider;
@@ -76,16 +77,13 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
public void testRegisterClientInitialAccessTokenCodeGrant() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
- WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
- busFile.toString());
+ WebClient wc =
+ WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
+ .accept("application/json").type("application/json")
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
- wc.accept("application/json").type("application/json");
ClientRegistration reg = newClientRegistrationCodeGrant();
- ClientRegistrationResponse resp = null;
- assertEquals(401, wc.post(reg).getStatus());
-
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
- resp = wc.post(reg, ClientRegistrationResponse.class);
+ ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNotNull(resp.getClientSecret());
@@ -94,12 +92,12 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
- wc.reset();
wc.path(resp.getClientId());
assertEquals(401, wc.get().getStatus());
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
- ClientRegistration clientRegResp = wc.get(ClientRegistration.class);
+ ClientRegistration clientRegResp = wc
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
+ .get(ClientRegistration.class);
testCommonRegCodeGrantProperties(clientRegResp);
assertNull(clientRegResp.getTokenEndpointAuthMethod());
@@ -111,16 +109,15 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
public void testRegisterClientPasswordGrant() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
- WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
- busFile.toString());
-
- wc.accept("application/json").type("application/json");
+ WebClient wc =
+ WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
+ .accept("application/json").type("application/json")
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = new ClientRegistration();
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT));
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
@@ -130,11 +127,10 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
- wc.reset();
- wc.path(resp.getClientId());
+ ClientRegistration clientRegResp = wc.path(resp.getClientId())
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
+ .get(ClientRegistration.class);
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
- ClientRegistration clientRegResp = wc.get(ClientRegistration.class);
assertEquals("web", clientRegResp.getApplicationType());
assertEquals("dynamic_client", clientRegResp.getClientName());
assertEquals(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT),
@@ -150,16 +146,15 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
public void testRegisterClientPasswordGrantPublic() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
- WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
- busFile.toString());
-
- wc.accept("application/json").type("application/json");
+ WebClient wc =
+ WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
+ .accept("application/json").type("application/json")
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = new ClientRegistration();
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT));
reg.setTokenEndpointAuthMethod(OAuthConstants.TOKEN_ENDPOINT_AUTH_NONE);
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
@@ -168,11 +163,10 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
- wc.reset();
- wc.path(resp.getClientId());
+ ClientRegistration clientRegResp = wc.path(resp.getClientId())
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
+ .get(ClientRegistration.class);
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
- ClientRegistration clientRegResp = wc.get(ClientRegistration.class);
assertEquals("native", clientRegResp.getApplicationType());
assertEquals("dynamic_client", clientRegResp.getClientName());
assertEquals(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT),
@@ -184,37 +178,21 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
assertEquals(200, wc.delete().getStatus());
}
- private void testCommonRegCodeGrantProperties(ClientRegistration clientRegResp) {
- assertNotNull(clientRegResp);
- assertEquals("web", clientRegResp.getApplicationType());
- assertEquals("dynamic_client", clientRegResp.getClientName());
- assertEquals("openid", clientRegResp.getScope());
- assertEquals(Collections.singletonList(OAuthConstants.AUTHORIZATION_CODE_GRANT),
- clientRegResp.getGrantTypes());
- assertEquals(Collections.singletonList("https://a/b/c"),
- clientRegResp.getRedirectUris());
- assertEquals(Collections.singletonList("https://rp/logout"),
- clientRegResp.getListStringProperty("post_logout_redirect_uris"));
- }
-
@org.junit.Test
public void testRegisterClientInitialAccessTokenCodeGrantTls() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
- WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
- busFile.toString());
+ WebClient wc =
+ WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
+ .accept("application/json").type("application/json")
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
- wc.accept("application/json").type("application/json");
ClientRegistration reg = newClientRegistrationCodeGrant();
reg.setTokenEndpointAuthMethod(OAuthConstants.TOKEN_ENDPOINT_AUTH_TLS);
reg.setProperty(OAuthConstants.TLS_CLIENT_AUTH_SUBJECT_DN,
"CN=whateverhost.com,OU=Morpit,O=ApacheTest,L=Syracuse,C=US");
- ClientRegistrationResponse resp = null;
- assertEquals(401, wc.post(reg).getStatus());
-
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
- resp = wc.post(reg, ClientRegistrationResponse.class);
+ ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNull(resp.getClientSecret());
@@ -223,12 +201,10 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
- wc.reset();
- wc.path(resp.getClientId());
- assertEquals(401, wc.get().getStatus());
+ ClientRegistration clientRegResp = wc.path(resp.getClientId())
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
+ .get(ClientRegistration.class);
- wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
- ClientRegistration clientRegResp = wc.get(ClientRegistration.class);
testCommonRegCodeGrantProperties(clientRegResp);
assertEquals(OAuthConstants.TOKEN_ENDPOINT_AUTH_TLS, clientRegResp.getTokenEndpointAuthMethod());
assertEquals("CN=whateverhost.com,OU=Morpit,O=ApacheTest,L=Syracuse,C=US",
@@ -237,12 +213,52 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
assertEquals(200, wc.delete().getStatus());
}
+ @org.junit.Test
+ public void testUpdateClient() throws Exception {
+ URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
+ String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
+ WebClient wc =
+ WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
+ .accept("application/json").type("application/json")
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
+
+ final ClientRegistration reg = newClientRegistrationCodeGrant();
+ final ClientRegistrationResponse clientRegistrationResponse = wc
+ .post(reg, ClientRegistrationResponse.class);
+
+ final String regAccessToken = clientRegistrationResponse.getRegistrationAccessToken();
+ assertNotNull(regAccessToken);
+
+ reg.setScope(OidcUtils.getEmailScope());
+ final ClientRegistration updatedClientRegistration = wc.path(clientRegistrationResponse.getClientId())
+ .authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
+ .put(reg, ClientRegistration.class);
+
+ assertEquals(OidcUtils.getEmailScope(), updatedClientRegistration.getScope());
+ // https://tools.ietf.org/html/rfc7592#section-2.2
+ assertNull(updatedClientRegistration.getProperty("registration_access_token"));
+ assertNull(updatedClientRegistration.getProperty("registration_client_uri"));
+ assertNull(updatedClientRegistration.getProperty("client_secret_expires_at"));
+ assertNull(updatedClientRegistration.getProperty("client_id_issued_at"));
+
+ wc.authorization(null);
+
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(),
+ wc.put(reg).getStatus());
+ assertEquals(Status.UNAUTHORIZED.getStatusCode(),
+ wc.delete().getStatus());
+
+ wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
+ assertEquals(200, wc.delete().getStatus());
+ }
+
private static ClientRegistration newClientRegistrationCodeGrant() {
final ClientRegistration reg = new ClientRegistration();
reg.setApplicationType("web");
reg.setScope(OidcUtils.getOpenIdScope());
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.AUTHORIZATION_CODE_GRANT));
+// reg.setResponseTypes(Collections.singletonList(OAuthConstants.CODE_RESPONSE_TYPE));
reg.setRedirectUris(Collections.singletonList("https://a/b/c"));
reg.setProperty("post_logout_redirect_uris",
@@ -250,4 +266,19 @@ public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase
return reg;
}
+ private static void testCommonRegCodeGrantProperties(ClientRegistration clientRegResp) {
+ assertNotNull(clientRegResp);
+ assertEquals("web", clientRegResp.getApplicationType());
+ assertEquals("openid", clientRegResp.getScope());
+ assertEquals("dynamic_client", clientRegResp.getClientName());
+ assertEquals(Collections.singletonList(OAuthConstants.AUTHORIZATION_CODE_GRANT),
+ clientRegResp.getGrantTypes());
+// assertEquals(Collections.singletonList(OAuthConstants.CODE_RESPONSE_TYPE),
+// clientRegResp.getResponseTypes());
+ assertEquals(Collections.singletonList("https://a/b/c"),
+ clientRegResp.getRedirectUris());
+ assertEquals(Collections.singletonList("https://rp/logout"),
+ clientRegResp.getListStringProperty("post_logout_redirect_uris"));
+ }
+
}