diff --git a/COPYING b/COPYING index d645695..bd8c1e3 100644 --- a/COPYING +++ b/COPYING @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2023 Phase Two, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/main/java/io/phasetwo/keycloak/ext/auth/BaseAuthenticatorFactory.java b/src/main/java/io/phasetwo/keycloak/ext/auth/BaseAuthenticatorFactory.java index 82d68a2..9270b0b 100644 --- a/src/main/java/io/phasetwo/keycloak/ext/auth/BaseAuthenticatorFactory.java +++ b/src/main/java/io/phasetwo/keycloak/ext/auth/BaseAuthenticatorFactory.java @@ -22,11 +22,11 @@ public abstract class BaseAuthenticatorFactory private final String providerId; private final AuthenticatorConfigProperties propsProvider; - BaseAuthenticatorFactory(String providerId) { + public BaseAuthenticatorFactory(String providerId) { this(providerId, new AuthenticatorConfigProperties() {}); } - BaseAuthenticatorFactory(String providerId, AuthenticatorConfigProperties propsProvider) { + public BaseAuthenticatorFactory(String providerId, AuthenticatorConfigProperties propsProvider) { this.providerId = providerId; this.propsProvider = propsProvider; } diff --git a/src/main/java/io/phasetwo/keycloak/ext/model/jpa/entity/Entities.java b/src/main/java/io/phasetwo/keycloak/ext/model/jpa/entity/Entities.java index 816c5b3..53bca3e 100644 --- a/src/main/java/io/phasetwo/keycloak/ext/model/jpa/entity/Entities.java +++ b/src/main/java/io/phasetwo/keycloak/ext/model/jpa/entity/Entities.java @@ -4,7 +4,7 @@ public class Entities { - static void setCollection(Collection src, Collection dest) { + public static void setCollection(Collection src, Collection dest) { if (dest == null) { dest = src; } else if (dest != src) { diff --git a/src/main/java/io/phasetwo/keycloak/ext/resource/AbstractAdminResource.java b/src/main/java/io/phasetwo/keycloak/ext/resource/AbstractAdminResource.java index abfdb72..ca0f79f 100644 --- a/src/main/java/io/phasetwo/keycloak/ext/resource/AbstractAdminResource.java +++ b/src/main/java/io/phasetwo/keycloak/ext/resource/AbstractAdminResource.java @@ -4,14 +4,11 @@ import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.UriInfo; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import lombok.extern.jbosslog.JBossLog; import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.Config; import org.keycloak.common.ClientConnection; +import org.keycloak.events.EventBuilder; import org.keycloak.http.HttpRequest; import org.keycloak.http.HttpResponse; import org.keycloak.jose.jws.JWSInput; @@ -28,6 +25,7 @@ import org.keycloak.services.resources.admin.AdminEventBuilder; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissions; +import org.keycloak.services.resources.admin.permissions.ManagementPermissions; /** */ @JBossLog @@ -42,6 +40,7 @@ public abstract class AbstractAdminResource { protected T auth; protected AdminPermissionEvaluator permissions; protected AdminEventBuilder adminEvent; + protected EventBuilder event; protected UserModel user; protected RealmModel adminRealm; @@ -64,6 +63,11 @@ protected AbstractAdminResource(AbstractAdminResource parent) { this.adminRealm = parent.adminRealm; } + public T createAdminAuth( + RealmModel realm, AccessToken token, UserModel user, ClientModel client) { + return (T) new AdminAuth(realm, token, user, client); + } + public final void setup() { setupAuth(); setupEvents(); @@ -71,6 +75,15 @@ public final void setup() { setupCors(); } + public void requireAdminRole(String role) { + if (!hasAdminRole(role)) + throw new NotAuthorizedException(String.format("%s role is required", role)); + } + + public boolean hasAdminRole(String role) { + return ManagementPermissions.hasOneAdminRole(session, realm, auth, role); + } + private void setupCors() { HttpRequest request = session.getContext().getHttpRequest(); HttpResponse response = session.getContext().getHttpResponse(); @@ -132,39 +145,15 @@ private void setupAuth() { throw new NotFoundException("Could not find client for authorization"); } - user = authResult.getUser(); - - Type genericSuperClass = getClass().getGenericSuperclass(); - ParameterizedType parametrizedType = null; - while (parametrizedType == null) { - if ((genericSuperClass instanceof ParameterizedType)) { - parametrizedType = (ParameterizedType) genericSuperClass; - } else { - genericSuperClass = ((Class) genericSuperClass).getGenericSuperclass(); - } - } - - Class clazz = (Class) parametrizedType.getActualTypeArguments()[0]; - - try { - Constructor constructor = - clazz.getConstructor( - RealmModel.class, AccessToken.class, UserModel.class, ClientModel.class); - auth = (T) constructor.newInstance(new Object[] {this.realm, token, user, client}); - } catch (NoSuchMethodException - | SecurityException - | InstantiationException - | IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { - log.error("Failed to instantiate AdminAuth instance", ex); - } + this.user = authResult.getUser(); + auth = createAdminAuth(this.realm, token, this.user, client); } private void setupEvents() { adminEvent = new AdminEventBuilder(this.realm, auth, session, session.getContext().getConnection()) .realm(realm); + event = new EventBuilder(this.realm, session, connection).realm(realm); } private void setupPermissions() { diff --git a/src/main/java/io/phasetwo/keycloak/ext/util/Stats.java b/src/main/java/io/phasetwo/keycloak/ext/util/Stats.java index 58e35c3..aa9b9f7 100644 --- a/src/main/java/io/phasetwo/keycloak/ext/util/Stats.java +++ b/src/main/java/io/phasetwo/keycloak/ext/util/Stats.java @@ -13,7 +13,6 @@ public class Stats { public static final String PHASETWO_ANALYTICS_DISABLED_KEY = "PHASETWO_ANALYTICS_DISABLED"; - // todo make a redirect public static final String PHASETWO_ANALYTICS_URL = "https://eooemvvcxy5k16a.m.pipedream.net"; public static void collect(String name, String version, String commit, Object... args) { diff --git a/src/main/java/org/keycloak/services/resources/admin/permissions/ManagementPermissions.java b/src/main/java/org/keycloak/services/resources/admin/permissions/ManagementPermissions.java new file mode 100644 index 0000000..4600c23 --- /dev/null +++ b/src/main/java/org/keycloak/services/resources/admin/permissions/ManagementPermissions.java @@ -0,0 +1,13 @@ +package org.keycloak.services.resources.admin.permissions; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.resources.admin.AdminAuth; + +// I'm not sure if this is a good idea, but it seems to be the only way to get admin permissions +public class ManagementPermissions { + public static boolean hasOneAdminRole( + KeycloakSession session, RealmModel realm, AdminAuth auth, String... adminRoles) { + return new MgmtPermissions(session, realm, auth).hasOneAdminRole(adminRoles); + } +}