Prevent user from removing built-in client scopes (#27134)

Closes #26937

Signed-off-by: Kaustubh B <kbawanka@redhat.com>
This commit is contained in:
kaustubh-rh 2024-02-26 15:46:23 +05:30 committed by GitHub
parent 83af01c4c0
commit 03f6cda85a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 57 additions and 27 deletions

View file

@ -282,6 +282,7 @@ samlKeysExportError=无法导出密钥,因为:{{error}}
webAuthnPolicyCreateTimeout=超时 webAuthnPolicyCreateTimeout=超时
comparison=对比 comparison=对比
deletedSuccessClientScope=客户端作用域已删除 deletedSuccessClientScope=客户端作用域已删除
notAllowedToDeleteAllClientScopes=您不能删除所有客户端作用域
notBeforeError=清除领域的“不早于”时出错\: {{error}} notBeforeError=清除领域的“不早于”时出错\: {{error}}
columnDisplayName=展示名称 columnDisplayName=展示名称
noUsersFoundErrorStorage=找不到用户,可能是由于错误配置了联合提供程序{{error}} noUsersFoundErrorStorage=找不到用户,可能是由于错误配置了联合提供程序{{error}}

View file

@ -305,6 +305,7 @@ webAuthnPolicyCreateTimeout=Timeout
comparison=Comparison comparison=Comparison
passwordPoliciesHelp.digits=The number of numerical digits required in the password string. passwordPoliciesHelp.digits=The number of numerical digits required in the password string.
deletedSuccessClientScope=The client scope has been deleted deletedSuccessClientScope=The client scope has been deleted
notAllowedToDeleteAllClientScopes=You are not allowed to delete all the client scopes.
columnDisplayName=Display name columnDisplayName=Display name
noUsersFoundErrorStorage=No users found, could be due to wrongly configured federated provider {{error}} noUsersFoundErrorStorage=No users found, could be due to wrongly configured federated provider {{error}}
lookAround=Look around window lookAround=Look around window

View file

@ -157,6 +157,9 @@ export default function ClientScopesSection() {
continueButtonLabel: "delete", continueButtonLabel: "delete",
continueButtonVariant: ButtonVariant.danger, continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => { onConfirm: async () => {
const clientScopes = await adminClient.clientScopes.find();
const clientScopeLength = Object.keys(clientScopes).length;
if (clientScopeLength - selectedScopes.length > 0) {
try { try {
for (const scope of selectedScopes) { for (const scope of selectedScopes) {
try { try {
@ -174,6 +177,9 @@ export default function ClientScopesSection() {
} catch (error) { } catch (error) {
addError("deleteErrorClientScope", error); addError("deleteErrorClientScope", error);
} }
} else {
addError(t("notAllowedToDeleteAllClientScopes"), "error");
}
}, },
}); });

View file

@ -47,6 +47,7 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -148,8 +149,10 @@ public class ClientScopeResource {
@Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENT_SCOPES) @Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENT_SCOPES)
@Operation(summary = "Delete the client scope") @Operation(summary = "Delete the client scope")
public Response deleteClientScope() { public Response deleteClientScope() {
auth.clients().requireManage(clientScope);
auth.clients().requireManage(clientScope);
long clientScopesCount = Arrays.stream(realm.getClientScopesStream().toArray()).count();
if (clientScopesCount > 1) {
try { try {
realm.removeClientScope(clientScope.getId()); realm.removeClientScope(clientScope.getId());
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).success(); adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).success();
@ -157,6 +160,9 @@ public class ClientScopeResource {
} catch (ModelException me) { } catch (ModelException me) {
throw ErrorResponse.error(me.getMessage(), Response.Status.BAD_REQUEST); throw ErrorResponse.error(me.getMessage(), Response.Status.BAD_REQUEST);
} }
} else {
throw ErrorResponse.error("You are not allowed to delete all the client scopes.", Response.Status.FORBIDDEN);
}
} }
/** /**

View file

@ -33,12 +33,7 @@ import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.*;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ErrorRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.DisableFeature; import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
@ -841,6 +836,19 @@ public class ClientScopeTest extends AbstractClientTest {
} }
@Test
public void deleteAllClientScopesMustFail() {
List<ClientScopeRepresentation> clientScopes = clientScopes().findAll();
for (int i = 0; i < clientScopes.size(); i++) {
ClientScopeRepresentation clientScope = clientScopes.get(i);
if (i != clientScopes.size() - 1) {
removeClientScope(clientScope.getId());
} else {
removeClientScopeMustFail(clientScope.getId());
}
}
}
private void handleExpectedCreateFailure(ClientScopeRepresentation scopeRep, int expectedErrorCode, String expectedErrorMessage) { private void handleExpectedCreateFailure(ClientScopeRepresentation scopeRep, int expectedErrorCode, String expectedErrorMessage) {
try(Response resp = clientScopes().create(scopeRep)) { try(Response resp = clientScopes().create(scopeRep)) {
Assert.assertEquals(expectedErrorCode, resp.getStatus()); Assert.assertEquals(expectedErrorCode, resp.getStatus());
@ -876,4 +884,12 @@ public class ClientScopeTest extends AbstractClientTest {
assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientScopeResourcePath(clientScopeId), ResourceType.CLIENT_SCOPE); assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientScopeResourcePath(clientScopeId), ResourceType.CLIENT_SCOPE);
} }
private void removeClientScopeMustFail(String clientScopeId) {
try {
clientScopes().get(clientScopeId).remove();
} catch (Exception expected) {
}
}
} }