Hide the 'Delete' button in the account console when DeleteCredentialAction is disabled or unavailable

closes #30204

Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
mposolda 2024-10-21 16:01:41 +02:00 committed by Marek Posolda
parent 2f27dc91e0
commit 703f16ea86
3 changed files with 32 additions and 5 deletions

View file

@ -207,7 +207,7 @@ export const SigningIn = () => {
aria-label={t("updateCredAriaLabel")} aria-label={t("updateCredAriaLabel")}
aria-labelledby={`cred-${meta.credential.id}`} aria-labelledby={`cred-${meta.credential.id}`}
> >
{container.removeable ? ( {container.removeable && (
<Button <Button
variant="danger" variant="danger"
data-testrole="remove" data-testrole="remove"
@ -221,12 +221,12 @@ export const SigningIn = () => {
> >
{t("delete")} {t("delete")}
</Button> </Button>
) : ( )}
{container.updateAction && (
<Button <Button
variant="secondary" variant="secondary"
onClick={() => { onClick={() => {
if (container.updateAction) login({ action: container.updateAction });
login({ action: container.updateAction });
}} }}
data-testrole="update" data-testrole="update"
> >

View file

@ -244,6 +244,9 @@ public class CredentialTypeMetadata implements Comparable<CredentialTypeMetadata
if (instance.createAction != null && instance.updateAction != null) { if (instance.createAction != null && instance.updateAction != null) {
throw new IllegalStateException("Both createAction and updateAction are not null when building CredentialTypeMetadata for the credential type '" + instance.type); throw new IllegalStateException("Both createAction and updateAction are not null when building CredentialTypeMetadata for the credential type '" + instance.type);
} }
if (!verifyRequiredAction(session, "delete_credential")) {
instance.removeable = false;
}
return instance; return instance;
} }

View file

@ -31,6 +31,7 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticatorFactory; import org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.WebAuthnPasswordlessAuthenticatorFactory; import org.keycloak.authentication.authenticators.browser.WebAuthnPasswordlessAuthenticatorFactory;
import org.keycloak.authentication.requiredactions.DeleteCredentialAction;
import org.keycloak.authentication.requiredactions.WebAuthnPasswordlessRegisterFactory; import org.keycloak.authentication.requiredactions.WebAuthnPasswordlessRegisterFactory;
import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory; import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory;
import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.broker.provider.util.SimpleHttp;
@ -987,10 +988,33 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
assertEquals(204, response.getStatus()); assertEquals(204, response.getStatus());
} }
// Revert - re-enable requiredAction and remove OTP credential from the user // Revert - re-enable requiredAction
setRequiredActionEnabledStatus(UserModel.RequiredAction.CONFIGURE_TOTP.name(), true); setRequiredActionEnabledStatus(UserModel.RequiredAction.CONFIGURE_TOTP.name(), true);
} }
// Issue 30204
@Test
public void testCredentialsGetWithDisabledDeleteCredentialAction() throws IOException {
// Assert OTP will be returned by default
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
assertExpectedCredentialTypes(credentials, PasswordCredentialModel.TYPE, OTPCredentialModel.TYPE);
// Assert OTP removeable
AccountCredentialResource.CredentialContainer otpCredential = credentials.get(1);
assertTrue(otpCredential.isRemoveable());
// Disable "Delete credential" action
setRequiredActionEnabledStatus(DeleteCredentialAction.PROVIDER_ID, false);
// Assert OTP not removeable
credentials = getCredentials();
otpCredential = credentials.get(1);
assertFalse(otpCredential.isRemoveable());
// Revert - re-enable requiredAction
setRequiredActionEnabledStatus(DeleteCredentialAction.PROVIDER_ID, true);
}
private void setRequiredActionEnabledStatus(String requiredActionProviderId, boolean enabled) { private void setRequiredActionEnabledStatus(String requiredActionProviderId, boolean enabled) {
RequiredActionProviderRepresentation requiredActionRep = testRealm().flows().getRequiredAction(requiredActionProviderId); RequiredActionProviderRepresentation requiredActionRep = testRealm().flows().getRequiredAction(requiredActionProviderId);
requiredActionRep.setEnabled(enabled); requiredActionRep.setEnabled(enabled);