diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationSchema.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationSchema.java new file mode 100644 index 000000000000..47d76f12e63a --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationSchema.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.representations.idm.authorization; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +public class AuthorizationSchema { + + private final Set resourceTypes; + + public AuthorizationSchema(ResourceType... resourceTypes) { + this.resourceTypes = Arrays.stream(resourceTypes).collect(Collectors.toSet()); + } + + public Set getResourceTypes() { + return Collections.unmodifiableSet(resourceTypes); + } +} diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/FineGrainedAdminPermissionsAuthorizationSchema.java b/core/src/main/java/org/keycloak/representations/idm/authorization/FineGrainedAdminPermissionsAuthorizationSchema.java new file mode 100644 index 000000000000..d246251c6791 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/FineGrainedAdminPermissionsAuthorizationSchema.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.representations.idm.authorization; + +import java.util.Arrays; +import java.util.HashSet; + +public class FineGrainedAdminPermissionsAuthorizationSchema extends AuthorizationSchema { + + public static final FineGrainedAdminPermissionsAuthorizationSchema INSTANCE = new FineGrainedAdminPermissionsAuthorizationSchema(); + + private FineGrainedAdminPermissionsAuthorizationSchema() { + super(new ResourceType("Users", new HashSet<>(Arrays.asList("manage")))); + } + +} diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceServerRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceServerRepresentation.java index f5ce88dedf50..a4dfeaf8ea16 100644 --- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceServerRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceServerRepresentation.java @@ -35,6 +35,7 @@ public class ResourceServerRepresentation { private List policies = emptyList(); private List scopes = emptyList(); private DecisionStrategy decisionStrategy; + private AuthorizationSchema authorizationSchema; public void setId(String id) { this.id = id; @@ -107,4 +108,12 @@ public void setDecisionStrategy(DecisionStrategy decisionStrategy) { public DecisionStrategy getDecisionStrategy() { return decisionStrategy; } + + public void setAuthorizationSchema(FineGrainedAdminPermissionsAuthorizationSchema authorizationSchema) { + this.authorizationSchema = authorizationSchema; + } + + public AuthorizationSchema getAuthorizationSchema() { + return authorizationSchema; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceType.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceType.java new file mode 100644 index 000000000000..6c6b383fd084 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceType.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.representations.idm.authorization; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collections; +import java.util.Set; + +public class ResourceType { + private final String type; + private final Set scopes; + + @JsonCreator + public ResourceType(@JsonProperty("type") String type, @JsonProperty("scopes") Set scopes) { + this.type = type; + this.scopes = Collections.unmodifiableSet(scopes); + } + + public String getType() { + return type; + } + + public Set getScopes() { + return Collections.unmodifiableSet(scopes); + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index b2e908582448..26ac23ba1c5e 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -1038,6 +1038,7 @@ public static ResourceServerRepresentation toRepresentation(ResourceServer model server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement()); server.setPolicyEnforcementMode(model.getPolicyEnforcementMode()); server.setDecisionStrategy(model.getDecisionStrategy()); + server.setAuthorizationSchema(Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2) ? FineGrainedAdminPermissionsAuthorizationSchema.INSTANCE : null); return server; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/admin/FineGrainedAdminPermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/admin/FineGrainedAdminPermissionsTest.java new file mode 100644 index 000000000000..bf0c44a24ac6 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/admin/FineGrainedAdminPermissionsTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.authz.admin; + +import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import org.junit.Test; +import org.keycloak.common.Profile; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.arquillian.annotation.EnableFeature; +import org.keycloak.testsuite.util.ClientBuilder; + +public class FineGrainedAdminPermissionsTest extends AbstractTestRealmKeycloakTest { + + private final String CLIENT_ID = "fgap-client"; + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + testRealm.getClients().add(ClientBuilder.create() + .clientId(CLIENT_ID) + .serviceAccount() + .authorizationServicesEnabled(true) + .build()); + } + + @Test + public void authorizationSchemaNotAvailableFeatureDisabled() { + List clients = testRealm().clients().findByClientId(CLIENT_ID); + assertThat(clients, hasSize(1)); + ResourceServerRepresentation authorizationSettings = testRealm().clients().get(clients.get(0).getId()).authorization().getSettings(); + assertThat(authorizationSettings, notNullValue()); + assertThat(authorizationSettings.getAuthorizationSchema(), nullValue()); + } + + @Test + @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) + public void authorizationSchemaNotAvailableFeatureV1Enabled() throws Exception { + reconnectAdminClient(); + List clients = testRealm().clients().findByClientId(CLIENT_ID); + assertThat(clients, hasSize(1)); + ResourceServerRepresentation authorizationSettings = testRealm().clients().get(clients.get(0).getId()).authorization().getSettings(); + assertThat(authorizationSettings, notNullValue()); + assertThat(authorizationSettings.getAuthorizationSchema(), nullValue()); + } + + @Test + @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ_V2) + public void authorizationSchemaAvailableFeatureV2Enabled() throws Exception { + reconnectAdminClient(); + List clients = testRealm().clients().findByClientId(CLIENT_ID); + assertThat(clients, hasSize(1)); + ResourceServerRepresentation authorizationSettings = testRealm().clients().get(clients.get(0).getId()).authorization().getSettings(); + assertThat(authorizationSettings, notNullValue()); + assertThat(authorizationSettings.getAuthorizationSchema(), notNullValue()); + } +}