From b292fd802362b56808daaeea0119c70e8bc91dd0 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Sat, 18 Jul 2026 15:01:26 +0200 Subject: [PATCH 1/3] Give a meaningful error message when the necessary role is missing --- .../attribute_endpoints/AttributeEndpointsResourceProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/attribute-endpoints-provider/src/main/java/de/ccc/hamburg/keycloak/attribute_endpoints/AttributeEndpointsResourceProvider.java b/attribute-endpoints-provider/src/main/java/de/ccc/hamburg/keycloak/attribute_endpoints/AttributeEndpointsResourceProvider.java index bd98b30..15303dc 100644 --- a/attribute-endpoints-provider/src/main/java/de/ccc/hamburg/keycloak/attribute_endpoints/AttributeEndpointsResourceProvider.java +++ b/attribute-endpoints-provider/src/main/java/de/ccc/hamburg/keycloak/attribute_endpoints/AttributeEndpointsResourceProvider.java @@ -109,6 +109,7 @@ public class AttributeEndpointsResourceProvider implements RealmResourceProvider UserModel authUser = auth.getUser(); if (!authUser.hasRole(authRole)) { + LOG.info("User " + authUser.getUsername() + " does not have required role " + authRole.getName()); throw new ForbiddenException("User does not have required auth role."); } From 5e279f8c711006a9c32da31a945ac181050f65ca Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Sat, 18 Jul 2026 15:01:48 +0200 Subject: [PATCH 2/3] Assign roles to service account user, not the client --- testing/import/testing.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testing/import/testing.json b/testing/import/testing.json index a91df6c..3744ff8 100644 --- a/testing/import/testing.json +++ b/testing/import/testing.json @@ -470,7 +470,7 @@ "credentials" : [ ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "default-roles-testing" ], + "realmRoles" : [ "export-dooris-ssh-keys", "default-roles-testing" ], "notBefore" : 0, "groups" : [ ] }, { @@ -484,7 +484,7 @@ "credentials" : [ ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "default-roles-testing" ], + "realmRoles" : [ "export-mailing-list-addresses", "default-roles-testing" ], "notBefore" : 0, "groups" : [ ] }, { @@ -1478,7 +1478,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] } }, { "id" : "a49de9bf-462b-4c61-bb52-0373732f4b1b", @@ -1538,7 +1538,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ] } }, { "id" : "dd4024e9-f080-4319-aeb7-7f9906c345c5", @@ -1625,8 +1625,8 @@ "subComponents" : { }, "config" : { "match-role" : [ "mailing-list-chaos-member" ], - "attribute-group" : [ "mailing-list-addresses" ], "auth-role" : [ "export-mailing-list-addresses" ], + "attribute-group" : [ "mailing-list-addresses" ], "slug" : [ "mailing-list-addresses-chaos" ] } } ] From f586e5feee99c7e46bfb901e6c8becbfd42b4916 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Sat, 18 Jul 2026 15:02:12 +0200 Subject: [PATCH 3/3] Integration test to demonstrate ssh key export --- testing/README.md | 49 ++++++++++++++++++++++++++-- testing/client-test-export-dooris.sh | 20 ++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100755 testing/client-test-export-dooris.sh diff --git a/testing/README.md b/testing/README.md index 86ff971..2c5600e 100644 --- a/testing/README.md +++ b/testing/README.md @@ -23,12 +23,26 @@ The copying of the database is necessary to avoid locking errors. docker compose exec -it keycloak sh -c "cp -rp /opt/keycloak/data/h2 /tmp && env -i -- /opt/keycloak/bin/kc.sh export --db dev-file --db-url 'jdbc:h2:file:/tmp/h2/keycloakdb;NON_KEYWORDS=VALUE' --file /opt/keycloak/data/import/testing.json --realm testing" ``` -## Testing Realm Configuration +## Running Integration Tests + +This directory also contains shell scripts that exercise the attribute endpoint. + +Bring up Keycloak with `docker compose up -d`, then run one of the `client-test-export-`*`.sh` scripts. + +```shell +$ ./client-test-export-dooris.sh +[ + "hacker-ssh-key-1", + "hacker-ssh-key-2" +] +``` + +## Realm `testing` Configuration The realm contains these objects: * Clients: - * `export-dooris-ssh-keys` with secret `export-dooris-ssh-keys-secret` and role `export-dooris-ssh-keys` + * `export-dooris-ssh-keys` with secret `export-dooris-ssh-keys-secret` and user `service-account-export-dooris-ssh-keys` * `export-mailing-list-addresses` with secret `export-mailing-list-addresses-secret` and role `export-mailing-list-addresses` * Realm Roles: * `dooris-authorized` @@ -54,3 +68,34 @@ The realm contains these objects: * Dooris SSH Keys: * SSH Key 1: `hacker-ssh-key-1` * SSH Key 2: `hacker-ssh-key-2` + * `service-account-export-dooris-ssh-keys` + * Role `export-dooris-ssh-keys` + * `service-account-export-mailing-list-addresses` + * Role `export-mailing-list-addresses` +* Realm Settings: + * User Profile: + * Attribute Groups: + * `dooris-ssh-keys` + * `mailing-list-addresses` + * Attributes: + * `mailing-list-address-chaos` + * Attribute Group `mailing-list-addresses` + * `mailing-list-address-intern` + * Attribute Group `mailing-list-intern` + * `ssh-key-1` + * Attribute Group `dooris-ssh-keys` + * `ssh-key-2` + * Attribute Group `dooris-ssh-keys` +* Custom Attributes: + * Slug `dooris-ssh-keys` + * Attribute Group `dooris-ssh-keys` + * Match Role `dooris-authorized` + * Auth Role `export-dooris-ssh-keys` + * Slug `mailing-list-addresses-chaos` + * Attribute Group `mailing-list-addresses` + * Match Role `mailing-list-chaos-member` + * Auth Role `export-mailing-list-addresses` + * Slug `mailing-list-addresses-intern` + * Attribute Group `mailing-list-addresses` + * Match Role `mailing-list-intern-member` + * Auth Role `export-mailing-list-addresses` diff --git a/testing/client-test-export-dooris.sh b/testing/client-test-export-dooris.sh new file mode 100755 index 0000000..b6d4696 --- /dev/null +++ b/testing/client-test-export-dooris.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# +# Use curl to run a test export of Dooris ssh keys +# + +set -e + +ACCESS_TOKEN="$(curl -s --request POST \ + --url http://localhost:8080/realms/testing/protocol/openid-connect/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data scope=openid \ + --data client_id=export-dooris-ssh-keys \ + --data client_secret=export-dooris-ssh-keys-secret \ + --data grant_type=client_credentials | jq --raw-output .access_token -)" + +curl -s --request GET \ + --url http://localhost:8080/realms/testing/attribute-endpoints-provider/export/dooris-ssh-keys \ + --header "authorization: Bearer ${ACCESS_TOKEN}" \ + --header "content-type: application/json" | jq . - \ No newline at end of file