diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index 0735a2a074..9557807237 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -35,5 +35,6 @@ event-store-mem federation-provider authenticator + rest diff --git a/examples/providers/rest/README.md b/examples/providers/rest/README.md new file mode 100644 index 0000000000..5124f88b1d --- /dev/null +++ b/examples/providers/rest/README.md @@ -0,0 +1,16 @@ +Example Realm REST Resource provider +==================================== + +To deploy copy target/hello-rest-example.jar to providers directory. Alternatively you can deploy as a module by running: + + $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api" + +Then registering the provider by editing keycloak-server.json and adding the module to the providers field: + + "providers": [ + .... + "module:org.keycloak.examples.hello-rest-example" + ], + +Then start (or restart) the server. Once started open http://localhost:8080/realms/master/hello and you should see the message _Hello master_. +You can also invoke the endpoint for other realms by replacing `master` with the realm name in the above url. \ No newline at end of file diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml new file mode 100755 index 0000000000..8666e375c7 --- /dev/null +++ b/examples/providers/rest/pom.xml @@ -0,0 +1,53 @@ + + + + + keycloak-examples-providers-parent + org.keycloak + 2.0.0.CR1-SNAPSHOT + + + Authenticator Example + + 4.0.0 + + keycloak-examples-providers-rest + jar + + + + org.keycloak + keycloak-core + provided + + + org.keycloak + keycloak-server-spi + provided + + + org.jboss.spec.javax.ws.rs + jboss-jaxrs-api_2.0_spec + + + + + hello-rest-example + + diff --git a/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java new file mode 100644 index 0000000000..aebd677221 --- /dev/null +++ b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016 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.examples.rest; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.resource.RealmResourceProvider; + +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * @author Stian Thorgersen + */ +public class HelloResourceProvider implements RealmResourceProvider { + + private KeycloakSession session; + + public HelloResourceProvider(KeycloakSession session) { + this.session = session; + } + + @Override + public Object getResource() { + return this; + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String get() { + String name = session.getContext().getRealm().getDisplayName(); + if (name == null) { + name = session.getContext().getRealm().getName(); + } + return "Hello " + name; + } + + @Override + public void close() { + } + +} diff --git a/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProviderFactory.java b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProviderFactory.java new file mode 100644 index 0000000000..584a90f515 --- /dev/null +++ b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProviderFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 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.examples.rest; + +import org.keycloak.Config.Scope; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; +import org.keycloak.services.resource.RealmResourceProviderFactory; + +/** + * @author Stian Thorgersen + */ +public class HelloResourceProviderFactory implements RealmResourceProviderFactory { + + public static final String ID = "hello"; + + @Override + public String getId() { + return ID; + } + + @Override + public RealmResourceProvider create(KeycloakSession session) { + return new HelloResourceProvider(session); + } + + @Override + public void init(Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + +} diff --git a/examples/providers/rest/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/examples/providers/rest/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory new file mode 100644 index 0000000000..3d053e0047 --- /dev/null +++ b/examples/providers/rest/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory @@ -0,0 +1,18 @@ +# +# Copyright 2016 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. +# + +org.keycloak.examples.rest.HelloResourceProviderFactory \ No newline at end of file diff --git a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java b/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java index f8f21e4d05..8f615c6f9a 100644 --- a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java +++ b/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java @@ -29,13 +29,10 @@ import org.keycloak.provider.Provider; public interface RealmResourceProvider extends Provider { /** - *

Returns a JAX-RS resource instance that maps to the given path. + *

Returns a JAX-RS resource instance. * - *

If the given path could not be resolved to a sub-resource, this method must return null to give a chance to other providers - * to resolve their sub-resources. - * - * @param path the sub-resource's path - * @return a JAX-RS sub-resource instance that maps to the given path or null if the path could not be resolved to a sub-resource. + * @return a JAX-RS sub-resource instance */ - Object getResource(String path); + Object getResource(); + } diff --git a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java b/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java index d970687381..b39bc12f93 100644 --- a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java +++ b/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java @@ -18,24 +18,13 @@ package org.keycloak.services.resource; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.ServerInfoAwareProviderFactory; /** *

A factory that creates {@link RealmResourceProvider} instances. * * @author Pedro Igor */ -public interface RealmResourceProviderFactory extends ProviderFactory, ServerInfoAwareProviderFactory { +public interface RealmResourceProviderFactory extends ProviderFactory { - /** - * Creates a {@link RealmResourceProvider}. - * - * @param realm the {@link RealmModel} associated with the current request - * @param keycloakSession the {@link KeycloakSession} associated with the current request - * @return a {@link RealmResourceProvider} instance - */ - RealmResourceProvider create(RealmModel realm, KeycloakSession keycloakSession); } \ No newline at end of file diff --git a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProvider.java b/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProvider.java deleted file mode 100644 index be949774f2..0000000000 --- a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.services.resource.admin; - -import org.keycloak.provider.Provider; - -/** - *

A {@link RealmAdminResourceProvider} creates JAX-RS sub-resource instances for paths relative to - * Realm's Admin RESTful API that could not be resolved by the server. - * - * @author Pedro Igor - */ -public interface RealmAdminResourceProvider extends Provider { - - /** - *

Returns a JAX-RS resource instance that maps to the given path. - * - *

If the given path could not be resolved to a sub-resource, this method must return null to give a chance to other providers - * to resolve their sub-resources. - * - * @param path the sub-resource's path - * @return a JAX-RS sub-resource instance that maps to the given path or null if the path could not be resolved to a sub-resource. - */ - Object getResource(String path); -} diff --git a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProviderFactory.java b/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProviderFactory.java deleted file mode 100644 index 5a797a5b44..0000000000 --- a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceProviderFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.services.resource.admin; - -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.ServerInfoAwareProviderFactory; - -/** - *

A factory that creates {@link RealmAdminResourceProvider} instances. - * - * @author Pedro Igor - */ -public interface RealmAdminResourceProviderFactory extends ProviderFactory, ServerInfoAwareProviderFactory { - - /** - * Creates a {@link RealmAdminResourceProvider}. - * - * @param realm the {@link RealmModel} associated with the current request - * @param keycloakSession the {@link KeycloakSession} associated with the current request - * @return a {@link RealmAdminResourceProvider} instance - */ - RealmAdminResourceProvider create(RealmModel realm, KeycloakSession keycloakSession); -} diff --git a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceSPI.java b/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceSPI.java deleted file mode 100644 index f72741d00f..0000000000 --- a/server-spi/src/main/java/org/keycloak/services/resource/admin/RealmAdminResourceSPI.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.services.resource.admin; - -import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.Spi; - -/** - *

A {@link Spi} to plug additional sub-resources to Realms' Administration RESTful API. - * - *

Implementors can use this {@link Spi} to provide additional services to the mentioned API and extend Keycloak capabilities by - * creating JAX-RS sub-resources for paths not known by the server. - * - * @author Pedro Igor - */ -public class RealmAdminResourceSPI implements Spi { - - @Override - public boolean isInternal() { - return true; - } - - @Override - public String getName() { - return "realm-admin-restapi-extension"; - } - - @Override - public Class getProviderClass() { - return RealmAdminResourceProvider.class; - } - - @Override - public Class getProviderFactoryClass() { - return RealmAdminResourceProviderFactory.class; - } -} diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi index d263efdd48..657faaf390 100755 --- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -31,7 +31,6 @@ org.keycloak.exportimport.ImportSpi org.keycloak.timer.TimerSpi org.keycloak.services.managers.BruteForceProtectorSpi org.keycloak.services.resource.RealmResourceSPI -org.keycloak.services.resource.admin.RealmAdminResourceSPI org.keycloak.protocol.ClientInstallationSpi org.keycloak.protocol.LoginProtocolSpi org.keycloak.protocol.ProtocolMapperSpi diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index f6cdbf7af4..ae743f399d 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -17,7 +17,6 @@ package org.keycloak.services.resources; import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.KeycloakUriBuilder; @@ -28,24 +27,22 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocolFactory; -import org.keycloak.provider.ProviderFactory; import org.keycloak.services.ServicesLogger; import org.keycloak.services.clientregistration.ClientRegistrationService; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resource.RealmResourceProvider; -import org.keycloak.services.resource.RealmResourceProviderFactory; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.services.util.ResolveRelative; import org.keycloak.wellknown.WellKnownProvider; import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.*; import javax.ws.rs.core.Response.ResponseBuilder; import java.net.URI; -import java.util.List; /** * @author Bill Burke @@ -243,27 +240,20 @@ public class RealmsResource { /** * A JAX-RS sub-resource locator that uses the {@link org.keycloak.services.resource.RealmResourceSPI} to resolve sub-resources instances given an unknownPath. * - * @param unknownPath a path that is unknown to the server - * @return a JAX-RS sub-resource instance that maps to the given unknownPath. Otherwise null is returned. + * @param extension a path that could be to a REST extension + * @return a JAX-RS sub-resource instance for the REST extension if found. Otherwise null is returned. */ - @Path("{realm}/{unknow_path}") - public Object resolveUnknowPath(@PathParam("realm") String realmName, @PathParam("unknow_path") String unknownPath) { - List factory = this.session.getKeycloakSessionFactory().getProviderFactories(RealmResourceProvider.class); - - if (factory != null) { - RealmModel realm = init(realmName); - - for (ProviderFactory providerFactory : factory) { - RealmResourceProviderFactory realmFactory = (RealmResourceProviderFactory) providerFactory; - RealmResourceProvider resourceProvider = realmFactory.create(realm, this.session); - Object resource = resourceProvider.getResource(unknownPath); - - if (resource != null) { - return resource; - } + @Path("{realm}/{extension}") + public Object resolveRealmExtension(@PathParam("realm") String realmName, @PathParam("extension") String extension) { + RealmResourceProvider provider = session.getProvider(RealmResourceProvider.class, extension); + if (provider != null) { + init(realmName); + Object resource = provider.getResource(); + if (resource != null) { + return resource; } } - return null; + throw new NotFoundException(); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 5825d0ddae..b434cdfe04 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -64,10 +64,6 @@ import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.UsersSyncManager; import org.keycloak.services.ErrorResponse; -import org.keycloak.services.resource.RealmResourceProvider; -import org.keycloak.services.resource.RealmResourceProviderFactory; -import org.keycloak.services.resource.admin.RealmAdminResourceProvider; -import org.keycloak.services.resource.admin.RealmAdminResourceProviderFactory; import org.keycloak.timer.TimerProvider; import javax.ws.rs.Consumes; @@ -794,30 +790,4 @@ public class RealmAdminResource { } } - /** - * A JAX-RS sub-resource locator that uses the {@link org.keycloak.services.resource.admin.RealmAdminResourceSPI} to resolve - * sub-resources instances given an unknownPath. - * - * @param unknownPath a path that is unknown to the server - * @return a JAX-RS sub-resource instance that maps to the given unknownPath. Otherwise null is returned. - */ - @Path("{unknow_path}") - public Object resolveUnknowPath(@PathParam("unknow_path") String unknownPath) { - List factory = this.session.getKeycloakSessionFactory().getProviderFactories(RealmAdminResourceProvider.class); - - if (factory != null) { - for (ProviderFactory providerFactory : factory) { - RealmAdminResourceProviderFactory realmFactory = (RealmAdminResourceProviderFactory) providerFactory; - RealmAdminResourceProvider resourceProvider = realmFactory.create(realm, this.session); - Object resource = resourceProvider.getResource(unknownPath); - - if (resource != null) { - return resource; - } - } - } - - return null; - } - }