jaxrs-doclet support

This commit is contained in:
Bill Burke 2014-05-26 20:41:33 -04:00
parent 38b52283a5
commit 199c786e22
24 changed files with 961 additions and 26 deletions

View file

@ -18,6 +18,10 @@
<directory>../../target/site/apidocs</directory>
<outputDirectory>docs/javadocs</outputDirectory>
</fileSet>
<fileSet>
<directory>../../services/target/site/apidocs</directory>
<outputDirectory>docs/rest-api</outputDirectory>
</fileSet>
<fileSet>
<directory>../../docbook/target/docbook/publish/en-US</directory>
<outputDirectory>docs/userguide</outputDirectory>

View file

@ -195,16 +195,34 @@
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<reportSets>
<reportSet>
<version>2.9.1</version>
<executions>
<!--
<execution>
<id>generate-service-docs</id>
<phase>generate-resources</phase>
<configuration>
<doclet>com.hypnoticocelot.jaxrs.doclet.ServiceDoclet</doclet>
<docletArtifact>
<groupId>com.hypnoticocelot</groupId>
<artifactId>jaxrs-doclet</artifactId>
<version>0.0.4-SNAPSHOT</version>
</docletArtifact>
<reportOutputDirectory>swagger</reportOutputDirectory>
<useStandardDocletOptions>false</useStandardDocletOptions>
<additionalparam>-apiVersion 1 -docBasePath /apidocs -apiBasePath /</additionalparam>
</configuration>
<goals>
<goal>javadoc</goal>
</goals>
</execution>
-->
<execution>
<id>generate-service-docs</id>
<phase>generate-resources</phase>
<configuration>
<doclet>com.lunatech.doclets.jax.jaxrs.JAXRSDoclet</doclet>
<docletArtifacts>
@ -214,13 +232,22 @@
<version>0.10.0</version>
</docletArtifact>
</docletArtifacts>
<detectOfflineLinks>false</detectOfflineLinks>
<offlineLinks>
<offlineLink>
<url>../javadocs</url>
<!-- <location>C:/Users/William/jboss/keycloak/p1b-repo/keycloak/target/site/apidocs</location> -->
<location>${project.basedir}/../target/site/apidocs</location>
</offlineLink>
</offlineLinks>
<additionalparam>-disablejavascriptexample</additionalparam>
</configuration>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
<goals>
<goal>javadoc</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</reporting>
</build>
</project>

View file

@ -200,12 +200,22 @@ public class AccountService {
}
}
/**
* CORS preflight
*
* @return
*/
@Path("/")
@OPTIONS
public Response accountPreflight() {
return Cors.add(request, Response.ok()).auth().preflight().build();
}
/**
* Get account information.
*
* @return
*/
@Path("/")
@GET
public Response accountPage() {
@ -279,6 +289,18 @@ public class AccountService {
return forwardToPage("sessions", AccountPages.SESSIONS);
}
/**
* Update account information.
*
* Form params:
*
* firstName
* lastName
* email
*
* @param formData
* @return
*/
@Path("/")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -348,6 +370,17 @@ public class AccountService {
return Response.seeOther(Urls.accountSessionsPage(uriInfo.getBaseUri(), realm.getName())).build();
}
/**
* Update the TOTP for this account.
*
* form parameters:
*
* totp - otp generated by authenticator
* totpSecret - totp secret to register
*
* @param formData
* @return
*/
@Path("totp")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -381,6 +414,18 @@ public class AccountService {
return account.setSuccess("successTotp").createResponse(AccountPages.TOTP);
}
/**
* Update account password
*
* Form params:
*
* password - old password
* password-new
* pasword-confirm
*
* @param formData
* @return
*/
@Path("password")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)

View file

@ -7,11 +7,18 @@ import javax.ws.rs.core.Response;
import java.io.InputStream;
/**
* Get keycloak.js file for javascript clients
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Path("/js")
public class JsResource {
/**
* Get keycloak.js file for javascript clients
*
* @return
*/
@GET
@Path("/keycloak.js")
@Produces("text/javascript")

View file

@ -13,6 +13,8 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
/**
* Resource class for public realm information
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -28,10 +30,15 @@ public class PublicRealmResource {
this.realm = realm;
}
/**
* Public information about the realm.
*
* @return
*/
@GET
@NoCache
@Produces("application/json")
public PublishedRealmRepresentation getRealm(@PathParam("realm") String id) {
public PublishedRealmRepresentation getRealm() {
return realmRep(realm, uriInfo);
}

View file

@ -18,11 +18,23 @@ import java.io.IOException;
import java.io.OutputStream;
/**
* Create a barcode image
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Path("/qrcode")
public class QRCodeResource {
/**
* Create a bar code image
*
* @param contents
* @param size
* @return
* @throws ServletException
* @throws IOException
* @throws WriterException
*/
@GET
@Produces("image/png")
public Response createQrCode(@QueryParam("contents") String contents, @QueryParam("size") String size) throws ServletException, IOException, WriterException {

View file

@ -162,7 +162,7 @@ public class RealmsResource {
protected RealmModel locateRealm(String name, RealmManager realmManager) {
RealmModel realm = realmManager.getRealmByName(name);
if (realm == null) {
throw new NotFoundException("Realm " + name + " not found");
throw new NotFoundException("Realm " + name + " does not exist");
}
return realm;
}

View file

@ -16,6 +16,8 @@ import javax.ws.rs.core.Response;
import java.io.InputStream;
/**
* Theme resource
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@Path("/theme")
@ -28,6 +30,14 @@ public class ThemeResource {
@Context
private ProviderSession providerSession;
/**
* Get theme content
*
* @param themType
* @param themeName
* @param path
* @return
*/
@GET
@Path("/{themType}/{themeName}/{path:.*}")
public Response getResource(@PathParam("themType") String themType, @PathParam("themeName") String themeName, @PathParam("path") String path) {

View file

@ -74,6 +74,8 @@ import java.util.Map;
import java.util.Set;
/**
* Resource class for the oauth/openid connect token service
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -198,7 +200,23 @@ public class TokenService {
}
/**
* Direct grant REST invocation. One stop call to obtain an access token.
*
* If the client is a confidential client
* you must include the client-id (application name or oauth client name) and secret in an Basic Auth Authorization header.
*
* If the client is a public client, then you must include a "client_id" form parameter with the app's or oauth client's name.
*
* The realm must be configured to allow these types of auth requests. (Direct Grant API in admin console Settings page)
*
*
* @See <a href="http://tools.ietf.org/html/rfc6749#section-4.3">http://tools.ietf.org/html/rfc6749#section-4.3</a>
*
* @param authorizationHeader
* @param form
* @return @see org.keycloak.representations.AccessTokenResponse
*/
@Path("grants/access")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -284,6 +302,20 @@ public class TokenService {
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
}
/**
* URL for making refresh token requests.
*
* @See <a href="http://tools.ietf.org/html/rfc6749#section-6">http://tools.ietf.org/html/rfc6749#section-6</a>
*
* If the client is a confidential client
* you must include the client-id (application name or oauth client name) and secret in an Basic Auth Authorization header.
*
* If the client is a public client, then you must include a "client_id" form parameter with the app's or oauth client's name.
*
* @param authorizationHeader
* @param form
* @return
*/
@Path("refresh")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -321,6 +353,16 @@ public class TokenService {
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
}
/**
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
*
* @param clientId
* @param scopeParam
* @param state
* @param redirect
* @param formData
* @return
*/
@Path("auth/request/login")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -422,6 +464,16 @@ public class TokenService {
return service;
}
/**
* Registration
*
* @param clientId
* @param scopeParam
* @param state
* @param redirect
* @param formData
* @return
*/
@Path("registrations")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@ -530,6 +582,11 @@ public class TokenService {
return processLogin(clientId, scopeParam, state, redirect, formData);
}
/**
* CORS preflight path for access code to token
*
* @return
*/
@Path("access/codes")
@OPTIONS
@Produces("application/json")
@ -538,6 +595,15 @@ public class TokenService {
return Cors.add(request, Response.ok()).auth().preflight().build();
}
/**
* URL invoked by adapter to turn an access code to access token
*
* @See <a href="http://tools.ietf.org/html/rfc6749#section-4.1">http://tools.ietf.org/html/rfc6749#section-4.1</a>
*
* @param authorizationHeader
* @param formData
* @return
*/
@Path("access/codes")
@POST
@Produces("application/json")
@ -720,6 +786,20 @@ public class TokenService {
return client;
}
/**
* Login page. Must be redirected to from the application or oauth client.
*
* @See <a href="http://tools.ietf.org/html/rfc6749#section-4.1">http://tools.ietf.org/html/rfc6749#section-4.1</a>
*
*
* @param responseType
* @param redirect
* @param clientId
* @param scopeParam
* @param state
* @param prompt
* @return
*/
@Path("login")
@GET
public Response loginPage(final @QueryParam("response_type") String responseType,
@ -784,6 +864,16 @@ public class TokenService {
return Flows.forms(providerSession, realm, uriInfo).createLogin();
}
/**
* Registration page. Must be redirected to from login page.
*
* @param responseType
* @param redirect
* @param clientId
* @param scopeParam
* @param state
* @return
*/
@Path("registrations")
@GET
public Response registerPage(final @QueryParam("response_type") String responseType,
@ -834,6 +924,13 @@ public class TokenService {
return Flows.forms(providerSession, realm, uriInfo).createRegistration();
}
/**
* Logout user session.
*
* @param sessionState
* @param redirectUri
* @return
*/
@Path("logout")
@GET
@NoCache
@ -877,6 +974,12 @@ public class TokenService {
audit.user(user).session(session).success();
}
/**
* OAuth grant page. You should not invoked this directly!
*
* @param formData
* @return
*/
@Path("oauth/grant")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)

View file

@ -20,6 +20,12 @@ public class WelcomeResource {
@Context
private UriInfo uriInfo;
/**
* Welcome page of Keycloak
*
* @return
* @throws URISyntaxException
*/
@GET
@Produces("text/html")
public Response getWelcomePage() throws URISyntaxException {
@ -31,6 +37,12 @@ public class WelcomeResource {
}
}
/**
* Resources for welcome page
*
* @param name
* @return
*/
@GET
@Path("/welcome-content/{name}")
@Produces("text/html")

View file

@ -147,6 +147,11 @@ public class AdminConsole {
}
}
/**
* Adapter configuration for the admin console for this realm
*
* @return
*/
@Path("config")
@GET
@Produces("application/json")
@ -160,6 +165,12 @@ public class AdminConsole {
}
/**
* Permission information
*
* @param headers
* @return
*/
@Path("whoami")
@GET
@Produces("application/json")
@ -231,6 +242,11 @@ public class AdminConsole {
}
}
/**
* Logout from the admin console
*
* @return
*/
@Path("logout")
@GET
@NoCache
@ -248,6 +264,12 @@ public class AdminConsole {
private static FileTypeMap mimeTypes = MimetypesFileTypeMap.getDefaultFileTypeMap();
/**
* Main page of this realm's admin console
*
* @return
* @throws URISyntaxException
*/
@GET
public Response getMainPage() throws URISyntaxException {
if (!uriInfo.getRequestUri().getPath().endsWith("/")) {
@ -257,6 +279,11 @@ public class AdminConsole {
}
}
/**
* Javascript used by admin console
*
* @return
*/
@GET
@Path("js/keycloak.js")
@Produces("text/javascript")
@ -270,7 +297,12 @@ public class AdminConsole {
}
}
/**
* Theme resources for this realm's admin console. (images, html files, etc..)
*
* @param path
* @return
*/
@GET
@Path("{path:.+}")
public Response getResource(@PathParam("path") String path) {

View file

@ -35,6 +35,8 @@ import javax.ws.rs.core.UriInfo;
import java.io.IOException;
/**
* Root resource for admin console and admin REST API
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -72,6 +74,12 @@ public class AdminRoot {
/**
* Convenience path to master realm admin console
*
* @return
*/
@GET
public Response masterRealmAdminConsoleRedirect() {
RealmModel master = new RealmManager(session).getKeycloakAdminstrationRealm();
@ -80,7 +88,12 @@ public class AdminRoot {
).build();
}
@Path("index.{hack:html}")
/**
* Convenience path to master realm admin console
*
* @return
*/
@Path("index.{html:html}") // expression is actually "index.html" but this is a hack to get around jax-doclet bug
@GET
public Response masterRealmAdminConsoleRedirectHtml() {
return masterRealmAdminConsoleRedirect();
@ -103,7 +116,12 @@ public class AdminRoot {
return adminBaseUrl(base).path(AdminRoot.class, "getAdminConsole");
}
@Path("{realm}/console")
/**
* path to realm admin console ui
*
* @param name Realm name (not id!)
* @return
*/
public AdminConsole getAdminConsole(final @PathParam("realm") String name) {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = locateRealm(name, realmManager);
@ -152,6 +170,13 @@ public class AdminRoot {
return adminBaseUrl(base).path(AdminRoot.class, "getRealmsAdmin");
}
/**
* Base Path to realm admin REST interface
*
* @param headers
* @return
*/
@Path("realms")
public RealmsAdminResource getRealmsAdmin(@Context final HttpHeaders headers) {
if (request.getHttpMethod().equalsIgnoreCase("OPTIONS")) {
@ -173,6 +198,12 @@ public class AdminRoot {
return adminResource;
}
/**
* General information about the server
*
* @param headers
* @return
*/
@Path("serverinfo")
public ServerInfoAdminResource getServerInfo(@Context final HttpHeaders headers) {
ServerInfoAdminResource adminResource = new ServerInfoAdminResource();

View file

@ -45,6 +45,8 @@ import java.util.Map;
import java.util.Set;
/**
* Base resource class for managing one particular application of a realm.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -73,11 +75,21 @@ public class ApplicationResource {
auth.init(RealmAuth.Resource.APPLICATION);
}
/**
* base path for managing allowed application claims
*
* @return
*/
@Path("claims")
public ClaimResource getClaimResource() {
return new ClaimResource(application, auth);
}
/**
* Update the application.
* @param rep
* @return
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response update(final ApplicationRepresentation rep) {
@ -93,6 +105,11 @@ public class ApplicationResource {
}
/**
* Get representation of the application.
*
* @return
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@ -104,6 +121,12 @@ public class ApplicationResource {
}
/**
* Return keycloak.json file for this application to be used to configure the adapter of that application.
*
* @return
* @throws IOException
*/
@GET
@NoCache
@Path("installation/json")
@ -118,6 +141,12 @@ public class ApplicationResource {
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
}
/**
* Return XML that can be included in the JBoss/Wildfly Keycloak subsystem to configure the adapter of that application.
*
* @return
* @throws IOException
*/
@GET
@NoCache
@Path("installation/jboss")
@ -129,6 +158,10 @@ public class ApplicationResource {
return applicationManager.toJBossSubsystemConfig(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
}
/**
* Delete this application.
*
*/
@DELETE
@NoCache
public void deleteApplication() {
@ -137,6 +170,12 @@ public class ApplicationResource {
realm.removeApplication(application.getId());
}
/**
* Generates a new secret for this application
*
* @return
*/
@Path("client-secret")
@POST
@Produces("application/json")
@ -150,6 +189,11 @@ public class ApplicationResource {
return rep;
}
/**
* Get the secret of this application
*
* @return
*/
@Path("client-secret")
@GET
@Produces("application/json")
@ -162,7 +206,11 @@ public class ApplicationResource {
return ModelToRepresentation.toRepresentation(model);
}
/**
* Base path for managing the scope mappings for this application
*
* @return
*/
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
return new ScopeMappedResource(realm, auth, application, session);
@ -173,6 +221,12 @@ public class ApplicationResource {
return new RoleContainerResource(realm, auth, application);
}
/**
* Returns set of allowed origin. This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this application.
*
* @return
*/
@Path("allowed-origins")
@GET
@Produces("application/json")
@ -183,6 +237,12 @@ public class ApplicationResource {
return application.getWebOrigins();
}
/**
* Change the set of allowed origins. This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this application.
*
* @param allowedOrigins
*/
@Path("allowed-origins")
@PUT
@Consumes("application/json")
@ -193,6 +253,12 @@ public class ApplicationResource {
application.setWebOrigins(allowedOrigins);
}
/**
* Remove set of allowed origins from current allowed origins list. This is used for CORS requests. Access tokens will have
* their allowedOrigins claim set to this value for tokens created for this application.
*
* @param allowedOrigins
*/
@Path("allowed-origins")
@DELETE
@Consumes("application/json")
@ -205,6 +271,10 @@ public class ApplicationResource {
}
}
/**
* If the application has an admin URL, push the application's revocation policy to it.
*
*/
@Path("push-revocation")
@POST
public void pushRevocation() {
@ -212,6 +282,12 @@ public class ApplicationResource {
new ResourceAdminManager().pushApplicationRevocationPolicy(uriInfo.getRequestUri(), realm, application);
}
/**
* If the application has an admin URL, query it directly for session stats.
*
* @param users whether to include users logged in.
* @return
*/
@Path("session-stats")
@GET
@NoCache
@ -235,6 +311,15 @@ public class ApplicationResource {
return stats;
}
/**
* Number of user sessions associated with this application
*
* {
* "count": number
* }
*
* @return
*/
@Path("session-count")
@GET
@NoCache
@ -246,6 +331,11 @@ public class ApplicationResource {
return map;
}
/**
* Return a list of user sessions associated with this application
*
* @return
*/
@Path("user-sessions")
@GET
@NoCache
@ -260,6 +350,10 @@ public class ApplicationResource {
return sessions;
}
/**
* If the application has an admin URL, invalidate all sessions associated with that application directly.
*
*/
@Path("logout-all")
@POST
public void logoutAll() {
@ -267,6 +361,10 @@ public class ApplicationResource {
new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application, null, null);
}
/**
* If the application 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) {

View file

@ -27,6 +27,8 @@ import java.util.ArrayList;
import java.util.List;
/**
* Base resource class for managing a realm's applications.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -45,6 +47,11 @@ public class ApplicationsResource {
auth.init(RealmAuth.Resource.APPLICATION);
}
/**
* List of applications belonging to this realm.
*
* @return
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@ -68,6 +75,13 @@ public class ApplicationsResource {
return rep;
}
/**
* Create a new application. Application name must be unique!
*
* @param uriInfo
* @param rep
* @return
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createApplication(final @Context UriInfo uriInfo, final ApplicationRepresentation rep) {
@ -82,6 +96,12 @@ public class ApplicationsResource {
}
}
/**
* Base path for managing a specific application.
*
* @param name
* @return
*/
@Path("{app-name}")
public ApplicationResource getApplication(final @PathParam("app-name") String name) {
ApplicationModel applicationModel = realm.getApplicationByName(name);

View file

@ -12,6 +12,8 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Base resource class for managing allowed claims for an application or oauth client
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -24,6 +26,11 @@ public class ClaimResource {
this.auth = auth;
}
/**
* Get the claims a client is allowed to ask for
*
* @return
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public ClaimRepresentation getClaims() {
@ -31,6 +38,11 @@ public class ClaimResource {
return ModelToRepresentation.toRepresentation(model);
}
/**
* Set the cliams a client is allowed to ask for.
*
* @param rep
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateClaims(ClaimRepresentation rep) {

View file

@ -30,6 +30,8 @@ import javax.ws.rs.core.UriInfo;
import java.io.IOException;
/**
* Base resource class for managing oauth clients
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -58,12 +60,22 @@ public class OAuthClientResource {
auth.init(RealmAuth.Resource.CLIENT);
}
/**
* Base path for managing allowed oauth client claims
*
* @return
*/
@Path("claims")
public ClaimResource getClaimResource() {
return new ClaimResource(oauthClient, auth);
}
/**
* Update the oauth client
*
* @param rep
* @return
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response update(final OAuthClientRepresentation rep) {
@ -78,7 +90,11 @@ public class OAuthClientResource {
}
}
/**
* Get a representation of the oauth client
*
* @return
*/
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
@ -88,6 +104,12 @@ public class OAuthClientResource {
return OAuthClientManager.toRepresentation(oauthClient);
}
/**
* Get an example keycloak.json file to use to configure the oauth client
*
* @return
* @throws IOException
*/
@GET
@NoCache
@Path("installation")
@ -102,6 +124,10 @@ public class OAuthClientResource {
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
}
/**
* Remove the OAuth Client
*
*/
@DELETE
@NoCache
public void deleteOAuthClient() {
@ -110,6 +136,12 @@ public class OAuthClientResource {
realm.removeOAuthClient(oauthClient.getId());
}
/**
* Generate a new client secret for the oauth client
*
* @return
*/
@Path("client-secret")
@POST
@Produces("application/json")
@ -124,6 +156,11 @@ public class OAuthClientResource {
return rep;
}
/**
* Get the secret of the oauth client
*
* @return
*/
@Path("client-secret")
@GET
@Produces("application/json")
@ -136,6 +173,11 @@ public class OAuthClientResource {
return ModelToRepresentation.toRepresentation(model);
}
/**
* Base path for managing the oauth client's scope
*
* @return
*/
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
return new ScopeMappedResource(realm, auth, oauthClient, session);

View file

@ -50,6 +50,11 @@ public class OAuthClientsResource {
auth.init(RealmAuth.Resource.CLIENT);
}
/**
* Get a list of oauth clients in this realm.
*
* @return
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@ -70,6 +75,13 @@ public class OAuthClientsResource {
return rep;
}
/**
* Create an oauth client
*
* @param uriInfo
* @param rep
* @return
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createOAuthClient(final @Context UriInfo uriInfo, final OAuthClientRepresentation rep) {
@ -84,6 +96,12 @@ public class OAuthClientsResource {
}
}
/**
* Base path to manage one specific oauth client
*
* @param id oauth client's id (not clientId!)
* @return
*/
@Path("{id}")
public OAuthClientResource getOAuthClient(final @PathParam("id") String id) {
auth.requireView();

View file

@ -41,6 +41,8 @@ import java.util.List;
import java.util.Map;
/**
* Base resource class for the admin REST api of one realm
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -72,6 +74,11 @@ public class RealmAdminResource {
auth.init(RealmAuth.Resource.REALM);
}
/**
* Base path for managing applications under this realm.
*
* @return
*/
@Path("applications")
public ApplicationsResource getApplications() {
ApplicationsResource applicationsResource = new ApplicationsResource(realm, auth);
@ -80,6 +87,11 @@ public class RealmAdminResource {
return applicationsResource;
}
/**
* base path for managing oauth clients in this realm
*
* @return
*/
@Path("oauth-clients")
public OAuthClientsResource getOAuthClients() {
OAuthClientsResource oauth = new OAuthClientsResource(realm, auth, session);
@ -88,11 +100,22 @@ public class RealmAdminResource {
return oauth;
}
/**
* base path for managing realm-level roles of this realm
*
* @return
*/
@Path("roles")
public RoleContainerResource getRoleContainerResource() {
return new RoleContainerResource(realm, auth, realm);
}
/**
* Get the top-level representation of the realm. It will not include nested information like User, Application, or OAuth
* Client representations.
*
* @return
*/
@GET
@NoCache
@Produces("application/json")
@ -109,6 +132,13 @@ public class RealmAdminResource {
}
}
/**
* Update the top-level information of this realm. Any user, roles, application, or oauth client information in the representation
* will be ignored. This will only update top-level attributes of the realm.
*
* @param rep
* @return
*/
@PUT
@Consumes("application/json")
public Response updateRealm(final RealmRepresentation rep) {
@ -123,6 +153,10 @@ public class RealmAdminResource {
}
}
/**
* Delete this realm.
*
*/
@DELETE
public void deleteRealm() {
auth.requireManage();
@ -132,6 +166,11 @@ public class RealmAdminResource {
}
}
/**
* Base path for managing users in this realm.
*
* @return
*/
@Path("users")
public UsersResource users() {
UsersResource users = new UsersResource(providers, realm, auth, tokenManager);
@ -140,6 +179,11 @@ public class RealmAdminResource {
return users;
}
/**
* Path for managing all realm-level or application-level roles defined in this realm by it's id.
*
* @return
*/
@Path("roles-by-id")
public RoleByIdResource rolesById() {
RoleByIdResource resource = new RoleByIdResource(realm, auth);
@ -148,6 +192,10 @@ public class RealmAdminResource {
return resource;
}
/**
* Push the realm's revocation policy to any application that has an admin url associated with it.
*
*/
@Path("push-revocation")
@POST
public void pushRevocation() {
@ -155,6 +203,11 @@ public class RealmAdminResource {
new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
}
/**
* Removes all user sessions. Any application that has an admin url will also be told to invalidate any sessions
* they have.
*
*/
@Path("logout-all")
@POST
public void logoutAll() {
@ -163,6 +216,12 @@ public class RealmAdminResource {
new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
}
/**
* Remove a specific user session. Any application that has an admin url will also be told to invalidate this
* particular session.
*
* @param sessionId
*/
@Path("sessions/{session}")
@DELETE
public void deleteSession(@PathParam("session") String sessionId) {
@ -172,6 +231,12 @@ public class RealmAdminResource {
new ResourceAdminManager().logoutSession(uriInfo.getRequestUri(), realm, session.getId());
}
/**
* Returns a JSON map. The key is the application name, the value is the number of sessions that currently are active
* with that application. Only application's that actually have a session associated with them will be in this map.
*
* @return
*/
@Path("application-session-stats")
@GET
@NoCache
@ -187,6 +252,12 @@ public class RealmAdminResource {
return stats;
}
/**
* Any application that has an admin URL will be asked directly how many sessions they have active and what users
* are involved with those sessions.
*
* @return
*/
@Path("session-stats")
@GET
@NoCache
@ -203,6 +274,11 @@ public class RealmAdminResource {
return stats;
}
/**
* View the audit provider and how it is configured.
*
* @return
*/
@GET
@Path("audit")
@Produces("application/json")
@ -212,6 +288,11 @@ public class RealmAdminResource {
return ModelToRepresentation.toAuditReprensetation(realm);
}
/**
* Change the audit provider and/or it's configuration
*
* @param rep
*/
@PUT
@Path("audit")
@Consumes("application/json")
@ -222,6 +303,17 @@ public class RealmAdminResource {
new RealmManager(session).updateRealmAudit(rep, realm);
}
/**
* Query audit events. Returns all events, or will query based on URL query parameters listed here
*
* @param client app or oauth client name
* @param event event type
* @param user user id
* @param ipAddress
* @param firstResult
* @param maxResults
* @return
*/
@Path("audit/events")
@GET
@NoCache
@ -255,6 +347,10 @@ public class RealmAdminResource {
return query.getResultList();
}
/**
* Delete all audit events.
*
*/
@Path("audit/events")
@DELETE
public void clearAudit() {

View file

@ -41,6 +41,8 @@ import java.util.List;
import java.util.Map;
/**
* Top level resource for Admin REST API
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -71,10 +73,16 @@ public class RealmsAdminResource {
@Context
protected KeycloakApplication keycloak;
/**
* Returns a list of realms. This list is filtered based on what realms the caller is allowed to view.
*
* @return
*/
@GET
@NoCache
@Produces("application/json")
public List<RealmRepresentation> getRealms() {
public List<RealmRepresentation> getRealms() {
RealmManager realmManager = new RealmManager(session);
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
@ -101,6 +109,13 @@ public class RealmsAdminResource {
}
}
/**
* Import a realm from a full representation of that realm. Realm name must be unique.
*
* @param uriInfo
* @param rep JSON representation
* @return
*/
@POST
@Consumes("application/json")
public Response importRealm(@Context final UriInfo uriInfo, final RealmRepresentation rep) {
@ -127,6 +142,15 @@ public class RealmsAdminResource {
}
}
/**
* Upload a realm from a uploaded JSON file. The posted represenation is expected to be a multipart/form-data encapsulation
* of a JSON file. The same format a browser would use when uploading a file.
*
* @param uriInfo
* @param input multipart/form data
* @return
* @throws IOException
*/
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadRealm(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
@ -178,7 +202,13 @@ public class RealmsAdminResource {
}
}
/**
* Base path for the admin REST API for one particular realm.
*
* @param headers
* @param name realm name (not id!)
* @return
*/
@Path("{realm}")
public RealmAdminResource getRealmAdmin(@Context final HttpHeaders headers,
@PathParam("realm") final String name) {

View file

@ -39,6 +39,12 @@ public class RoleByIdResource extends RoleResource {
this.auth = auth;
}
/**
* Get a specific role's representation
*
* @param id id of role
* @return
*/
@Path("{role-id}")
@GET
@NoCache
@ -70,6 +76,11 @@ public class RoleByIdResource extends RoleResource {
return roleModel;
}
/**
* Delete this role
*
* @param id id of role
*/
@Path("{role-id}")
@DELETE
@NoCache
@ -79,6 +90,12 @@ public class RoleByIdResource extends RoleResource {
deleteRole(role);
}
/**
* Update this role
*
* @param id id of role
* @param rep
*/
@Path("{role-id}")
@PUT
@Consumes("application/json")
@ -88,6 +105,12 @@ public class RoleByIdResource extends RoleResource {
updateRole(rep, role);
}
/**
* Make this role a composite role by associating some child roles to it.
*
* @param id
* @param roles
*/
@Path("{role-id}/composites")
@POST
@Consumes("application/json")
@ -97,6 +120,12 @@ public class RoleByIdResource extends RoleResource {
addComposites(roles, role);
}
/**
* If this role is a composite, return a set of its children
*
* @param id
* @return
*/
@Path("{role-id}/composites")
@GET
@NoCache
@ -109,6 +138,12 @@ public class RoleByIdResource extends RoleResource {
return getRoleComposites(role);
}
/**
* Return a set of realm-level roles that are in the role's composite
*
* @param id
* @return
*/
@Path("{role-id}/composites/realm")
@GET
@NoCache
@ -119,6 +154,13 @@ public class RoleByIdResource extends RoleResource {
return getRealmRoleComposites(role);
}
/**
* Return a set of application-level roles for a specific app that are in the role's composite
*
* @param id
* @param appName
* @return
*/
@Path("{role-id}/composites/applications/{app}")
@GET
@NoCache
@ -130,7 +172,12 @@ public class RoleByIdResource extends RoleResource {
return getApplicationRoleComposites(appName, role);
}
/**
* Remove the listed set of roles from this role's composite
*
* @param id
* @param roles
*/
@Path("{role-id}/composites")
@DELETE
@Consumes("application/json")

View file

@ -42,6 +42,11 @@ public class RoleContainerResource extends RoleResource {
this.roleContainer = roleContainer;
}
/**
* List all roles for this realm or application
*
* @return
*/
@GET
@NoCache
@Produces("application/json")
@ -56,6 +61,13 @@ public class RoleContainerResource extends RoleResource {
return roles;
}
/**
* Create a new role for this realm or application
*
* @param uriInfo
* @param rep
* @return
*/
@POST
@Consumes("application/json")
public Response createRole(final @Context UriInfo uriInfo, final RoleRepresentation rep) {
@ -70,6 +82,12 @@ public class RoleContainerResource extends RoleResource {
}
}
/**
* Get a role by name
*
* @param roleName role's name (not id!)
* @return
*/
@Path("{role-name}")
@GET
@NoCache
@ -84,6 +102,11 @@ public class RoleContainerResource extends RoleResource {
return getRole(roleModel);
}
/**
* Delete a role by name
*
* @param roleName role's name (not id!)
*/
@Path("{role-name}")
@DELETE
@NoCache
@ -97,6 +120,13 @@ public class RoleContainerResource extends RoleResource {
deleteRole(role);
}
/**
* Update a role by name
*
* @param roleName role's name (not id!)
* @param rep
* @return
*/
@Path("{role-name}")
@PUT
@Consumes("application/json")
@ -115,6 +145,12 @@ public class RoleContainerResource extends RoleResource {
}
}
/**
* Add a composite to this role
*
* @param roleName role's name (not id!)
* @param roles
*/
@Path("{role-name}/composites")
@POST
@Consumes("application/json")
@ -128,6 +164,12 @@ public class RoleContainerResource extends RoleResource {
addComposites(roles, role);
}
/**
* List composites of this role
*
* @param roleName role's name (not id!)
* @return
*/
@Path("{role-name}/composites")
@GET
@NoCache
@ -142,6 +184,12 @@ public class RoleContainerResource extends RoleResource {
return getRoleComposites(role);
}
/**
* Get realm-level roles of this role's composite
*
* @param roleName role's name (not id!)
* @return
*/
@Path("{role-name}/composites/realm")
@GET
@NoCache
@ -156,6 +204,13 @@ public class RoleContainerResource extends RoleResource {
return getRealmRoleComposites(role);
}
/**
* An app-level roles for a specific app for this role's composite
*
* @param roleName role's name (not id!)
* @param appName
* @return
*/
@Path("{role-name}/composites/application/{app}")
@GET
@NoCache
@ -172,6 +227,12 @@ public class RoleContainerResource extends RoleResource {
}
/**
* Remove roles from this role's composite
*
* @param roleName role's name (not id!)
* @param roles roles to remove
*/
@Path("{role-name}/composites")
@DELETE
@Consumes("application/json")

View file

@ -28,6 +28,8 @@ import java.util.Map;
import java.util.Set;
/**
* Base class for managing the scope mappings of a specific client (application or oauth).
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -44,6 +46,11 @@ public class ScopeMappedResource {
this.session = session;
}
/**
* Get all scope mappings for this client
*
* @return
*/
@GET
@Produces("application/json")
@NoCache
@ -82,6 +89,11 @@ public class ScopeMappedResource {
return all;
}
/**
* Get list of realm-level roles associated with this client's scope.
*
* @return
*/
@Path("realm")
@GET
@Produces("application/json")
@ -97,6 +109,11 @@ public class ScopeMappedResource {
return realmMappingsRep;
}
/**
* Get list of realm-level roles that are available to attach to this client's scope.
*
* @return
*/
@Path("realm/available")
@GET
@Produces("application/json")
@ -117,6 +134,13 @@ public class ScopeMappedResource {
return available;
}
/**
* Get all effective realm-level roles that are associated with this client's scope. What this does is recurse
* any composite roles associated with the client's scope and adds the roles to this lists. The method is really
* to show a comprehensive total view of realm-level roles associated with the client.
*
* @return
*/
@Path("realm/composite")
@GET
@Produces("application/json")
@ -136,6 +160,11 @@ public class ScopeMappedResource {
return composite;
}
/**
* Add a set of realm-level roles to the client's scope
*
* @param roles
*/
@Path("realm")
@POST
@Consumes("application/json")
@ -153,6 +182,11 @@ public class ScopeMappedResource {
}
/**
* Remove a set of realm-level roles from the client's scope
*
* @param roles
*/
@Path("realm")
@DELETE
@Consumes("application/json")
@ -176,6 +210,12 @@ public class ScopeMappedResource {
}
}
/**
* Get the roles associated with a client's scope for a specific application.
*
* @param appName roles associated with client's scope for a specific application
* @return
*/
@Path("applications/{app}")
@GET
@Produces("application/json")
@ -197,6 +237,12 @@ public class ScopeMappedResource {
return mapRep;
}
/**
* The available application-level roles that can be associated with the client's scope
*
* @param appName available roles for a specific application
* @return
*/
@Path("applications/{app}/available")
@GET
@Produces("application/json")
@ -214,6 +260,12 @@ public class ScopeMappedResource {
return getAvailable(roles);
}
/**
* Get effective application roles that are associated with the client's scope for a specific application.
*
* @param appName effective roles for a specific app
* @return
*/
@Path("applications/{app}/composite")
@GET
@Produces("application/json")
@ -231,6 +283,12 @@ public class ScopeMappedResource {
return getComposite(roles);
}
/**
* Add application-level roles to the client's scope
*
* @param appName
* @param roles
*/
@Path("applications/{app}")
@POST
@Consumes("application/json")
@ -253,6 +311,12 @@ public class ScopeMappedResource {
}
/**
* Remove application-level roles from the client's scope.
*
* @param appName
* @param roles
*/
@Path("applications/{app}")
@DELETE
@Consumes("application/json")

View file

@ -26,6 +26,11 @@ public class ServerInfoAdminResource {
@Context
private ProviderSession providers;
/**
* Returns a list of themes, social providers, auth providers, and audit listeners available on this server
*
* @return
*/
@GET
public ServerInfoRepresentation getInfo() {
ServerInfoRepresentation info = new ServerInfoRepresentation();

View file

@ -58,6 +58,8 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Base resource for managing users
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@ -91,6 +93,13 @@ public class UsersResource {
protected KeycloakSession session;
/**
* Update the user
*
* @param username user name (not id!)
* @param rep
* @return
*/
@Path("{username}")
@PUT
@Consumes("application/json")
@ -110,6 +119,13 @@ public class UsersResource {
}
}
/**
* Create a new user. Must be a unique username!
*
* @param uriInfo
* @param rep
* @return
*/
@POST
@Consumes("application/json")
public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) {
@ -153,6 +169,12 @@ public class UsersResource {
}
}
/**
* Get represenation of the user
*
* @param username username (not id!)
* @return
*/
@Path("{username}")
@GET
@NoCache
@ -167,6 +189,15 @@ public class UsersResource {
return ModelToRepresentation.toRepresentation(user);
}
/**
* For each application with an admin URL, query them for the set of users logged in. This not as reliable
* as getSessions().
*
* @See getSessions
*
* @param username
* @return
*/
@Path("{username}/session-stats")
@GET
@NoCache
@ -188,6 +219,12 @@ public class UsersResource {
return stats;
}
/**
* List set of sessions associated with this user.
*
* @param username
* @return
*/
@Path("{username}/sessions")
@GET
@NoCache
@ -208,6 +245,12 @@ public class UsersResource {
return reps;
}
/**
* List set of social logins associated with this user.
*
* @param username
* @return
*/
@Path("{username}/social-links")
@GET
@NoCache
@ -227,6 +270,12 @@ public class UsersResource {
return result;
}
/**
* Remove all user sessions associated with this user. And, for all applications that have an admin URL, tell
* them to invalidate the sessions for this particular user.
*
* @param username username (not id!)
*/
@Path("{username}/logout")
@POST
public void logout(final @PathParam("username") String username) {
@ -241,7 +290,11 @@ public class UsersResource {
new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), null);
}
/**
* delete this user
*
* @param username username (not id!)
*/
@Path("{username}")
@DELETE
@NoCache
@ -251,6 +304,16 @@ public class UsersResource {
realm.removeUser(username);
}
/**
* Query list of users. May pass in query criteria
*
* @param search string contained in username, first or last name, or email
* @param last
* @param first
* @param email
* @param username
* @return
*/
@GET
@NoCache
@Produces("application/json")
@ -294,6 +357,12 @@ public class UsersResource {
return results;
}
/**
* Get role mappings for this user
*
* @param username username (not id!)
* @return
*/
@Path("{username}/role-mappings")
@GET
@Produces("application/json")
@ -339,6 +408,12 @@ public class UsersResource {
return all;
}
/**
* Get realm-level role mappings for this user
*
* @param username username (not id!)
* @return
*/
@Path("{username}/role-mappings/realm")
@GET
@Produces("application/json")
@ -359,6 +434,12 @@ public class UsersResource {
return realmMappingsRep;
}
/**
* Effective realm-level role mappings for this user. Will recurse all composite roles to get this list.
*
* @param username username (not id!)
* @return
*/
@Path("{username}/role-mappings/realm/composite")
@GET
@Produces("application/json")
@ -381,6 +462,12 @@ public class UsersResource {
return realmMappingsRep;
}
/**
* Realm-level roles that can be mapped to this user
*
* @param username username (not id!)
* @return
*/
@Path("{username}/role-mappings/realm/available")
@GET
@Produces("application/json")
@ -397,6 +484,12 @@ public class UsersResource {
return getAvailableRoles(user, available);
}
/**
* Add realm-level role mappings
*
* @param username username (not id!)
* @param roles
*/
@Path("{username}/role-mappings/realm")
@POST
@Consumes("application/json")
@ -420,6 +513,12 @@ public class UsersResource {
}
/**
* Delete realm-level role mappings
*
* @param username username (not id!)
* @param roles
*/
@Path("{username}/role-mappings/realm")
@DELETE
@Consumes("application/json")
@ -449,6 +548,13 @@ public class UsersResource {
}
}
/**
* Get application-level role mappings for this user for a specific app
*
* @param username username (not id!)
* @param appName app name (not id!)
* @return
*/
@Path("{username}/role-mappings/applications/{app}")
@GET
@Produces("application/json")
@ -478,6 +584,13 @@ public class UsersResource {
return mapRep;
}
/**
* Get effective application-level role mappings. This recurses any composite roles
*
* @param username username (not id!)
* @param appName app name (not id!)
* @return
*/
@Path("{username}/role-mappings/applications/{app}/composite")
@GET
@Produces("application/json")
@ -507,6 +620,13 @@ public class UsersResource {
return mapRep;
}
/**
* Get available application-level roles that can be mapped to the user
*
* @param username username (not id!)
* @param appName app name (not id!)
* @return
*/
@Path("{username}/role-mappings/applications/{app}/available")
@GET
@Produces("application/json")
@ -544,6 +664,13 @@ public class UsersResource {
return mappings;
}
/**
* Add applicaiton-level roles to the user role mapping.
*
* @param username username (not id!)
* @param appName app name (not id!)
* @param roles
*/
@Path("{username}/role-mappings/applications/{app}")
@POST
@Consumes("application/json")
@ -572,6 +699,13 @@ public class UsersResource {
}
/**
* Delete application-level roles from user role mapping.
*
* @param username username (not id!)
* @param appName app name (not id!)
* @param roles
*/
@Path("{username}/role-mappings/applications/{app}")
@DELETE
@Consumes("application/json")
@ -610,6 +744,13 @@ public class UsersResource {
}
}
/**
* Set up a temporary password for this user. User will have to reset this temporary password when they log
* in next.
*
* @param username username (not id!)
* @param pass temporary password
*/
@Path("{username}/reset-password")
@PUT
@Consumes("application/json")
@ -629,6 +770,11 @@ public class UsersResource {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
/**
*
*
* @param username username (not id!)
*/
@Path("{username}/remove-totp")
@PUT
@Consumes("application/json")
@ -643,6 +789,12 @@ public class UsersResource {
user.setTotp(false);
}
/**
* Send an email to the user with a link they can click to reset their password
*
* @param username username (not id!)
* @return
*/
@Path("{username}/reset-password-email")
@PUT
@Consumes("application/json")