diff --git a/compose.yaml b/compose.yaml index 93b89e9..0be3049 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,11 +1,12 @@ services: keycloak: - image: quay.io/keycloak/keycloak:latest + image: quay.io/keycloak/keycloak:26.4.2 pull_policy: always command: start-dev environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin + KC_LOG_LEVEL: info ports: - "8080:8080" volumes: diff --git a/ssh-key-provider/pom.xml b/ssh-key-provider/pom.xml index 7db5e40..32b6da5 100644 --- a/ssh-key-provider/pom.xml +++ b/ssh-key-provider/pom.xml @@ -15,9 +15,9 @@ UTF-8 - 1.7 - 1.7 - 26.4.0 + 17 + 17 + 26.4.2 @@ -43,6 +43,11 @@ keycloak-services provided + + com.google.auto.service + auto-service + 1.1.1 + @@ -108,4 +113,4 @@ - \ No newline at end of file + diff --git a/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/App.java b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/App.java deleted file mode 100644 index 39aa87d..0000000 --- a/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.ccc.hamburg.keycloak; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -} diff --git a/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/AuthHelper.java b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/AuthHelper.java new file mode 100644 index 0000000..15f8390 --- /dev/null +++ b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/AuthHelper.java @@ -0,0 +1,52 @@ +package de.ccc.hamburg.keycloak; + +import jakarta.ws.rs.ForbiddenException; +import jakarta.ws.rs.NotAuthorizedException; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.managers.AppAuthManager; +import org.keycloak.services.managers.Auth; +import org.keycloak.services.managers.AuthenticationManager; + +import java.util.function.Function; + +public class AuthHelper { + + public static AuthenticationManager.AuthResult getAuthResult(KeycloakSession session, Function authFn) { + AuthenticationManager.AuthResult auth = new AppAuthManager.BearerTokenAuthenticator(session).authenticate(); + + System.err.println(auth.getToken().getIssuedFor()); + + if (auth == null) { + throw new NotAuthorizedException("Bearer"); + } else if (!authFn.apply(auth)) { + throw new ForbiddenException(); + } + return auth; + } + + public static Auth getAuth(KeycloakSession session, Function authFn) { + return getAuth(session, getAuthResult(session, authFn)); + } + + public static Auth getAuth(KeycloakSession session, String clientId, Function authFn) { + return getAuth(session, getAuthResult(session, authFn), clientId); + } + + public static Auth getAuth(KeycloakSession session, AuthenticationManager.AuthResult authResult) { + return getAuth(session, authResult, null); + } + + public static Auth getAuth(KeycloakSession session, AuthenticationManager.AuthResult authResult, String clientId) { + RealmModel realm = session.getContext().getRealm(); + ClientModel client; + if (clientId == null) { + client = authResult.getClient(); + } else { + client = realm.getClientByClientId(clientId); + } + return new Auth(realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), false); + } + +} \ No newline at end of file diff --git a/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProvider.java b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProvider.java new file mode 100644 index 0000000..070b957 --- /dev/null +++ b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProvider.java @@ -0,0 +1,60 @@ +package de.ccc.hamburg.keycloak; + +import java.util.Map; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.managers.Auth; +import org.keycloak.services.resource.RealmResourceProvider; + +public class SSHKeyResourceProvider implements RealmResourceProvider { + private final KeycloakSession session; + + public SSHKeyResourceProvider(KeycloakSession keycloakSession) { + this.session = keycloakSession; + } + + @Override + public Object getResource() { + return this; + } + + @Override + public void close() { + } + + @GET + @Path("hello") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Public hello endpoint", description = "This endpoint returns hello and the name of the requested realm.") + @APIResponse(responseCode = "200", description = "", content = { + @Content(schema = @Schema(implementation = Response.class, type = SchemaType.OBJECT)) }) + public Response helloAnonymous() { + return Response.ok(Map.of("hello", session.getContext().getRealm().getName())).build(); + } + + @GET + @Path("hello-auth") + @Produces(MediaType.APPLICATION_JSON) + public Response helloAuthenticated() { + try { + Auth auth = AuthHelper.getAuth(session, + authResult -> authResult.getToken().getIssuedFor().equals("admin-cli")); + return Response.ok(Map.of("hello", auth.getUser().getUsername())).build(); + } catch (Exception e) { + System.err.println(e); + return Response.status(401, e.getMessage()).build(); + } + + } + +} diff --git a/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProviderFactory.java b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProviderFactory.java new file mode 100644 index 0000000..1eb8632 --- /dev/null +++ b/ssh-key-provider/src/main/java/de/ccc/hamburg/keycloak/SSHKeyResourceProviderFactory.java @@ -0,0 +1,40 @@ +package de.ccc.hamburg.keycloak; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; +import org.keycloak.services.resource.RealmResourceProviderFactory; +import org.keycloak.Config.Scope; +import com.google.auto.service.AutoService; +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; +import org.keycloak.services.resource.RealmResourceProviderFactory; + +@AutoService(RealmResourceProviderFactory.class) +public class SSHKeyResourceProviderFactory implements RealmResourceProviderFactory { + static final String PROVIDER_ID = "ssh-key-provider"; + + @Override + public RealmResourceProvider create(KeycloakSession keycloakSession) { + return new SSHKeyResourceProvider(keycloakSession); + } + + @Override + public void init(Config.Scope scope) { + } + + @Override + public void postInit(KeycloakSessionFactory keycloakSessionFactory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return PROVIDER_ID; + } +}