KEYCLOAK-10065 Merge preview features test: BrokerLinkAndTokenExchangeTest
This commit is contained in:
parent
391f1bf069
commit
ccd90d5fdc
2 changed files with 294 additions and 267 deletions
|
@ -16,9 +16,16 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.adapter.servlet;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.Assert;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
|
@ -31,14 +38,9 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -69,22 +71,20 @@ public class LinkAndExchangeServlet extends HttpServlet {
|
|||
|
||||
public AccessTokenResponse doTokenExchange(String realm, String token, String requestedIssuer,
|
||||
String clientId, String clientSecret) throws Exception {
|
||||
try {
|
||||
|
||||
try (CloseableHttpClient client = (CloseableHttpClient) new HttpClientBuilder().disableTrustManager().build()) {
|
||||
String exchangeUrl = KeycloakUriBuilder.fromUri(ServletTestUtils.getAuthServerUrlBase())
|
||||
.path("/auth/realms/{realm}/protocol/openid-connect/token").build(realm).toString();
|
||||
|
||||
URL url = new URL(exchangeUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoInput(true);
|
||||
conn.setDoOutput(true);
|
||||
HttpPost post = new HttpPost(exchangeUrl);
|
||||
HashMap<String, String> parameters = new HashMap<>();
|
||||
|
||||
if (clientSecret != null) {
|
||||
String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
|
||||
conn.setRequestProperty(HttpHeaders.AUTHORIZATION, authorization);
|
||||
post.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString());
|
||||
post.setHeader(HttpHeaders.AUTHORIZATION, authorization);
|
||||
} else {
|
||||
parameters.put("client_id", clientId);
|
||||
|
||||
}
|
||||
|
||||
parameters.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE);
|
||||
|
@ -92,27 +92,15 @@ public class LinkAndExchangeServlet extends HttpServlet {
|
|||
parameters.put(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE);
|
||||
parameters.put(OAuth2Constants.REQUESTED_ISSUER, requestedIssuer);
|
||||
|
||||
OutputStream os = conn.getOutputStream();
|
||||
BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(os, "UTF-8"));
|
||||
writer.write(getPostDataString(parameters));
|
||||
|
||||
writer.flush();
|
||||
writer.close();
|
||||
os.close();
|
||||
if (conn.getResponseCode() == 200) {
|
||||
AccessTokenResponse tokenResponse = JsonSerialization.readValue(conn.getInputStream(), AccessTokenResponse.class);
|
||||
conn.getInputStream().close();
|
||||
return tokenResponse;
|
||||
} else if (conn.getResponseCode() == 400) {
|
||||
AccessTokenResponse tokenResponse = JsonSerialization.readValue(conn.getErrorStream(), AccessTokenResponse.class);
|
||||
conn.getErrorStream().close();
|
||||
return tokenResponse;
|
||||
post.setEntity(new StringEntity(getPostDataString(parameters)));
|
||||
HttpResponse response = client.execute(post);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (statusCode == 200 || statusCode == 400) {
|
||||
return JsonSerialization.readValue(EntityUtils.toString(response.getEntity()), AccessTokenResponse.class);
|
||||
} else {
|
||||
throw new RuntimeException("Unknown error!");
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ import org.jboss.arquillian.container.test.api.TargetsContainer;
|
|||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
|
@ -59,6 +59,7 @@ import org.keycloak.testsuite.ProfileAssume;
|
|||
import org.keycloak.testsuite.adapter.AbstractAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
import org.keycloak.testsuite.broker.BrokerTestTools;
|
||||
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
||||
|
@ -84,6 +85,7 @@ import java.net.URL;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.keycloak.testsuite.Assert.assertEquals;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
|
||||
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||
|
||||
|
@ -187,7 +189,6 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
|
||||
testRealms.add(realm);
|
||||
|
||||
|
||||
realm = new RealmRepresentation();
|
||||
realm.setRealm(PARENT_IDP);
|
||||
realm.setEnabled(true);
|
||||
|
@ -200,17 +201,36 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
parentApp.setFullScopeAllowed(true);
|
||||
realm.setClients(new LinkedList<>());
|
||||
realm.getClients().add(parentApp);
|
||||
|
||||
testRealms.add(realm);
|
||||
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() {
|
||||
@Before
|
||||
public void enableFeature() throws Exception {
|
||||
try {
|
||||
addIdpUser();
|
||||
addChildUser();
|
||||
createBroker();
|
||||
|
||||
checkFeature(Response.Status.NOT_IMPLEMENTED.getStatusCode());
|
||||
Response response = testingClient.testing().enableFeature(Profile.Feature.TOKEN_EXCHANGE.toString());
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
|
||||
checkFeature(Response.Status.OK.getStatusCode());
|
||||
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.TOKEN_EXCHANGE);
|
||||
} catch (Exception e) {
|
||||
disableFeature();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void disableFeature() throws Exception {
|
||||
Response response = testingClient.testing().disableFeature(Profile.Feature.TOKEN_EXCHANGE.toString());
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
|
||||
checkFeature(Response.Status.NOT_IMPLEMENTED.getStatusCode());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void addIdpUser() {
|
||||
RealmResource realm = adminClient.realms().realm(PARENT_IDP);
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
|
@ -226,8 +246,6 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
|
||||
private String childUserId = null;
|
||||
|
||||
|
||||
@Before
|
||||
public void addChildUser() {
|
||||
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
|
@ -307,7 +325,7 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
idp.setStoreToken(true);
|
||||
realm.updateIdentityProvider(idp);
|
||||
}
|
||||
@Before
|
||||
|
||||
public void createBroker() {
|
||||
createParentChild();
|
||||
testingClient.server().run(BrokerLinkAndTokenExchangeTest::setupRealm);
|
||||
|
@ -319,9 +337,9 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
|
||||
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void testAccountLink() throws Exception {
|
||||
testingClient.server().run(BrokerLinkAndTokenExchangeTest::turnOnTokenStore);
|
||||
|
||||
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
|
||||
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
|
||||
Assert.assertTrue(links.isEmpty());
|
||||
|
@ -349,12 +367,11 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
Assert.assertFalse(links.isEmpty());
|
||||
|
||||
|
||||
|
||||
// do exchange
|
||||
|
||||
String accessToken = oauth.doGrantAccessTokenRequest(CHILD_IDP, "child", "password", null, ClientApp.DEPLOYMENT_NAME, "password").getAccessToken();
|
||||
Client httpClient = ClientBuilder.newClient();
|
||||
|
||||
try {
|
||||
WebTarget exchangeUrl = childTokenExchangeWebTarget(httpClient);
|
||||
System.out.println("Exchange url: " + exchangeUrl.getUri().toString());
|
||||
|
||||
|
@ -417,6 +434,9 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
|
||||
links = realm.users().get(childUserId).getFederatedIdentity();
|
||||
Assert.assertTrue(links.isEmpty());
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected WebTarget childTokenExchangeWebTarget(Client httpClient) {
|
||||
|
@ -443,6 +463,7 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
}
|
||||
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void testAccountLinkNoTokenStore() throws Exception {
|
||||
testingClient.server().run(BrokerLinkAndTokenExchangeTest::turnOffTokenStore);
|
||||
|
||||
|
@ -472,7 +493,6 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
Assert.assertFalse(links.isEmpty());
|
||||
|
||||
|
||||
|
||||
logoutAll();
|
||||
|
||||
|
||||
|
@ -488,6 +508,7 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void testExportImport() throws Exception {
|
||||
testExternalExchange();
|
||||
testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
|
||||
|
@ -507,53 +528,21 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
testExternalExchange();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void testExternalExchange() throws Exception {
|
||||
|
||||
RealmResource childRealm = adminClient.realms().realm(CHILD_IDP);
|
||||
|
||||
String accessToken = oauth.doGrantAccessTokenRequest(PARENT_IDP, PARENT2_USERNAME, "password", null, PARENT_CLIENT, "password").getAccessToken();
|
||||
Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
|
||||
Client httpClient = ClientBuilder.newClient();
|
||||
try {
|
||||
WebTarget exchangeUrl = childTokenExchangeWebTarget(httpClient);
|
||||
System.out.println("Exchange url: " + exchangeUrl.getUri().toString());
|
||||
|
||||
{
|
||||
IdentityProviderRepresentation rep = adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).toRepresentation();
|
||||
rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, String.valueOf(false));
|
||||
adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).update(rep);
|
||||
// test user info validation.
|
||||
Response response = exchangeUrl.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password"))
|
||||
.post(Entity.form(
|
||||
new Form()
|
||||
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP)
|
||||
checkFeature(200);
|
||||
|
||||
));
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||
String exchangedAccessToken = tokenResponse.getToken();
|
||||
Assert.assertNotNull(exchangedAccessToken);
|
||||
response.close();
|
||||
|
||||
Assert.assertEquals(1, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
|
||||
// test logout
|
||||
response = childLogoutWebTarget(httpClient)
|
||||
.queryParam("id_token_hint", exchangedAccessToken)
|
||||
.request()
|
||||
.get();
|
||||
response.close();
|
||||
|
||||
Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
|
||||
}
|
||||
IdentityProviderRepresentation rep = adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).toRepresentation();
|
||||
rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, String.valueOf(true));
|
||||
rep.getConfig().put(OIDCIdentityProviderConfig.USE_JWKS_URL, String.valueOf(true));
|
||||
|
@ -721,6 +710,9 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
));
|
||||
Assert.assertEquals(403, response.getStatus());
|
||||
}
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -736,6 +728,53 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
WaitUtils.waitForPageToLoad();
|
||||
}
|
||||
|
||||
private void checkFeature(int statusCode) throws Exception {
|
||||
String accessToken = oauth.doGrantAccessTokenRequest(PARENT_IDP, PARENT2_USERNAME, "password", null, PARENT_CLIENT, "password").getAccessToken();
|
||||
|
||||
|
||||
if (statusCode != Response.Status.NOT_IMPLEMENTED.getStatusCode()) {
|
||||
Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
}
|
||||
|
||||
Client httpClient = ClientBuilder.newClient();
|
||||
try {
|
||||
WebTarget exchangeUrl = childTokenExchangeWebTarget(httpClient);
|
||||
{
|
||||
IdentityProviderRepresentation rep = adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).toRepresentation();
|
||||
rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, String.valueOf(false));
|
||||
adminClient.realm(CHILD_IDP).identityProviders().get(PARENT_IDP).update(rep);
|
||||
// test user info validation.
|
||||
Response response = exchangeUrl.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(ClientApp.DEPLOYMENT_NAME, "password"))
|
||||
.post(Entity.form(
|
||||
new Form()
|
||||
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_ISSUER, PARENT_IDP)
|
||||
|
||||
));
|
||||
Assert.assertEquals(statusCode, response.getStatus());
|
||||
|
||||
if (statusCode != Response.Status.NOT_IMPLEMENTED.getStatusCode()) {
|
||||
AccessTokenResponse tokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||
String exchangedAccessToken = tokenResponse.getToken();
|
||||
Assert.assertNotNull(exchangedAccessToken);
|
||||
response.close();
|
||||
|
||||
Assert.assertEquals(1, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
|
||||
// test logout
|
||||
response = childLogoutWebTarget(httpClient)
|
||||
.queryParam("id_token_hint", exchangedAccessToken)
|
||||
.request()
|
||||
.get();
|
||||
response.close();
|
||||
|
||||
Assert.assertEquals(0, adminClient.realm(CHILD_IDP).getClientSessionStats().size());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue