Merge pull request #2324 from ssilvert/client-tests

KEYCLOAK-2535: ClientResource endpoint tests
This commit is contained in:
Stian Thorgersen 2016-03-07 06:13:55 +01:00
commit 56c3d53a24
17 changed files with 710 additions and 258 deletions

View file

@ -0,0 +1,80 @@
/*
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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 org.keycloak.representations;
/**
* Configuration of KeyStore.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class KeyStoreConfig {
protected Boolean realmCertificate;
protected String storePassword;
protected String keyPassword;
protected String keyAlias;
protected String realmAlias;
protected String format;
public Boolean isRealmCertificate() {
return realmCertificate;
}
public void setRealmCertificate(Boolean realmCertificate) {
this.realmCertificate = realmCertificate;
}
public String getStorePassword() {
return storePassword;
}
public void setStorePassword(String storePassword) {
this.storePassword = storePassword;
}
public String getKeyPassword() {
return keyPassword;
}
public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
}
public String getKeyAlias() {
return keyAlias;
}
public void setKeyAlias(String keyAlias) {
this.keyAlias = keyAlias;
}
public String getRealmAlias() {
return realmAlias;
}
public void setRealmAlias(String realmAlias) {
this.realmAlias = realmAlias;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}

View file

@ -55,6 +55,11 @@
<artifactId>resteasy-client</artifactId> <artifactId>resteasy-client</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId> <artifactId>resteasy-jackson2-provider</artifactId>

View file

@ -0,0 +1,114 @@
/*
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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 org.keycloak.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.keycloak.representations.KeyStoreConfig;
import org.keycloak.representations.idm.CertificateRepresentation;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public interface ClientAttributeCertificateResource {
/**
* Get key info
*
* @return
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation getKeyInfo();
/**
* Generate a new certificate with new key pair
*
* @return
*/
@POST
@NoCache
@Path("generate")
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation generate();
/**
* Upload certificate and eventually private key
*
* @param uriInfo
* @param input
* @return
*/
@POST
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input);
/**
* Upload only certificate, not private key
*
* @param uriInfo
* @param input
* @return
*/
@POST
@Path("upload-certificate")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJksCertificate(@Context final UriInfo uriInfo, MultipartFormDataInput input);
/**
* Get a keystore file for the client, containing private key and public certificate
*
* @param config Keystore configuration as JSON
* @return
*/
@POST
@NoCache
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)
public byte[] getKeystore(final KeyStoreConfig config);
/**
* Generate a new keypair and certificate, and get the private key file
*
* Generates a keypair and certificate and serves the private key in a specified keystore format.
* Only generated public certificate is saved in Keycloak DB - the private key is not.
*
* @param config Keystore configuration as JSON
* @return
*/
@POST
@NoCache
@Path("/generate-and-download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)
public byte[] generateAndGetKeystore(final KeyStoreConfig config);
}

View file

@ -34,7 +34,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* @author rodrigo.sasaki@icarros.com.br * @author rodrigo.sasaki@icarros.com.br
@ -55,21 +54,6 @@ public interface ClientResource {
@DELETE @DELETE
public void remove(); public void remove();
@GET
@Path("allowed-origins")
@Produces(MediaType.APPLICATION_JSON)
public Set<String> getAllowedOrigins();
@PUT
@Path("allowed-origins")
@Consumes(MediaType.APPLICATION_JSON)
public void updateAllowedOrigins(Set<String> allowedOrigins);
@DELETE
@Path("allowed-origins")
@Consumes(MediaType.APPLICATION_JSON)
public void removeAllowedOrigins(Set<String> originsToRemove);
@POST @POST
@Path("client-secret") @Path("client-secret")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -80,19 +64,31 @@ public interface ClientResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public CredentialRepresentation getSecret(); public CredentialRepresentation getSecret();
/**
* Generate a new registration access token for the client
*
* @return
*/
@Path("registration-access-token")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public ClientRepresentation regenerateRegistrationAccessToken();
/**
* Get representation of certificate resource
*
* @param attributePrefix
* @return
*/
@Path("certificates/{attr}")
public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix);
@GET @GET
@NoCache @NoCache
@Path("installation/providers/{providerId}") @Path("installation/providers/{providerId}")
public String getInstallationProvider(@PathParam("providerId") String providerId); public String getInstallationProvider(@PathParam("providerId") String providerId);
@POST
@Path("logout-all")
public void logoutAllUsers();
@POST
@Path("logout-user/{username}")
public void logoutUser(@PathParam("username") String username);
@Path("session-count") @Path("session-count")
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)

View file

@ -28,6 +28,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.KeyStoreConfig;
import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ErrorResponseException;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
@ -225,64 +226,6 @@ public class ClientAttributeCertificateResource {
return info; return info;
} }
public static class KeyStoreConfig {
protected Boolean realmCertificate;
protected String storePassword;
protected String keyPassword;
protected String keyAlias;
protected String realmAlias;
protected String format;
public Boolean isRealmCertificate() {
return realmCertificate;
}
public void setRealmCertificate(Boolean realmCertificate) {
this.realmCertificate = realmCertificate;
}
public String getStorePassword() {
return storePassword;
}
public void setStorePassword(String storePassword) {
this.storePassword = storePassword;
}
public String getKeyPassword() {
return keyPassword;
}
public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
}
public String getKeyAlias() {
return keyAlias;
}
public void setKeyAlias(String keyAlias) {
this.keyAlias = keyAlias;
}
public String getRealmAlias() {
return realmAlias;
}
public void setRealmAlias(String realmAlias) {
this.realmAlias = realmAlias;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
/** /**
* Get a keystore file for the client, containing private key and public certificate * Get a keystore file for the client, containing private key and public certificate
* *

View file

@ -45,7 +45,6 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponse;
import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -62,12 +61,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
@ -172,54 +170,6 @@ public class ClientResource {
return provider.generateInstallation(session, realm, client, keycloak.getBaseUri(uriInfo)); return provider.generateInstallation(session, realm, client, keycloak.getBaseUri(uriInfo));
} }
/**
* Get keycloak.json file
*
* this method is deprecated, see getInstallationProvider
*
* Returns a keycloak.json file to be used to configure the adapter of the specified client.
*
* @return
* @throws IOException
*/
@Deprecated
@GET
@NoCache
@Path("installation/json")
@Produces(MediaType.APPLICATION_JSON)
public String getInstallation() throws IOException {
auth.requireView();
ClientManager clientManager = new ClientManager(new RealmManager(session));
Object rep = clientManager.toInstallationRepresentation(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
// TODO Temporary solution to pretty-print
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
}
/**
* Get adapter configuration XML for JBoss / Wildfly Keycloak subsystem
*
* this method is deprecated, see getInstallationProvider
*
* Returns XML that can be included in the JBoss / Wildfly Keycloak subsystem to configure the adapter of that client.
*
* @return
* @throws IOException
*/
@Deprecated
@GET
@NoCache
@Path("installation/jboss")
@Produces(MediaType.TEXT_PLAIN)
public String getJBossInstallation() throws IOException {
auth.requireView();
ClientManager clientManager = new ClientManager(new RealmManager(session));
return clientManager.toJBossSubsystemConfig(realm, client, getKeycloakApplication().getBaseUri(uriInfo));
}
/** /**
* Delete the client * Delete the client
* *
@ -306,64 +256,6 @@ public class ClientResource {
return new RoleContainerResource(uriInfo, realm, auth, client, adminEvent); return new RoleContainerResource(uriInfo, realm, auth, client, adminEvent);
} }
/**
* Get allowed origins
*
* This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this client.
*
* @return
*/
@Path("allowed-origins")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<String> getAllowedOrigins()
{
auth.requireView();
return client.getWebOrigins();
}
/**
* Update allowed origins
*
* This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this client.
*
* @param allowedOrigins
*/
@Path("allowed-origins")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateAllowedOrigins(Set<String> allowedOrigins)
{
auth.requireManage();
client.setWebOrigins(allowedOrigins);
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(client).success();
}
/**
* Delete the specified origins from current allowed origins
*
* This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this client.
*
* @param allowedOrigins List of origins to delete
*/
@Path("allowed-origins")
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteAllowedOrigins(Set<String> allowedOrigins)
{
auth.requireManage();
for (String origin : allowedOrigins) {
client.removeWebOrigin(origin);
}
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
}
/** /**
* Get a user dedicated to the service account * Get a user dedicated to the service account
* *
@ -507,41 +399,6 @@ public class ClientResource {
return sessions; return sessions;
} }
/**
* Logout all sessions
*
* If the client has an admin URL, invalidate all sessions associated with that client directly.
*
*/
@Path("logout-all")
@POST
public GlobalRequestResult logoutAll() {
auth.requireManage();
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
return new ResourceAdminManager(session).logoutClient(uriInfo.getRequestUri(), realm, client);
}
/**
* Logout the user by username
*
* If the client has an admin URL, invalidate the sessions for a particular user directly.
*
*/
@Path("logout-user/{username}")
@POST
public void logout(final @PathParam("username") String username) {
auth.requireManage();
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
new ResourceAdminManager(session).logoutUserFromClient(uriInfo.getRequestUri(), realm, client, user);
}
/** /**
* Register a cluster node with the client * Register a cluster node with the client
* *

View file

@ -51,8 +51,14 @@ import org.keycloak.provider.Spi;
import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation; import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
import org.keycloak.representations.info.*;
import org.keycloak.broker.social.SocialIdentityProvider; import org.keycloak.broker.social.SocialIdentityProvider;
import org.keycloak.representations.info.ClientInstallationRepresentation;
import org.keycloak.representations.info.MemoryInfoRepresentation;
import org.keycloak.representations.info.ProviderRepresentation;
import org.keycloak.representations.info.ServerInfoRepresentation;
import org.keycloak.representations.info.SpiInfoRepresentation;
import org.keycloak.representations.info.SystemInfoRepresentation;
import org.keycloak.representations.info.ThemeInfoRepresentation;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>

View file

@ -23,22 +23,29 @@ import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After; import org.junit.After;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.adapter.page.SessionPortal; import org.keycloak.testsuite.adapter.page.SessionPortal;
import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
import org.keycloak.testsuite.auth.page.account.Sessions; import org.keycloak.testsuite.auth.page.account.Sessions;
import org.keycloak.testsuite.auth.page.login.Login; import org.keycloak.testsuite.auth.page.login.Login;
import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
import org.keycloak.testsuite.util.SecondBrowser; import org.keycloak.testsuite.util.SecondBrowser;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
@ -156,9 +163,11 @@ public abstract class AbstractSessionServletAdapterTest extends AbstractServlets
public void testAdminApplicationLogout() { public void testAdminApplicationLogout() {
// login as bburke // login as bburke
loginAndCheckSession(driver, testRealmLoginPage); loginAndCheckSession(driver, testRealmLoginPage);
// logout mposolda with admin client // logout mposolda with admin client
findClientResourceByClientId(testRealmResource(), "session-portal") UserRepresentation mposolda = testRealmResource().users().search("mposolda", null, null, null, null, null).get(0);
.logoutUser("mposolda"); testRealmResource().users().get(mposolda.getId()).logout();
// bburke should be still logged with original httpSession in our browser window // bburke should be still logged with original httpSession in our browser window
sessionPortalPage.navigateTo(); sessionPortalPage.navigateTo();
assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString()); assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());

View file

@ -17,10 +17,12 @@
package org.keycloak.testsuite.admin.client; package org.keycloak.testsuite.admin.client;
import java.util.List;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.AbstractAuthTest;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
@ -34,29 +36,35 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
return testRealmResource().toRepresentation(); return testRealmResource().toRepresentation();
} }
protected void createOidcClient(String name) { // returns UserRepresentation retrieved from server, with all fields, including id
protected UserRepresentation getFullUserRep(String userName) {
List<UserRepresentation> results = testRealmResource().users().search(userName, null, null, null, null, null);
if (results.size() != 1) throw new RuntimeException("Did not find single user with username " + userName);
return results.get(0);
}
protected String createOidcClient(String name) {
ClientRepresentation clientRep = new ClientRepresentation(); ClientRepresentation clientRep = new ClientRepresentation();
clientRep.setClientId(name); clientRep.setClientId(name);
clientRep.setName(name); clientRep.setName(name);
clientRep.setRootUrl("foo"); clientRep.setRootUrl("foo");
clientRep.setProtocol("openid-connect"); clientRep.setProtocol("openid-connect");
createClient(clientRep); return createClient(clientRep);
} }
protected void createSamlClient(String name) { protected String createSamlClient(String name) {
ClientRepresentation clientRep = new ClientRepresentation(); ClientRepresentation clientRep = new ClientRepresentation();
clientRep.setClientId(name); clientRep.setClientId(name);
clientRep.setName(name); clientRep.setName(name);
clientRep.setProtocol("saml"); clientRep.setProtocol("saml");
clientRep.setAdminUrl("samlEndpoint"); clientRep.setAdminUrl("samlEndpoint");
createClient(clientRep); return createClient(clientRep);
} }
protected void createClient(ClientRepresentation clientRep) { protected String createClient(ClientRepresentation clientRep) {
Response resp = testRealmResource().clients().create(clientRep); Response resp = testRealmResource().clients().create(clientRep);
// for some reason, findAll() will later fail unless readEntity is called here resp.close();
resp.readEntity(String.class); return ApiUtil.getCreatedId(resp);
//testRealmResource().clients().findAll();
} }
protected ClientRepresentation findClientRepresentation(String name) { protected ClientRepresentation findClientRepresentation(String name) {
@ -69,4 +77,8 @@ public abstract class AbstractClientTest extends AbstractAuthTest {
return ApiUtil.findClientResourceByName(testRealmResource(), name); return ApiUtil.findClientResourceByName(testRealmResource(), name);
} }
protected ClientResource findClientResourceById(String id) {
return ApiUtil.findClientResourceByClientId(testRealmResource(), id);
}
} }

View file

@ -0,0 +1,266 @@
/*
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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 org.keycloak.testsuite.admin.client;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.testsuite.admin.ApiUtil;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class ClientProtocolMapperTest extends AbstractClientTest {
private ClientResource oidcClientRsc;
private ProtocolMappersResource oidcMappersRsc;
private ClientResource samlClientRsc;
private ProtocolMappersResource samlMappersRsc;
private Map<String, List<ProtocolMapperRepresentation>> builtinMappers = null;
@Before
public void init() {
createOidcClient("oidcMapperClient");
oidcClientRsc = findClientResource("oidcMapperClient");
oidcMappersRsc = oidcClientRsc.getProtocolMappers();
createSamlClient("samlMapperClient");
samlClientRsc = findClientResource("samlMapperClient");
samlMappersRsc = samlClientRsc.getProtocolMappers();
builtinMappers = adminClient.serverInfo().getInfo().getBuiltinProtocolMappers();
}
@After
public void tearDown() {
oidcClientRsc.remove();
samlClientRsc.remove();
}
private ProtocolMapperRepresentation makeMapper(String protocol, String name, String mapperType, Map<String, String> config) {
ProtocolMapperRepresentation rep = new ProtocolMapperRepresentation();
rep.setProtocol(protocol);
rep.setName(name);
rep.setProtocolMapper(mapperType);
rep.setConfig(config);
rep.setConsentRequired(true);
rep.setConsentText("Test Consent Text");
return rep;
}
private ProtocolMapperRepresentation makeSamlMapper(String name) {
Map<String, String> config = new HashMap<>();
config.put("role", "account.view-profile");
config.put("new.role.name", "new-role-name");
return makeMapper("saml", name, "saml-role-name-mapper", config);
}
private ProtocolMapperRepresentation makeOidcMapper(String name) {
Map<String, String> config = new HashMap<>();
config.put("role", "myrole");
return makeMapper("openid-connect", name, "oidc-hardcoded-role-mapper", config);
}
private void assertEqualMappers(ProtocolMapperRepresentation original, ProtocolMapperRepresentation created) {
assertNotNull(created);
assertEquals(original.getName(), created.getName());
assertEquals(original.getConfig(), created.getConfig());
assertEquals(original.getConsentText(), created.getConsentText());
assertEquals(original.isConsentRequired(), created.isConsentRequired());
assertEquals(original.getProtocol(), created.getProtocol());
assertEquals(original.getProtocolMapper(), created.getProtocolMapper());
}
@Test
public void testGetMappersList() {
assertFalse(oidcMappersRsc.getMappers().isEmpty());
assertFalse(samlMappersRsc.getMappers().isEmpty());
}
private boolean containsMapper(List<ProtocolMapperRepresentation> mappers, ProtocolMapperRepresentation mapper) {
for (ProtocolMapperRepresentation listedMapper : mappers) {
if (listedMapper.getName().equals(mapper.getName())) return true;
}
return false;
}
private List<ProtocolMapperRepresentation> mappersToAdd(List<ProtocolMapperRepresentation> oldMappers,
List<ProtocolMapperRepresentation> builtins) {
List<ProtocolMapperRepresentation> mappersToAdd = new ArrayList<>();
for (ProtocolMapperRepresentation builtin : builtins) {
if (!containsMapper(oldMappers, builtin)) mappersToAdd.add(builtin);
}
return mappersToAdd;
}
private void testAddAllBuiltinMappers(ProtocolMappersResource resource, String resourceName) {
List<ProtocolMapperRepresentation> oldMappers = resource.getMappersPerProtocol(resourceName);
List<ProtocolMapperRepresentation> builtins = builtinMappers.get(resourceName);
List<ProtocolMapperRepresentation> mappersToAdd = mappersToAdd(oldMappers, builtins);
// This is used by admin console to add builtin mappers
resource.createMapper(mappersToAdd);
List<ProtocolMapperRepresentation> newMappers = resource.getMappersPerProtocol(resourceName);
assertEquals(oldMappers.size() + mappersToAdd.size(), newMappers.size());
for (ProtocolMapperRepresentation rep : mappersToAdd) {
assertTrue(containsMapper(newMappers, rep));
}
}
@Test
public void testCreateOidcMappersFromList() {
testAddAllBuiltinMappers(oidcMappersRsc, "openid-connect");
}
@Test
public void testCreateSamlMappersFromList() {
testAddAllBuiltinMappers(samlMappersRsc, "saml");
}
@Test
public void testCreateSamlProtocolMapper() {
//{"protocol":"saml",
// "config":{"role":"account.view-profile","new.role.name":"new-role-name"},
// "consentRequired":true,
// "consentText":"My consent text",
// "name":"saml-role-name-maper",
// "protocolMapper":"saml-role-name-mapper"}
ProtocolMapperRepresentation rep = makeSamlMapper("saml-role-name-mapper");
int totalMappers = samlMappersRsc.getMappers().size();
int totalSamlMappers = samlMappersRsc.getMappersPerProtocol("saml").size();
Response resp = samlMappersRsc.createMapper(rep);
resp.close();
assertEquals(totalMappers + 1, samlMappersRsc.getMappers().size());
assertEquals(totalSamlMappers + 1, samlMappersRsc.getMappersPerProtocol("saml").size());
String createdId = ApiUtil.getCreatedId(resp);
ProtocolMapperRepresentation created = samlMappersRsc.getMapperById(createdId);
assertEqualMappers(rep, created);
}
@Test
public void testCreateOidcProtocolMapper() {
//{"protocol":"openid-connect",
// "config":{"role":"myrole"},
// "consentRequired":true,
// "consentText":"My consent text",
// "name":"oidc-hardcoded-role-mapper",
// "protocolMapper":"oidc-hardcoded-role-mapper"}
ProtocolMapperRepresentation rep = makeOidcMapper("oidc-hardcoded-role-mapper");
int totalMappers = oidcMappersRsc.getMappers().size();
int totalOidcMappers = oidcMappersRsc.getMappersPerProtocol("openid-connect").size();
Response resp = oidcMappersRsc.createMapper(rep);
resp.close();
assertEquals(totalMappers + 1, oidcMappersRsc.getMappers().size());
assertEquals(totalOidcMappers + 1, oidcMappersRsc.getMappersPerProtocol("openid-connect").size());
String createdId = ApiUtil.getCreatedId(resp);
ProtocolMapperRepresentation created = oidcMappersRsc.getMapperById(createdId);//findByName(samlMappersRsc, "saml-role-name-mapper");
assertEqualMappers(rep, created);
}
@Test
public void testUpdateSamlMapper() {
ProtocolMapperRepresentation rep = makeSamlMapper("saml-role-name-mapper2");
Response resp = samlMappersRsc.createMapper(rep);
resp.close();
String createdId = ApiUtil.getCreatedId(resp);
rep.getConfig().put("role", "account.manage-account");
rep.setId(createdId);
rep.setConsentRequired(false);
samlMappersRsc.update(createdId, rep);
ProtocolMapperRepresentation updated = samlMappersRsc.getMapperById(createdId);
assertEqualMappers(rep, updated);
}
@Test
public void testUpdateOidcMapper() {
ProtocolMapperRepresentation rep = makeOidcMapper("oidc-hardcoded-role-mapper2");
Response resp = oidcMappersRsc.createMapper(rep);
resp.close();
String createdId = ApiUtil.getCreatedId(resp);
rep.getConfig().put("role", "myotherrole");
rep.setId(createdId);
rep.setConsentRequired(false);
oidcMappersRsc.update(createdId, rep);
ProtocolMapperRepresentation updated = oidcMappersRsc.getMapperById(createdId);
assertEqualMappers(rep, updated);
}
@Test (expected = NotFoundException.class)
public void testDeleteSamlMapper() {
ProtocolMapperRepresentation rep = makeSamlMapper("saml-role-name-mapper3");
Response resp = samlMappersRsc.createMapper(rep);
resp.close();
String createdId = ApiUtil.getCreatedId(resp);
samlMappersRsc.delete(createdId);
samlMappersRsc.getMapperById(createdId);
}
@Test (expected = NotFoundException.class)
public void testDeleteOidcMapper() {
ProtocolMapperRepresentation rep = makeOidcMapper("oidc-hardcoded-role-mapper3");
Response resp = oidcMappersRsc.createMapper(rep);
resp.close();
String createdId = ApiUtil.getCreatedId(resp);
oidcMappersRsc.delete(createdId);
oidcMappersRsc.getMapperById(createdId);
}
}

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.admin.client; package org.keycloak.testsuite.admin.client;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
@ -42,16 +43,17 @@ public class ClientRolesTest extends AbstractClientTest {
rolesRsc = clientRsc.roles(); rolesRsc = clientRsc.roles();
} }
@After
public void tearDown() {
clientRsc.remove();
}
private RoleRepresentation makeRole(String name) { private RoleRepresentation makeRole(String name) {
RoleRepresentation role = new RoleRepresentation(); RoleRepresentation role = new RoleRepresentation();
role.setName(name); role.setName(name);
return role; return role;
} }
/* private boolean hasRole(RolesResource rolesRsc, String name) {
return rolesRsc.get(name) != null;
}*/
private boolean hasRole(RolesResource rolesRsc, String name) { private boolean hasRole(RolesResource rolesRsc, String name) {
for (RoleRepresentation role : rolesRsc.list()) { for (RoleRepresentation role : rolesRsc.list()) {
if (role.getName().equals(name)) return true; if (role.getName().equals(name)) return true;

View file

@ -0,0 +1,74 @@
/*
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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 org.keycloak.testsuite.admin.client;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientAttributeCertificateResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class CredentialsTest extends AbstractClientTest {
private ClientResource accountClient;
@Before
public void init() {
accountClient = findClientResourceById("account");
}
@Test
public void testGetAndRegenerateSecret() {
CredentialRepresentation oldCredential = accountClient.getSecret();
CredentialRepresentation newCredential = accountClient.generateNewSecret();
assertNotNull(oldCredential);
assertNotNull(newCredential);
assertNotEquals(newCredential.getValue(), oldCredential.getValue());
assertEquals(newCredential.getValue(), accountClient.getSecret().getValue());
}
@Test
public void testGetAndRegenerateRegistrationAccessToken() {
ClientRepresentation rep = accountClient.toRepresentation();
String oldToken = rep.getRegistrationAccessToken();
String newToken = accountClient.regenerateRegistrationAccessToken().getRegistrationAccessToken();
assertNull(oldToken); // registration access token not saved in ClientRep
assertNotNull(newToken); // it's only available via regenerateRegistrationAccessToken()
assertNull(accountClient.toRepresentation().getRegistrationAccessToken());
}
@Test
public void testGetCertificateResource() {
ClientAttributeCertificateResource certRsc = accountClient.getCertficateResource("jwt.credential");
CertificateRepresentation cert = certRsc.generate();
CertificateRepresentation certFromGet = certRsc.getKeyInfo();
assertEquals(cert.getCertificate(), certFromGet.getCertificate());
assertEquals(cert.getPrivateKey(), certFromGet.getPrivateKey());
}
}

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.admin.client; package org.keycloak.testsuite.admin.client;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
@ -47,6 +48,12 @@ public class InstallationTest extends AbstractClientTest {
samlClient = findClientResource(SAML_NAME); samlClient = findClientResource(SAML_NAME);
} }
@After
public void tearDown() {
oidcClient.remove();
samlClient.remove();
}
private String authServerUrl() { private String authServerUrl() {
return AuthServerTestEnricher.getAuthServerContextRoot() + "/auth"; return AuthServerTestEnricher.getAuthServerContextRoot() + "/auth";
} }

View file

@ -0,0 +1,92 @@
/*
* Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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 org.keycloak.testsuite.admin.client;
import java.util.List;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.testsuite.auth.page.account.AccountManagement;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class SessionTest extends AbstractClientTest {
private static boolean testUserCreated = false;
@Page
protected AccountManagement testRealmAccountManagementPage;
@Before
public void init() {
// make user test user exists in test realm
if (!testUserCreated) createTestUserWithAdminClient();
testUserCreated = true;
}
@Test
public void testGetAppSessionCount() {
ClientResource accountClient = findClientResourceById("account");
int sessionCount = accountClient.getApplicationSessionCount().get("count");
assertEquals(0, sessionCount);
testRealmAccountManagementPage.navigateTo();
loginPage.form().login(testUser);
sessionCount = accountClient.getApplicationSessionCount().get("count");
assertEquals(1, sessionCount);
testRealmAccountManagementPage.signOut();
sessionCount = accountClient.getApplicationSessionCount().get("count");
assertEquals(0, sessionCount);
}
@Test
public void testGetUserSessions() {
//List<java.util.Map<String, String>> stats = this.testRealmResource().getClientSessionStats();
ClientResource account = findClientResourceById("account");
testRealmAccountManagementPage.navigateTo();
loginPage.form().login(testUser);
List<UserSessionRepresentation> sessions = account.getUserSessions(0, 5);
assertEquals(1, sessions.size());
UserSessionRepresentation rep = sessions.get(0);
UserRepresentation testUserRep = getFullUserRep(testUser.getUsername());
assertEquals(testUserRep.getId(), rep.getUserId());
assertEquals(testUserRep.getUsername(), rep.getUsername());
String clientId = account.toRepresentation().getId();
assertEquals("account", rep.getClients().get(clientId));
assertNotNull(rep.getIpAddress());
assertNotNull(rep.getLastAccess());
assertNotNull(rep.getStart());
testRealmAccountManagementPage.signOut();
}
}

View file

@ -59,6 +59,7 @@ import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.keycloak.representations.idm.UserRepresentation;
/** /**
* Tests Undertow Adapter * Tests Undertow Adapter
@ -598,7 +599,8 @@ public class AdapterTestStrategy extends ExternalResource {
// logout mposolda with admin client // logout mposolda with admin client
Keycloak keycloakAdmin = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID); Keycloak keycloakAdmin = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
ApiUtil.findClientByClientId(keycloakAdmin.realm("demo"), "session-portal").logoutUser("mposolda"); UserRepresentation mposolda = keycloakAdmin.realm("demo").users().search("mposolda", null, null, null, null, null).get(0);
keycloakAdmin.realm("demo").users().get(mposolda.getId()).logout();
// bburke should be still logged with original httpSession in our browser window // bburke should be still logged with original httpSession in our browser window
driver.navigate().to(APP_SERVER_BASE_URL + "/session-portal"); driver.navigate().to(APP_SERVER_BASE_URL + "/session-portal");

View file

@ -952,19 +952,6 @@ module.factory('ClientOfflineSessions', function($resource) {
}); });
}); });
module.factory('ClientLogoutAll', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/logout-all', {
realm : '@realm',
client : "@client"
});
});
module.factory('ClientLogoutUser', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/logout-user/:user', {
realm : '@realm',
client : "@client",
user : "@user"
});
});
module.factory('RealmLogoutAll', function($resource) { module.factory('RealmLogoutAll', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/logout-all', { return $resource(authUrl + '/admin/realms/:realm/logout-all', {
realm : '@realm' realm : '@realm'