From ce028c91cf1d2728bbb8e922550a6fb69aa5b7e6 Mon Sep 17 00:00:00 2001
From: Erik Jan de Wit
Date: Thu, 2 Jun 2022 15:57:51 +0200
Subject: [PATCH] POC of how we could develop version 2 of the admin REST API
---
services/pom.xml | 20 ++
.../services/resources/admin/AdminRoot.java | 5 +
.../admin/ClientExampleResource.java | 48 ++++
services/src/main/resources/clients.yaml | 271 ++++++++++++++++++
4 files changed, 344 insertions(+)
create mode 100644 services/src/main/java/org/keycloak/services/resources/admin/ClientExampleResource.java
create mode 100644 services/src/main/resources/clients.yaml
diff --git a/services/pom.xml b/services/pom.xml
index bf4e4fbd4011..89cc73a15cde 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -188,6 +188,26 @@
+
+ org.openapitools
+ openapi-generator-maven-plugin
+
+
+
+ generate
+
+
+ ${project.basedir}/src/main/resources/clients.yaml
+ jaxrs-spec
+ interfaceOnly=true,useSwaggerAnnotations=false
+
+ src/gen/java/main
+ org.keycloak.services.resources.admin
+
+
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index dcb88bc0866f..262d0d85a7db 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -149,6 +149,11 @@ public AdminConsole getAdminConsole(final @PathParam("realm") String name) {
return service;
}
+ @Path("v2/clients")
+ public ClientExampleResource getNewClientResource() {
+ return new ClientExampleResource();
+ }
+
protected AdminAuth authenticateRealmAdminRequest(HttpHeaders headers) {
String tokenString = AppAuthManager.extractAuthorizationHeaderToken(headers);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientExampleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientExampleResource.java
new file mode 100644
index 000000000000..21a6176a2f7a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientExampleResource.java
@@ -0,0 +1,48 @@
+package org.keycloak.services.resources.admin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openapitools.model.Client;
+import org.openapitools.model.PartialClient;
+
+/**
+ * The interface is generated and we "only" need to implement them.
+ * PartialClient is to have type save responses instead of passing in `briefRepresentation` and only partially filling the entity
+ */
+public class ClientExampleResource implements ClientsApi {
+ @Override
+ public void createClient(Client client) {
+
+ }
+
+ @Override
+ public void deleteClient(String clientId) {
+
+ }
+
+ @Override
+ public Client getClient(String clientId) {
+ return null;
+ }
+
+ @Override
+ public List getClients() {
+ List partialClients = new ArrayList<>(2);
+ final PartialClient client = new PartialClient();
+ client.id("71e7f5d7-a093-4ddc-8847-3f45be0a662b");
+ client.setClientId("account");
+ partialClients.add(client);
+ final PartialClient client2 = new PartialClient();
+ client2.id("4389e5be-22b7-44d9-9c9c-ee4bc6da0159");
+ client2.setClientId("security-admin-console");
+ partialClients.add(client2);
+
+ return partialClients;
+ }
+
+ @Override
+ public void updateClient(String clientId, Client client) {
+
+ }
+}
diff --git a/services/src/main/resources/clients.yaml b/services/src/main/resources/clients.yaml
new file mode 100644
index 000000000000..6159f0054e32
--- /dev/null
+++ b/services/src/main/resources/clients.yaml
@@ -0,0 +1,271 @@
+openapi: 3.0.0
+info:
+ title: Admin Rest API v2
+ version: '1.0'
+ description: 'Example of how we could create a contract fist based rest api'
+servers:
+ -
+ url: '{scheme}://localhost:8180/{basePath}'
+ variables:
+ scheme:
+ enum:
+ - https
+ - http
+ default: http
+ basePath:
+ default: /admin/realms/
+paths:
+ /clients:
+ summary: Path used to manage the list of clients.
+ description: >-
+ The REST endpoint/path used to list and create zero or more `Client` entities. This path contains
+ a `GET` and `POST` operation to perform the list and create tasks, respectively.
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/PartialClient'
+ description: Successful response - returns an array of `PartialClient` entities.
+ operationId: getClients
+ summary: List All Clients
+ description: Gets a list of all `PartialClient` entities.
+ post:
+ requestBody:
+ description: A new `Client` to be created.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Client'
+ required: true
+ responses:
+ '201':
+ description: Successful response.
+ operationId: createClient
+ summary: Create a Client
+ description: Creates a new instance of a `Client`.
+ '/clients/{clientId}':
+ summary: Path used to manage a single Client.
+ description: >-
+ The REST endpoint/path used to get, update, and delete single instances of an `Client`. This path
+ contains `GET`, `PUT`, and `DELETE` operations used to perform the get, update, and delete tasks,
+ respectively.
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Client'
+ description: Successful response - returns a single `Client`.
+ operationId: getClient
+ summary: Get a Client
+ description: Gets the details of a single instance of a `Client`.
+ put:
+ requestBody:
+ description: Updated `Client` information.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Client'
+ required: true
+ responses:
+ '202':
+ description: Successful response.
+ operationId: updateClient
+ summary: Update a Client
+ description: Updates an existing `Client`.
+ delete:
+ responses:
+ '204':
+ description: Successful response.
+ operationId: deleteClient
+ summary: Delete a Client
+ description: Deletes an existing `Client`.
+ parameters:
+ -
+ name: clientId
+ description: A unique identifier for a `Client`.
+ schema:
+ type: string
+ in: path
+ required: true
+components:
+ schemas:
+ ErrorModel:
+ type: object
+ properties:
+ code:
+ type: string
+ Client:
+ title: Root Type for Client
+ description: ''
+ required:
+ - id
+ - clientId
+ - protocol
+ - access
+ type: object
+ properties:
+ id:
+ type: string
+ readOnly: true
+ clientId:
+ type: string
+ rootUrl:
+ type: string
+ adminUrl:
+ type: string
+ surrogateAuthRequired:
+ type: boolean
+ enabled:
+ type: boolean
+ alwaysDisplayInConsole:
+ type: boolean
+ clientAuthenticatorType:
+ type: string
+ redirectUris:
+ type: array
+ items:
+ type: string
+ webOrigins:
+ type: array
+ items:
+ type: string
+ notBefore:
+ format: int32
+ type: integer
+ bearerOnly:
+ type: boolean
+ consentRequired:
+ type: boolean
+ standardFlowEnabled:
+ type: boolean
+ implicitFlowEnabled:
+ type: boolean
+ directAccessGrantsEnabled:
+ type: boolean
+ serviceAccountsEnabled:
+ type: boolean
+ publicClient:
+ type: boolean
+ frontchannelLogout:
+ type: boolean
+ protocol:
+ type: string
+ attributes:
+ type: object
+ properties:
+ backchannel.logout.session.required:
+ type: string
+ backchannel.logout.revoke.offline.tokens:
+ type: string
+ authenticationFlowBindingOverrides:
+ type: object
+ fullScopeAllowed:
+ type: boolean
+ nodeReRegistrationTimeout:
+ format: int32
+ type: integer
+ defaultClientScopes:
+ type: array
+ items:
+ type: string
+ optionalClientScopes:
+ type: array
+ items:
+ type: string
+ access:
+ properties:
+ view:
+ type: boolean
+ configure:
+ type: boolean
+ manage:
+ type: boolean
+ example:
+ id: bfae6a3b-ff81-48ee-a545-99d300b0c09d
+ clientId: security-admin-console-v2
+ rootUrl: 'http://localhost:8080/'
+ adminUrl: 'http://localhost:8080/'
+ surrogateAuthRequired: false
+ enabled: true
+ alwaysDisplayInConsole: false
+ clientAuthenticatorType: client-secret
+ redirectUris:
+ - 'http://localhost:8080/*'
+ webOrigins:
+ - 'http://localhost:8080'
+ notBefore: 0
+ bearerOnly: false
+ consentRequired: false
+ standardFlowEnabled: true
+ implicitFlowEnabled: false
+ directAccessGrantsEnabled: true
+ serviceAccountsEnabled: false
+ publicClient: true
+ frontchannelLogout: false
+ protocol: openid-connect
+ attributes:
+ backchannel.logout.session.required: 'true'
+ backchannel.logout.revoke.offline.tokens: 'false'
+ authenticationFlowBindingOverrides: {}
+ fullScopeAllowed: true
+ nodeReRegistrationTimeout: -1
+ defaultClientScopes:
+ - web-origins
+ - roles
+ - profile
+ - email
+ optionalClientScopes:
+ - address
+ - phone
+ - offline_access
+ - microprofile-jwt
+ access:
+ view: true
+ configure: true
+ manage: true
+ PartialClient:
+ description: ''
+ required:
+ - id
+ - clientId
+ type: object
+ properties:
+ id:
+ description: ''
+ type: string
+ readOnly: true
+ clientId:
+ description: ''
+ type: string
+ protocol:
+ description: ''
+ type: string
+ description:
+ description: ''
+ type: string
+ baseUrl:
+ description: ''
+ type: string
+ example:
+ id: ebf0406a-5c70-4a04-8a29-25267c9d50bc
+ clientId: admin-cli
+ protocol: openid-connect
+ description: ''
+ baseUrl: /realms/master/account/
+ securitySchemes:
+ JWT:
+ type: apiKey
+ description: |
+ You can create a JSON Web Token (JWT) during auth.
+ Usage format: `Bearer `
+ name: Authorization
+ in: header
+tags:
+ -
+ name: master
\ No newline at end of file