8000 Remove form data from the registerNode and unregisterNode method signature by rmartinc · Pull Request #48633 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.NotAuthorizedException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
Expand Down Expand Up @@ -91,14 +90,12 @@ public static UriBuilder unregisterNodeUrl(UriBuilder baseUriBuilder) {
/**
* URL invoked by adapter to register new client cluster node. Each application cluster node will invoke this URL once it joins cluster
*
* @param authorizationHeader
* @param formData
* @return
*/
@Path("register-node")
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response registerNode(@HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader, final MultivaluedMap<String, String> formData) {
public Response registerNode() {
if (!checkSsl()) {
throw new ForbiddenException("HTTPS required");
}
Expand All @@ -110,6 +107,7 @@ public Response registerNode(@HeaderParam(HttpHeaders.AUTHORIZATION) String auth
throw new NotAuthorizedException("Realm not enabled");
}

MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
ClientModel client = authorizeClient();
String nodeHost = getClientClusterHost(formData);

Expand All @@ -132,14 +130,12 @@ public Response registerNode(@HeaderParam(HttpHeaders.AUTHORIZATION) String auth
/**
* URL invoked by adapter to register new client cluster node. Each application cluster node will invoke this URL once it joins cluster
*
* @param authorizationHeader
* @param formData
* @return
*/
@Path("unregister-node")
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response unregisterNode(@HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader, final MultivaluedMap<String, String> formData) {
public Response unregisterNode() {
if (!checkSsl()) {
throw new ForbiddenException("HTTPS required");
}
Expand All @@ -151,6 +147,7 @@ public Response unregisterNode(@HeaderParam(HttpHeaders.AUTHORIZATION) String au
throw new NotAuthorizedException("Realm not enabled");
}

MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
ClientModel client = authorizeClient();
String nodeHost = getClientClusterHost(formData);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.keycloak.tests.admin;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -39,14 +40,18 @@
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RoleMappingResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator;
import org.keycloak.common.util.Time;
import org.keycloak.crypto.Algorithm;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.protocol.oidc.client.authentication.JWTClientSecretCredentialsProvider;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
Expand Down Expand Up @@ -79,6 +84,8 @@
import org.keycloak.tests.utils.admin.AdminEventPaths;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
import org.keycloak.testsuite.util.oauth.RegisterNodeResponse;
import org.keycloak.testsuite.util.oauth.UnregisterNodeResponse;

import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
Expand Down Expand Up @@ -671,6 +678,52 @@ public void nodes() throws MalformedURLException, InterruptedException {
managedRealm.admin().clients().get(id).unregisterNode(myhost);
}

@Test
public void nodesUsingClientEndpointAndJwt() throws MalformedURLException, InterruptedException {
testApp.kcAdmin().clear();

ClientResource clientResource = AdminApiUtil.findClientByClientId(managedRealm.admin(), "test-app");
ClientRepresentation clientRep = clientResource.toRepresentation();
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
clientResource.update(clientRep);
managedRealm.cleanup().add(r -> {
clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID);
r.clients().get(clientRep.getId()).update(clientRep);
});

JWTClientSecretCredentialsProvider jwtProvider = new JWTClientSecretCredentialsProvider();
jwtProvider.setClientSecret(clientRep.getSecret(), Algorithm.HS256);
String jwt = jwtProvider.createSignedRequestToken("test-app", oauth.getEndpoints().getIssuer(), Algorithm.HS256);

// register the node
String myhost = URI.create(managedRealm.getBaseUrl()).getHost();
RegisterNodeResponse resgiterResponse = oauth.registerNodeRequest().clientClusterHost(myhost).clientJwt(jwt).send();
Assertions.assertTrue(resgiterResponse.isSuccess());
jwt = jwtProvider.createSignedRequestToken("test-app", oauth.getEndpoints().getIssuer(), Algorithm.HS256);
resgiterResponse = oauth.registerNodeRequest().clientClusterHost("invalid").clientJwt(jwt).send();
Assertions.assertTrue(resgiterResponse.isSuccess());
GlobalRequestResult result = managedRealm.admin().clients().get(clientRep.getId()).testNodesAvailable();
assertEquals(1, result.getSuccessRequests().size());
asse 10000 rtEquals(1, result.getFailedRequests().size());

// test availability and nodes are registered
TestAvailabilityAction testAvailable = testApp.kcAdmin().getTestAvailable();
assertEquals("test-app", testAvailable.getResource());
assertEquals(2, managedRealm.admin().clients().get(clientRep.getId()).toRepresentation().getRegisteredNodes().size());

// unregister the invalid node
jwt = jwtProvider.createSignedRequestToken("test-app", oauth.getEndpoints().getIssuer(), Algorithm.HS256);
UnregisterNodeResponse unregisterResponse = oauth.unregisterNodeRequest().clientClusterHost("invalid").clientJwt(jwt).send();
Assertions.assertTrue(unregisterResponse.isSuccess());
assertEquals(1, managedRealm.admin().clients().get(clientRep.getId()).toRepresentation().getRegisteredNodes().size());

// unregister the valid node
jwt = jwtProvider.createSignedRequestToken("test-app", oauth.getEndpoints().getIssuer(), Algorithm.HS256);
unregisterResponse = oauth.unregisterNodeRequest().clientClusterHost(myhost).clientJwt(jwt).send();
Assertions.assertTrue(unregisterResponse.isSuccess());
assertNull(managedRealm.admin().clients().get(clientRep.getId()).toRepresentation().getRegisteredNodes());
}

@Test
public void offlineUserSessions() {
ClientRepresentation client = AdminApiUtil.findClientByClientId(managedRealm.admin(), "test-app").toRepresentation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ public ParResponse doPushedAuthorizationRequest() {
return pushedAuthorizationRequest().send();
}

public RegisterNodeRequest registerNodeRequest() {
return new RegisterNodeRequest(this);
}

public UnregisterNodeRequest unregisterNodeRequest() {
return new UnregisterNodeRequest(this);
}

public <J extends JsonWebToken> J parseToken(String token, Class<J> clazz) {
return tokensManager.parseToken(token, clazz);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.keycloak.testsuite.util.oauth;

import java.io.IOException;

import jakarta.ws.rs.core.UriBuilder;

import org.keycloak.constants.AdapterConstants;
import org.keycloak.services.resources.ClientsManagementService;
import org.keycloak.services.resources.RealmsResource;

import org.apache.http.client.methods.CloseableHttpResponse;

/**
*
* @author rmartinc
*/
public class RegisterNodeRequest extends AbstractHttpPostRequest<RegisterNo BD0 deRequest, RegisterNodeResponse> {

private String clientClusterHost;

RegisterNodeRequest(AbstractOAuthClient<?> client) {
super(client);
}

@Override
protected String getEndpoint() {
return UriBuilder.fromUri(client.getBaseUrl())
.path(RealmsResource.class)
.path("{realm}/clients-managements")
.path(ClientsManagementService.class, "registerNode")
.build(client.getRealm())
.toString();
}

public RegisterNodeRequest clientClusterHost(String clientClusterHost) {
this.clientClusterHost = clientClusterHost;
return this;
}

@Override
protected void initRequest() {
parameter(AdapterConstants.CLIENT_CLUSTER_HOST, clientClusterHost);
}

@Override
protected RegisterNodeResponse toResponse(CloseableHttpResponse response) throws IOException {
return new RegisterNodeResponse(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.keycloak.testsuite.util.oauth;

import java.io.IOException;

import org.apache.http.client.methods.CloseableHttpResponse;

/**
*
* @author rmartinc
*/
public class RegisterNodeResponse extends AbstractHttpResponse {

public RegisterNodeResponse(CloseableHttpResponse response) throws IOException {
super(response);
}

@Override
protected void parseContent() {
}

@Override
protected int getSuccessCode() {
return 204;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.keycloak.testsuite.util.oauth;

import java.io.IOException;

import jakarta.ws.rs.core.UriBuilder;

import org.keycloak.constants.AdapterConstants;
import org.keycloak.services.resources.ClientsManagementService;
import org.keycloak.services.resources.RealmsResource;

import org.apache.http.client.methods.CloseableHttpResponse;

/**
*
* @author rmartinc
*/
public class UnregisterNodeRequest extends AbstractHttpPostRequest<UnregisterNodeRequest, UnregisterNodeResponse> {

private String clientClusterHost;

UnregisterNodeRequest(AbstractOAuthClient<?> client) {
super(client);
}

@Override
protected String getEndpoint() {
return UriBuilder.fromUri(client.getBaseUrl())
.path(RealmsResource.class)
.path("{realm}/clients-managements")
.path(ClientsManagementService.class, "unregisterNode")
.build(client.getRealm())
.toString();
}

public UnregisterNodeRequest clientClusterHost(String clientClusterHost) {
this.clientClusterHost = clientClusterHost;
return this;
}

@Override
protected void initRequest() {
parameter(AdapterConstants.CLIENT_CLUSTER_HOST, clientClusterHost);
}

@Override
protected UnregisterNodeResponse toResponse(CloseableHttpResponse response) throws IOException {
return new UnregisterNodeResponse(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.keycloak.testsuite.util.oauth;

import java.io.IOException;

import org.apache.http.client.methods.CloseableHttpResponse;

/**
*
* @author rmartinc
*/
public class UnregisterNodeResponse extends AbstractHttpResponse {

public UnregisterNodeResponse(CloseableHttpResponse response) throws IOException {
super(response);
}

@Override
protected void parseContent() {
}

@Override
protected int getSuccessCode() {
return 204;
}
}
Loading
0