Support for running the client using different JAX-RS Client implementations

Closes #9539

Co-authored-by: geoand <geoand@gmail.com>
This commit is contained in:
Pedro Igor
2022-03-14 11:34:17 -03:00
parent 59450948f4
commit 53bbf307b3
40 changed files with 162 additions and 172 deletions

View File

@@ -16,18 +16,19 @@
*/
package org.keycloak.admin.client;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider;
import javax.ws.rs.client.WebTarget;
import org.keycloak.admin.client.resource.BearerAuthFilter;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RealmsResource;
import org.keycloak.admin.client.resource.ServerInfoResource;
import org.keycloak.admin.client.spi.ResteasyClientProvider;
import org.keycloak.admin.client.token.TokenManager;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.net.URI;
import java.util.Iterator;
import java.util.ServiceLoader;
import static org.keycloak.OAuth2Constants.PASSWORD;
@@ -41,10 +42,45 @@ import static org.keycloak.OAuth2Constants.PASSWORD;
* @see KeycloakBuilder
*/
public class Keycloak implements AutoCloseable {
private static volatile ResteasyClientProvider CLIENT_PROVIDER = resolveResteasyClientProvider();
private static ResteasyClientProvider resolveResteasyClientProvider() {
Iterator<ResteasyClientProvider> providers = ServiceLoader.load(ResteasyClientProvider.class).iterator();
if (providers.hasNext()) {
ResteasyClientProvider provider = providers.next();
if (providers.hasNext()) {
throw new IllegalArgumentException("Multiple " + ResteasyClientProvider.class + " implementations found");
}
return provider;
}
return createDefaultResteasyClientProvider();
}
private static ResteasyClientProvider createDefaultResteasyClientProvider() {
try {
return (ResteasyClientProvider) Keycloak.class.getClassLoader().loadClass("org.keycloak.admin.client.spi.ResteasyClientClassicProvider").getDeclaredConstructor().newInstance();
} catch (Exception cause) {
throw new RuntimeException("Could not instantiate default client provider", cause);
}
}
public static void setClientProvider(ResteasyClientProvider provider) {
CLIENT_PROVIDER = provider;
}
public static ResteasyClientProvider getClientProvider() {
return CLIENT_PROVIDER;
}
private final Config config;
private final TokenManager tokenManager;
private final String authToken;
private final ResteasyWebTarget target;
private final WebTarget target;
private final Client client;
private boolean closed = false;
@@ -54,27 +90,19 @@ public class Keycloak implements AutoCloseable {
authToken = authtoken;
tokenManager = authtoken == null ? new TokenManager(config, client) : null;
target = (ResteasyWebTarget) client.target(config.getServerUrl());
target = client.target(config.getServerUrl());
target.register(newAuthFilter());
}
private static Client newRestEasyClient(Object customJacksonProvider, SSLContext sslContext, boolean disableTrustManager) {
return CLIENT_PROVIDER.newRestEasyClient(customJacksonProvider, sslContext, disableTrustManager);
}
private BearerAuthFilter newAuthFilter() {
return authToken != null ? new BearerAuthFilter(authToken) : new BearerAuthFilter(tokenManager);
}
private static Client newRestEasyClient(ResteasyJackson2Provider customJacksonProvider, SSLContext sslContext, boolean disableTrustManager) {
ClientBuilder clientBuilder = ClientBuilderWrapper.create(sslContext, disableTrustManager);
if (customJacksonProvider != null) {
clientBuilder.register(customJacksonProvider, 100);
} else {
clientBuilder.register(JacksonProvider.class, 100);
}
return clientBuilder.build();
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, ResteasyJackson2Provider customJacksonProvider, boolean disableTrustManager, String authToken) {
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, Object customJacksonProvider, boolean disableTrustManager, String authToken) {
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, newRestEasyClient(customJacksonProvider, sslContext, disableTrustManager), authToken);
}
@@ -86,7 +114,7 @@ public class Keycloak implements AutoCloseable {
return getInstance(serverUrl, realm, username, password, clientId, clientSecret, sslContext, null, false, null);
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, ResteasyJackson2Provider customJacksonProvider) {
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, Object customJacksonProvider) {
return getInstance(serverUrl, realm, username, password, clientId, clientSecret, sslContext, customJacksonProvider, false, null);
}
@@ -107,7 +135,7 @@ public class Keycloak implements AutoCloseable {
}
public RealmsResource realms() {
return target.proxy(RealmsResource.class);
return CLIENT_PROVIDER.targetProxy(target, RealmsResource.class);
}
public RealmResource realm(String realmName) {
@@ -115,7 +143,7 @@ public class Keycloak implements AutoCloseable {
}
public ServerInfoResource serverInfo() {
return target.proxy(ServerInfoResource.class);
return CLIENT_PROVIDER.targetProxy(target, ServerInfoResource.class);
}
public TokenManager tokenManager() {
@@ -132,7 +160,8 @@ public class Keycloak implements AutoCloseable {
* @return
*/
public <T> T proxy(Class<T> proxyClass, URI absoluteURI) {
return ((ResteasyWebTarget) client.target(absoluteURI)).register(newAuthFilter()).proxy(proxyClass);
WebTarget register = client.target(absoluteURI).register(newAuthFilter());
return CLIENT_PROVIDER.targetProxy(register, proxyClass);
}
/**

View File

@@ -17,15 +17,14 @@
package org.keycloak.admin.client;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import static org.keycloak.OAuth2Constants.CLIENT_CREDENTIALS;
import static org.keycloak.OAuth2Constants.PASSWORD;
import javax.ws.rs.client.Client;
/**
* Provides a {@link Keycloak} client builder with the ability to customize the underlying
* {@link ResteasyClient RESTEasy client} used to communicate with the Keycloak server.
* {@link javax.ws.rs.client.Client RESTEasy client} used to communicate with the Keycloak server.
* <p>
* <p>Example usage with a connection pool size of 20:</p>
* <pre>
@@ -51,7 +50,7 @@ import static org.keycloak.OAuth2Constants.PASSWORD;
* </pre>
*
* @author Scott Rossillo
* @see ResteasyClientBuilder
* @see javax.ws.rs.client.Client
*/
public class KeycloakBuilder {
private String serverUrl;
@@ -61,7 +60,7 @@ public class KeycloakBuilder {
private String clientId;
private String clientSecret;
private String grantType;
private ResteasyClient resteasyClient;
private Client resteasyClient;
private String authorization;
public KeycloakBuilder serverUrl(String serverUrl) {
@@ -100,7 +99,7 @@ public class KeycloakBuilder {
return this;
}
public KeycloakBuilder resteasyClient(ResteasyClient resteasyClient) {
public KeycloakBuilder resteasyClient(Client resteasyClient) {
this.resteasyClient = resteasyClient;
return this;
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
/**
@@ -45,6 +44,5 @@ public interface AggregatePoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
AggregatePolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -38,7 +37,6 @@ public interface AggregatePolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
AggregatePolicyRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface AggregatePolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -17,8 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -34,7 +32,6 @@ public interface AttackDetectionResource {
@GET
@Path("brute-force/users/{userId}")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId);

View File

@@ -17,8 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.keycloak.representations.KeyStoreConfig;
import org.keycloak.representations.idm.CertificateRepresentation;
@@ -41,7 +39,6 @@ public interface ClientAttributeCertificateResource {
* @return
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
CertificateRepresentation getKeyInfo();
@@ -51,7 +48,6 @@ public interface ClientAttributeCertificateResource {
* @return
*/
@POST
@NoCache
@Path("generate")
@Produces(MediaType.APPLICATION_JSON)
CertificateRepresentation generate();
@@ -66,7 +62,7 @@ public interface ClientAttributeCertificateResource {
@Path("upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
CertificateRepresentation uploadJks(MultipartFormDataOutput output);
CertificateRepresentation uploadJks(Object output);
/**
* Upload only certificate, not private key
@@ -78,7 +74,7 @@ public interface ClientAttributeCertificateResource {
@Path("upload-certificate")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
CertificateRepresentation uploadJksCertificate(MultipartFormDataOutput output);
CertificateRepresentation uploadJksCertificate(Object output);
/**
* Get a keystore file for the client, containing private key and public certificate
@@ -87,7 +83,6 @@ public interface ClientAttributeCertificateResource {
* @return
*/
@POST
@NoCache
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)
@@ -103,7 +98,6 @@ public interface ClientAttributeCertificateResource {
* @return
*/
@POST
@NoCache
@Path("/generate-and-download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)

View File

@@ -6,7 +6,6 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
/**
@@ -15,7 +14,6 @@ import org.keycloak.representations.idm.ClientPoliciesRepresentation;
public interface ClientPoliciesPoliciesResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
ClientPoliciesRepresentation getPolicies();

View File

@@ -7,7 +7,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.ClientProfilesRepresentation;
/**
@@ -16,7 +15,6 @@ import org.keycloak.representations.idm.ClientProfilesRepresentation;
public interface ClientPoliciesProfilesResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
ClientProfilesRepresentation getProfiles(@QueryParam("include-global-profiles") Boolean includeGlobalProfiles);

View File

@@ -26,8 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
/**
@@ -46,6 +44,5 @@ public interface ClientPoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ClientPolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -38,7 +37,6 @@ public interface ClientPolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ClientPolicyRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface ClientPolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -24,7 +24,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.ComponentTypeRepresentation;
/**
@@ -34,7 +33,6 @@ public interface ClientRegistrationPolicyResource {
@Path("providers")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ComponentTypeRepresentation> getProviders();
}

View File

@@ -31,7 +31,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
@@ -115,7 +114,6 @@ public interface ClientResource {
ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix);
@GET
@NoCache
@Path("installation/providers/{providerId}")
String getInstallationProvider(@PathParam("providerId") String providerId);
@@ -188,7 +186,6 @@ public interface ClientResource {
@Path("/service-account-user")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
UserRepresentation getServiceAccountUser();

View File

@@ -25,7 +25,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ClientScopePolicyRepresentation;
/**
@@ -41,6 +40,5 @@ public interface ClientScopePoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ClientScopePolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,9 +26,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -46,6 +44,5 @@ public interface GroupPoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
GroupPolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,11 +26,9 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -39,7 +37,6 @@ public interface GroupPolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
GroupPolicyRepresentation toRepresentation();
@PUT
@@ -52,19 +49,16 @@ public interface GroupPolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -17,7 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.ManagementPermissionRepresentation;
@@ -71,7 +70,6 @@ public interface GroupResource {
* @return
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
GroupRepresentation toRepresentation();
@@ -96,7 +94,6 @@ public interface GroupResource {
*/
@POST
@Path("children")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
Response subGroup(GroupRepresentation rep);
@@ -113,7 +110,6 @@ public interface GroupResource {
* @return Returns a max size of 100 users
*/
@GET
@NoCache
@Path("/members")
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> members();
@@ -128,7 +124,6 @@ public interface GroupResource {
* @return
*/
@GET
@NoCache
@Path("/members")
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> members(@QueryParam("first") Integer firstResult,
@@ -147,7 +142,6 @@ public interface GroupResource {
* @return
*/
@GET
@NoCache
@Path("/members")
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> members(@QueryParam("first") Integer firstResult,

View File

@@ -17,7 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.GroupRepresentation;
import javax.ws.rs.*;
@@ -37,7 +36,6 @@ public interface GroupsResource {
* @return A list containing all groups.
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<GroupRepresentation> groups();
@@ -48,7 +46,6 @@ public interface GroupsResource {
* @return A list containing the slice of all groups.
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
List<GroupRepresentation> groups(@QueryParam("first") Integer first, @QueryParam("max") Integer max);
@@ -61,7 +58,6 @@ public interface GroupsResource {
* @return A list containing the slice of all groups.
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
List<GroupRepresentation> groups(@QueryParam("search") String search,
@@ -77,7 +73,6 @@ public interface GroupsResource {
* @return A list containing the slice of all groups.
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
List<GroupRepresentation> groups(@QueryParam("search") String search,
@@ -89,7 +84,6 @@ public interface GroupsResource {
* @return A map containing key "count" with number of groups as value.
*/
@GET
@NoCache
@Path("count")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@@ -101,7 +95,6 @@ public interface GroupsResource {
* @return A map containing key "count" with number of groups as value which matching with search.
*/
@GET
@NoCache
@Path("count")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@@ -113,7 +106,6 @@ public interface GroupsResource {
* @return A map containing key "count" with number of top level groups.
*/
@GET
@NoCache
@Path("count")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)

View File

@@ -17,7 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import javax.ws.rs.Consumes;
@@ -58,7 +57,7 @@ public interface IdentityProvidersResource {
@Path("import-config")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
Map<String, String> importFrom(MultipartFormDataOutput data);
Map<String, String> importFrom(Object data);
@POST
@Path("import-config")

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
/**
@@ -45,6 +44,5 @@ public interface JSPoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
JSPolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,11 +26,9 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -39,7 +37,6 @@ public interface JSPolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
JSPolicyRepresentation toRepresentation();
@PUT
@@ -52,19 +49,16 @@ public interface JSPolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -16,7 +16,6 @@
*/
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
@@ -49,17 +48,14 @@ public interface PoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
PolicyRepresentation findByName(@QueryParam("name") String name);
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> policies();
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> policies(@QueryParam("policyId") String id,
@QueryParam("name") String name,
@QueryParam("type") String type,
@@ -74,7 +70,6 @@ public interface PoliciesResource {
@Path("providers")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyProviderRepresentation> policyProviders();
@POST

View File

@@ -27,7 +27,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -39,12 +38,10 @@ public interface PolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
PolicyRepresentation toRepresentation();
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
PolicyRepresentation toRepresentation(@QueryParam("fields") String fields);
@PUT
@@ -57,24 +54,20 @@ public interface PolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/scopes")
@GET
@Produces("application/json")
@NoCache
List<ScopeRepresentation> scopes();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -17,8 +17,6 @@
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
@@ -94,7 +92,7 @@ public interface RealmResource {
@Path("client-description-converter")
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
ClientRepresentation convertClientDescription(String description);
@@ -121,7 +119,6 @@ public interface RealmResource {
@Path("events")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<EventRepresentation> getEvents(@QueryParam("type") List<String> types, @QueryParam("client") String client,
@QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
@@ -167,7 +164,6 @@ public interface RealmResource {
@GET
@Path("group-by-path/{path: .*}")
@NoCache
@Produces(MediaType.APPLICATION_JSON)
GroupRepresentation getGroupByPath(@PathParam("path") String path);
@@ -221,7 +217,6 @@ public interface RealmResource {
@Path("testLDAPConnection")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@NoCache
@Deprecated
Response testLDAPConnection(@FormParam("action") String action, @FormParam("connectionUrl") String connectionUrl,
@FormParam("bindDn") String bindDn, @FormParam("bindCredential") String bindCredential,
@@ -230,26 +225,22 @@ public interface RealmResource {
@Path("testLDAPConnection")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
Response testLDAPConnection(TestLdapConnectionRepresentation config);
@POST
@Path("ldap-server-capabilities")
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
@Produces(javax.ws.rs.core.MediaType.APPLICATION_JSON)
List<LDAPCapabilityRepresentation> ldapServerCapabilities(TestLdapConnectionRepresentation config);
@Path("testSMTPConnection")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Deprecated
Response testSMTPConnection(@FormParam("config") String config);
@Path("testSMTPConnection")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
Response testSMTPConnection(Map<String, String> config);

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -38,7 +37,6 @@ public interface ResourcePermissionResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourcePermissionRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface ResourcePermissionResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
/**
@@ -45,6 +44,5 @@ public interface ResourcePermissionsResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourcePermissionRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -18,7 +18,6 @@ package org.keycloak.admin.client.resource;
import java.util.List;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -37,7 +36,6 @@ public interface ResourceResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourceRepresentation toRepresentation();
@PUT
@@ -49,7 +47,6 @@ public interface ResourceResource {
@Path("permissions")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<PolicyRepresentation> permissions();
}

View File

@@ -18,7 +18,6 @@ package org.keycloak.admin.client.resource;
import java.util.List;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -37,7 +36,6 @@ public interface ResourceScopeResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopeRepresentation toRepresentation();
@PUT

View File

@@ -16,7 +16,6 @@
*/
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import javax.ws.rs.Consumes;
@@ -44,13 +43,11 @@ public interface ResourceScopesResource {
ResourceScopeResource scope(@PathParam("id") String id);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ScopeRepresentation> scopes();
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopeRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -16,7 +16,6 @@
*/
package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import javax.ws.rs.Consumes;
@@ -44,7 +43,6 @@ public interface ResourcesResource {
ResourceResource resource(@PathParam("id") String id);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> find(@QueryParam("name") String name,
@QueryParam("uri") String uri,
@@ -55,17 +53,14 @@ public interface ResourcesResource {
@QueryParam("max") Integer maxResult);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name, @QueryParam("owner") String owner);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> resources();
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
@@ -45,6 +44,5 @@ public interface RolePoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
RolePolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
@@ -38,7 +37,6 @@ public interface RolePolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
RolePolicyRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface RolePolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
@@ -39,7 +38,6 @@ public interface ScopePermissionResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopePermissionRepresentation toRepresentation();
@PUT
@@ -52,24 +50,20 @@ public interface ScopePermissionResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
@Path("/scopes")
@GET
@Produces("application/json")
@NoCache
List<ScopeRepresentation> scopes();
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
/**
@@ -45,6 +44,5 @@ public interface ScopePermissionsResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopePermissionRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
/**
@@ -45,6 +44,5 @@ public interface TimePoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
TimePolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.TimePolicyRepresentation;
@@ -38,7 +37,6 @@ public interface TimePolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
TimePolicyRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface TimePolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
/**
@@ -45,6 +44,5 @@ public interface UserPoliciesResource {
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
UserPolicyRepresentation findByName(@QueryParam("name") String name);
}

View File

@@ -26,7 +26,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
@@ -38,7 +37,6 @@ public interface UserPolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
UserPolicyRepresentation toRepresentation();
@PUT
@@ -51,19 +49,16 @@ public interface UserPolicyResource {
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.spi;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.keycloak.admin.client.ClientBuilderWrapper;
import org.keycloak.admin.client.JacksonProvider;
/**
* An implementation of {@link ResteasyClientProvider} based on RESTEasy classic.
*/
public class ResteasyClientClassicProvider implements ResteasyClientProvider {
@Override
public Client newRestEasyClient(Object customJacksonProvider, SSLContext sslContext, boolean disableTrustManager) {
ClientBuilder clientBuilder = ClientBuilderWrapper.create(sslContext, disableTrustManager);
if (customJacksonProvider != null) {
clientBuilder.register(customJacksonProvider, 100);
} else {
clientBuilder.register(JacksonProvider.class, 100);
}
return clientBuilder.build();
}
@Override
public <R> R targetProxy(WebTarget client, Class<R> targetClass) {
return ResteasyWebTarget.class.cast(client).proxy(targetClass);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.spi;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
/**
* An SPI for using the JAX-RS Client API regardless of the underlying stack.
*/
public interface ResteasyClientProvider {
/**
* Creates a new {@link Client}.
*
* @param messageHandler a {@link javax.ws.rs.ext.MessageBodyReader} and/or {@link javax.ws.rs.ext.MessageBodyWriter} instance.
* @param sslContext an optional {@link SSLContext}
* @param disableTrustManager if the client should not validate the server certificates when using TLS
* @return
*/
Client newRestEasyClient(Object messageHandler, SSLContext sslContext, boolean disableTrustManager);
/**
* Creates a implementation-specific proxy for a given {@code targetClass}.
*
* @param target the {@link WebTarget} instance
* @param targetClass the JAX-RS client resource class
* @return an instance of {@code targetClass}
*/
<R> R targetProxy(WebTarget target, Class<R> targetClass);
}

View File

@@ -17,9 +17,11 @@
package org.keycloak.admin.client.token;
import javax.ws.rs.client.WebTarget;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.keycloak.admin.client.Config;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.BasicAuthFilter;
import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessTokenResponse;
@@ -49,11 +51,11 @@ public class TokenManager {
public TokenManager(Config config, Client client) {
this.config = config;
ResteasyWebTarget target = (ResteasyWebTarget) client.target(config.getServerUrl());
WebTarget target = client.target(config.getServerUrl());
if (!config.isPublicClient()) {
target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
}
this.tokenService = target.proxy(TokenService.class);
this.tokenService = Keycloak.getClientProvider().targetProxy(target, TokenService.class);
this.accessTokenGrantType = config.getGrantType();
if (CLIENT_CREDENTIALS.equals(accessTokenGrantType) && config.isPublicClient()) {