Skip to content

Commit

Permalink
Missing auth checks in some admin endpoints (#166)
Browse files Browse the repository at this point in the history
Closes keycloak/keycloak-private#156

Signed-off-by: rmartinc <rmartinc@redhat.com>
  • Loading branch information
rmartinc committed May 21, 2024
1 parent 76353a2 commit d9f0c84
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
*/
package org.keycloak.services.resources.admin;

import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.common.ClientConnection;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
Expand Down Expand Up @@ -89,6 +87,7 @@ public Response testLDAPConnection(@FormParam("action") String action, @FormPara
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
auth.realm().requireManageRealm();
try {
LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
return Response.noContent().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import jakarta.ws.rs.core.MediaType;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.userprofile.UserProfile;
import org.keycloak.userprofile.UserProfileProvider;
import org.keycloak.utils.StringUtil;
Expand All @@ -30,10 +30,12 @@
public class UserResource {

private final KeycloakSession session;
private final AdminPermissionEvaluator auth;
private final UserModel user;

public UserResource(KeycloakSession session, UserModel user) {
public UserResource(KeycloakSession session, AdminPermissionEvaluator auth, UserModel user) {
this.session = session;
this.auth = auth;
this.user = user;
}

Expand All @@ -42,6 +44,8 @@ public UserResource(KeycloakSession session, UserModel user) {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, List<String>> getUnmanagedAttributes() {
auth.users().requireView(user);

UserProfileProvider provider = session.getProvider(UserProfileProvider.class);

UserProfile profile = provider.create(USER_API, user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ public UserResource getUser(@PathParam("id") String id) {
else throw new ForbiddenException();
}

return new UserResource(session, user);
return new UserResource(session, auth, user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public ClientRegistrationPolicyResource(KeycloakSession session, AdminPermission
@Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENT_REGISTRATION_POLICY)
@Operation( summary="Base path for retrieve providers with the configProperties properly filled")
public Stream<ComponentTypeRepresentation> getProviders() {
auth.realm().requireViewRealm();
return session.getKeycloakSessionFactory().getProviderFactoriesStream(ClientRegistrationPolicy.class)
.map((ProviderFactory factory) -> {
ClientRegistrationPolicyFactory clientRegFactory = (ClientRegistrationPolicyFactory) factory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.junit.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.BearerAuthFilter;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.Profile;
import org.keycloak.models.AdminRoles;
Expand Down Expand Up @@ -51,6 +52,7 @@
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.TestLdapConnectionRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
Expand All @@ -73,6 +75,8 @@
import org.keycloak.userprofile.DeclarativeUserProfileProvider;

import jakarta.ws.rs.ClientErrorException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.lang.reflect.Method;
import java.util.Arrays;
Expand Down Expand Up @@ -375,7 +379,11 @@ public void invoke(RealmResource realm) {

invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
response.set(realm.testLDAPConnection("nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch"));
TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation(
"nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch");
response.set(realm.testLDAPConnection(config.getAction(), config.getConnectionUrl(), config.getBindDn(),
config.getBindCredential(), config.getUseTruststoreSpi(), config.getConnectionTimeout()));
response.set(realm.testLDAPConnection(config));
}
}, Resource.REALM, true);

Expand Down Expand Up @@ -1458,6 +1466,21 @@ public void invoke(RealmResource realm) {
realm.users().get(user.getId()).toRepresentation();
}
}, Resource.USER, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
// no-op
}
public void invoke(Keycloak keycloak, RealmResource realm, AtomicReference<Response> response) {
try (Client client = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) {
Response resp = client.target(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth")
.path("/admin/realms/" + realm.toRepresentation().getRealm() + "/ui-ext/users/" + user.getId() + "/unmanagedAttributes")
.register(new BearerAuthFilter(keycloak.tokenManager()))
.request(MediaType.APPLICATION_JSON)
.get();
response.set(resp);
}
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.users().get(user.getId()).update(user);
Expand Down Expand Up @@ -1757,6 +1780,11 @@ public void invoke(RealmResource realm) {
realm.components().query("nosuch");
}
}, Resource.REALM, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.clientRegistrationPolicy().getProviders();
}
}, Resource.REALM, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
response.set(realm.components().add(new ComponentRepresentation()));
Expand Down Expand Up @@ -1945,7 +1973,7 @@ private void invoke(InvocationWithResponse invocation, Keycloak client, boolean
int statusCode;
try {
AtomicReference<Response> responseReference = new AtomicReference<>();
invocation.invoke(client.realm(REALM_NAME), responseReference);
invocation.invoke(client, client.realm(REALM_NAME), responseReference);
Response response = responseReference.get();
if (response != null) {
statusCode = response.getStatus();
Expand Down Expand Up @@ -2054,6 +2082,9 @@ public interface InvocationWithResponse {

void invoke(RealmResource realm, AtomicReference<Response> response);

default void invoke(Keycloak keycloak, RealmResource realm, AtomicReference<Response> response) {
invoke(realm, response);
}
}

private void assertGettersEmpty(RealmRepresentation rep) {
Expand Down

0 comments on commit d9f0c84

Please sign in to comment.