API Reference

Complete REST API reference for all six admin-service controllers — Keycloak clients, realm roles, Vault AppRoles, KV secrets, HCL policies, and Kafka topic admin.

Authentication

All endpoints require a valid Keycloak-issued JWT passed as a Bearer token. Obtain a token from the Keycloak token endpoint before making requests.

> Bash code-highlight
# Get a token (client credentials flow)
curl -X POST http://localhost:5433/realms/event-based-banking-application/protocol/openid-connect/token \
  -d "grant_type=client_credentials" \
  -d "client_id=admin-service-client" \
  -d "client_secret=<secret>"

All admin endpoints additionally require the ROLE_ADMIN realm role to be present in the JWT's realm_access.roles claim. The /internal/** paths require ROLE_INTERNAL_SERVICE instead.


Swagger UI

The interactive API documentation is available at:

> Text code-highlight
http://localhost:8089/swagger-ui/index.html

The OpenAPI JSON spec is available at:

> Text code-highlight
http://localhost:8089/v3/api-docs

Keycloak — Client Management

Controller: ClientCreationController
Base Path: /api/admin

MethodPathRequired RoleDescription
POST/api/admin/inter-service-clientscreate-client (ROLE_ADMIN)Creates a new confidential Keycloak client for inter-service OAuth2

POST /api/admin/inter-service-clients

Creates a new Keycloak OAuth2 client configured for the client_credentials (service accounts) flow.

Request:

> Bash code-highlight
curl -X POST http://localhost:8089/api/admin/inter-service-clients?clientName=my-new-service \
  -H "Authorization: Bearer <token>"

Response:

> Json code-highlight
{
  "clientId": "my-new-service",
  "clientSecret": "generated-secret-value"
}

Error cases:

HTTP StatusCondition
409 ConflictClient with that name already exists
403 ForbiddenCaller does not have ROLE_ADMIN

What it does internally:

  1. Checks if a client with the given name already exists — throws KeyCloakClientAlreadyExists (409) if so.
  2. Builds a ClientRepresentation with service accounts enabled, standard flow disabled, access type confidential.
  3. Creates the client via Keycloak REST; throws KeyCloakServiceException if the response is not 201.
  4. Assigns the INTERNAL_SERVICE realm role to the newly created client's service account user.
  5. Returns the clientId and generated clientSecret.

Keycloak — Realm Roles

Controller: KeyCloakRolesController
Base Path: /api/admin

MethodPathRequired RoleDescription
GET/api/admin/realm-rolesquery-realm (ROLE_ADMIN)Lists all realm roles
GET/api/admin/realm-role?roleName=query-realm (ROLE_ADMIN)Fetches a single realm role by name
POST/api/admin/realm-rolesquery-realm (ROLE_ADMIN)Creates a new realm role

GET /api/admin/realm-roles

> Bash code-highlight
curl http://localhost:8089/api/admin/realm-roles \
  -H "Authorization: Bearer <token>"

Returns List<KeycloakRole>.

GET /api/admin/realm-role

> Bash code-highlight
curl "http://localhost:8089/api/admin/realm-role?roleName=ROLE_ADMIN" \
  -H "Authorization: Bearer <token>"

Returns a single KeycloakRole or 404 if the role does not exist.

POST /api/admin/realm-roles

> Bash code-highlight
curl -X POST http://localhost:8089/api/admin/realm-roles \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "ROLE_ANALYST", "description": "Read-only analyst role"}'

Returns a KeyCloakResponse with responseCode: KEYCLOAK_ROLE_CREATED_200.


Vault — AppRole Management

Controller: VaultAppRoleController
Base Path: /api/admin
Required Role: vault-ops (ROLE_ADMIN) for all endpoints.

MethodPathDescription
GET/api/admin/vault-approleLists all AppRole names
GET/api/admin/vault-approle/{role}Gets details for a specific AppRole
POST/api/admin/vault-approleCreates a new AppRole with policies
DELETE/api/admin/vault-approle?role=Deletes an AppRole
GET/api/admin/vault-approle/secrets?appRole=Generates fresh roleId + secretId credentials

POST /api/admin/vault-approle

> Bash code-highlight
curl -X POST http://localhost:8089/api/admin/vault-approle \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "roleName": "user-service",
    "policies": ["user-service-policy"]
  }'

Creates the AppRole with token_ttl=1h and token_max_ttl=4h. Returns an AppRoleResponseDto containing the generated roleId and secretId.

GET /api/admin/vault-approle/secrets

> Bash code-highlight
curl "http://localhost:8089/api/admin/vault-approle/secrets?appRole=user-service" \
  -H "Authorization: Bearer <token>"

Generates and returns a fresh secretId for an existing AppRole. Use this to rotate credentials after expiry.


Vault — KV Secrets

Controller: VaultOperationsController
Base Path: /api/admin

Secret path pattern: secret/arya-banking/{service}/dev

MethodPathRequired RoleDescription
POST/api/admin/vault-secretsvault-ops (ROLE_ADMIN)Creates a KV secret
PUT/api/admin/vault-secretsvault-ops (ROLE_ADMIN)Updates (patches) a KV secret
GET/api/admin/vault-secrets?service=vault-ops (ROLE_ADMIN)Reads all secrets for a service
DELETE/api/admin/vault-secrets?service=⚠️ NoneDeletes all secrets for a service path

The DELETE /api/admin/vault-secrets endpoint is missing @PreAuthorize. Any authenticated user (not just ROLE_ADMIN) can call it. This is a known issue and must be fixed before any production use.

POST /api/admin/vault-secrets

> Bash code-highlight
curl -X POST http://localhost:8089/api/admin/vault-secrets \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "service": "user-service",
    "secrets": [
      {"key": "db.password", "secret": "s3cr3tP@ss"},
      {"key": "api.key", "secret": "abc123"}
    ]
  }'

Writes each entry to secret/arya-banking/user-service/dev (put per secret on create, patch per secret on update). Batch shape since Sep 2026 — the old single secretKey/secretValue fields no longer exist.

PUT /api/admin/vault-secrets

Uses KV v2 PATCH semantics per secret entry — only the specified keys are merged; other keys in the same path are preserved.


Vault — Policy Management

Controller: VaultPolicyController
Base Path: /api/admin
Required Role: vault-ops (ROLE_ADMIN) for all endpoints.

MethodPathDescription
GET/api/admin/vault/policiesLists all ACL policy names from sys/policies/acl/
POST/api/admin/vault/policies?service=Uploads {service}-policy.hcl from classpath to Vault
DELETE/api/admin/vault/policies?service=Deletes the ACL policy for a service

POST /api/admin/vault/policies

> Bash code-highlight
# Upload the admin-service policy
curl -X POST "http://localhost:8089/api/admin/vault/policies?service=admin-service" \
  -H "Authorization: Bearer <token>"

# Upload the user-service policy
curl -X POST "http://localhost:8089/api/admin/vault/policies?service=user-service" \
  -H "Authorization: Bearer <token>"

The service resolves the policy file as {service}-policy.hcl from the classpath (src/main/resources/). The file is read via CommonUtils.loadConfig(path) and written to /sys/policies/acl/{service}-policy in Vault.


Kafka — Topic Admin

Controller: KafkaAdminController Base Path: /api/admin Required Role: kafka-ops (ROLE_ADMIN) for all endpoints. Backed by AdminClientManager (lazy singleton AdminClient from KafkaProperties) + KafkaAdminServiceImpl (5s orTimeout per call).

MethodPathDescription
GET/api/admin/topics?listInternal=&timeoutMs=List topics → List<TopicListingDto{name,isInternal}>
POST/api/admin/topicsCreate topic → ResponseDto("200","Topic created successfully") or TopicCreationException (TOPIC_CREATION_FAILED_400, 400)
GET/api/admin/topics/describe?topicNames=&timeoutMs=Describe topics → List<TopicDescriptionDto{name,partitions,isInternal,aclOperation}>

POST /api/admin/topics

> Bash code-highlight
curl -X POST http://localhost:8089/api/admin/topics \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"topicName":"user.update.event","numPartitions":3,"replicationFactor":3,"timeoutMs":5000}'

Creates a NewTopic via AdminClient.createTopics(..., validateOnly=false). Requires common-core:2.0.2+ (ResponseDto, TopicCreationException).


DTOs

DTOTypeFields
AppRole@Data classroleId, secretId
AppRoleResponseDtoJava recordroleId, secretId
CreateAppRoleDtoJava recordroleName, policies: List<String>
KeyCloakClientResponseJava recordclientId, clientSecret
KeycloakRoleJava recordcomposite, name, description, composites, attributes
VaultApiResponseDtoJava recorddata: Map<String,Object>, leastDuration, leaseId, requestId
VaultResponseDtoJava recordresponseCode, responseMessage
VaultSecretDtoJava recordservice, secrets: List<VaultSecret> (batch)
VaultSecretJava recordkey, secret
TopicCreationDtoJava recordtopicName, numPartitions, replicationFactor, timeoutMs
TopicListingDtoJava recordname, isInternal
TopicDescriptionDtoJava recordname, partitions, isInternal, aclOperation: Set<Byte>
ResponseDtoJava record (common-core:2.0.2)responseCode, message

All DTOs except AppRole are immutable Java records. AppRole is a @Data class because it is used as a configuration binding target, which requires a mutable JavaBean.

apirestendpointsswagger