Add ability to enable OID4VCI Verifiable Credentials per realm (#34524)

- Added new realm property verifiableCredentialsEnabled
- Updated RealmRepresentation
- Guarded route to Oid4VCI page
- Add boolean switch to Realm settings page to control Verifiable Credentials enablement
- We now only show the Verifiable Credentials page in the nave if the "Verifiable Credentials" realm setting is enabled.

Fixes #34524

Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
This commit is contained in:
Thomas Darimont 2024-10-31 01:32:59 +01:00 committed by Marek Posolda
parent f229790ba5
commit 3315ea718a
15 changed files with 86 additions and 3 deletions

View file

@ -218,6 +218,8 @@ public class RealmRepresentation {
protected Boolean organizationsEnabled;
private List<OrganizationRepresentation> organizations;
protected Boolean verifiableCredentialsEnabled;
@Deprecated
protected Boolean social;
@Deprecated
@ -1440,6 +1442,14 @@ public class RealmRepresentation {
this.organizationsEnabled = organizationsEnabled;
}
public Boolean isVerifiableCredentialsEnabled() {
return verifiableCredentialsEnabled;
}
public void setVerifiableCredentialsEnabled(Boolean verifiableCredentialsEnabled) {
this.verifiableCredentialsEnabled = verifiableCredentialsEnabled;
}
@JsonIgnore
public Map<String, String> getAttributesOrEmpty() {
return (Map<String, String>) (attributes == null ? Collections.emptyMap() : attributes);

View file

@ -84,7 +84,7 @@ export const RootRoute: RouteObject = {
PersonalInfoRoute,
ResourcesRoute,
ContentRoute,
Oid4VciRoute,
...(environment.features.isOid4VciEnabled ? [Oid4VciRoute] : []),
],
};

View file

@ -3165,6 +3165,8 @@ logo=Logo
avatarImage=Avatar image
organizationsEnabled=Organizations
organizationsEnabledHelp=If enabled, allows managing organizations. Otherwise, existing organizations are still kept but you will not be able to manage them anymore or authenticate their members.
verifiableCredentialsEnabled=Verifiable Credentials
verifiableCredentialsEnabledHelp=If enabled, allows managing verifiable credentials in this realm.
organizations=Organizations
organizationDetails=Organization details
organizationsList=Organizations

View file

@ -227,6 +227,13 @@ function RealmSettingsGeneralTabForm({
labelIcon={t("organizationsEnabledHelp")}
/>
)}
{isOpenid4vciEnabled && (
<DefaultSwitchControl
name="verifiableCredentialsEnabled"
label={t("verifiableCredentialsEnabled")}
labelIcon={t("verifiableCredentialsEnabledHelp")}
/>
)}
<SelectControl
name="unmanagedAttributePolicy"
label={t("unmanagedAttributes")}
@ -266,7 +273,7 @@ function RealmSettingsGeneralTabForm({
title={t("samlIdentityProviderMetadata")}
/>
</StackItem>
{isOpenid4vciEnabled && (
{isOpenid4vciEnabled && realm.verifiableCredentialsEnabled && (
<StackItem>
<FormattedLink
href={`${addTrailingSlash(

View file

@ -83,6 +83,7 @@ export default interface RealmRepresentation {
offlineSessionMaxLifespan?: number;
offlineSessionMaxLifespanEnabled?: boolean;
organizationsEnabled?: boolean;
verifiableCredentialsEnabled?: boolean;
otpPolicyAlgorithm?: string;
otpPolicyDigits?: number;
otpPolicyInitialCounter?: number;

View file

@ -1821,8 +1821,25 @@ public class RealmAdapter implements CachedRealmModel {
updated.setOrganizationsEnabled(organizationsEnabled);
}
@Override
public boolean isVerifiableCredentialsEnabled() {
if (isUpdated()) return featureVerifiableCredentialsEnabled(updated.isVerifiableCredentialsEnabled());
return featureVerifiableCredentialsEnabled(cached.isVerifiableCredentialsEnabled());
}
@Override
public void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled) {
getDelegateForUpdate();
updated.setVerifiableCredentialsEnabled(verifiableCredentialsEnabled);
}
private boolean featureAwareIsOrganizationsEnabled(boolean isOrganizationsEnabled) {
if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return false;
return isOrganizationsEnabled;
}
private boolean featureVerifiableCredentialsEnabled(boolean isVerifiableCredentialsEnabled) {
if (!Profile.isFeatureEnabled(Profile.Feature.OID4VC_VCI)) return false;
return isVerifiableCredentialsEnabled;
}
}

View file

@ -75,6 +75,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
protected boolean identityFederationEnabled;
protected boolean editUsernameAllowed;
protected boolean organizationsEnabled;
protected boolean verifiableCredentialsEnabled;
//--- brute force settings
protected boolean bruteForceProtected;
protected boolean permanentLockout;
@ -191,6 +192,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
resetPasswordAllowed = model.isResetPasswordAllowed();
editUsernameAllowed = model.isEditUsernameAllowed();
organizationsEnabled = model.isOrganizationsEnabled();
verifiableCredentialsEnabled = model.isVerifiableCredentialsEnabled();
//--- brute force settings
bruteForceProtected = model.isBruteForceProtected();
permanentLockout = model.isPermanentLockout();
@ -431,6 +433,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return organizationsEnabled;
}
public boolean isVerifiableCredentialsEnabled() {
return verifiableCredentialsEnabled;
}
public String getDefaultSignatureAlgorithm() {
return defaultSignatureAlgorithm;
}

View file

@ -1199,6 +1199,16 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
setAttribute(RealmAttributes.ORGANIZATIONS_ENABLED, organizationsEnabled);
}
@Override
public boolean isVerifiableCredentialsEnabled() {
return getAttribute(RealmAttributes.VERIFIABLE_CREDENTIALS_ENABLED, Boolean.FALSE);
}
@Override
public void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled) {
setAttribute(RealmAttributes.VERIFIABLE_CREDENTIALS_ENABLED, verifiableCredentialsEnabled);
}
@Override
public ClientModel getMasterAdminClient() {
String masterAdminClientId = realm.getMasterAdminClient();

View file

@ -56,5 +56,7 @@ public interface RealmAttributes {
String FIRST_BROKER_LOGIN_FLOW_ID = "firstBrokerLoginFlowId";
String VERIFIABLE_CREDENTIALS_ENABLED = "verifiableCredentialsEnabled";
String ORGANIZATIONS_ENABLED = "organizationsEnabled";
}

View file

@ -459,6 +459,9 @@ public class DefaultExportImportManager implements ExportImportManager {
if (rep.isInternationalizationEnabled() != null) {
newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled());
}
if (rep.isVerifiableCredentialsEnabled() != null) {
newRealm.setVerifiableCredentialsEnabled(rep.isVerifiableCredentialsEnabled());
}
if (rep.getSupportedLocales() != null) {
newRealm.setSupportedLocales(new HashSet<String>(rep.getSupportedLocales()));
}
@ -771,6 +774,7 @@ public class DefaultExportImportManager implements ExportImportManager {
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
if (rep.isOrganizationsEnabled() != null) realm.setOrganizationsEnabled(rep.isOrganizationsEnabled());
if (rep.isVerifiableCredentialsEnabled() != null) realm.setVerifiableCredentialsEnabled(rep.isVerifiableCredentialsEnabled());
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
if (rep.getAccessCodeLifespanUserAction() != null)

View file

@ -404,6 +404,7 @@ public class ModelToRepresentation {
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
rep.setEditUsernameAllowed(realm.isEditUsernameAllowed());
rep.setOrganizationsEnabled(realm.isOrganizationsEnabled());
rep.setVerifiableCredentialsEnabled(realm.isVerifiableCredentialsEnabled());
rep.setDefaultSignatureAlgorithm(realm.getDefaultSignatureAlgorithm());
rep.setRevokeRefreshToken(realm.isRevokeRefreshToken());
rep.setRefreshTokenMaxReuse(realm.getRefreshTokenMaxReuse());

View file

@ -1160,4 +1160,14 @@ public class RealmModelDelegate implements RealmModel {
public void setOrganizationsEnabled(boolean organizationsEnabled) {
delegate.setOrganizationsEnabled(organizationsEnabled);
}
@Override
public boolean isVerifiableCredentialsEnabled() {
return delegate.isVerifiableCredentialsEnabled();
}
@Override
public void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled) {
delegate.setVerifiableCredentialsEnabled(verifiableCredentialsEnabled);
}
}

View file

@ -1818,5 +1818,14 @@ public class IdentityBrokerStateTestHelpers {
@Override
public void setOrganizationsEnabled(boolean organizationsEnabled) {
}
@Override
public boolean isVerifiableCredentialsEnabled() {
return false;
}
@Override
public void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled) {
}
}
}

View file

@ -110,6 +110,10 @@ public interface RealmModel extends RoleContainerModel {
void setOrganizationsEnabled(boolean organizationsEnabled);
boolean isVerifiableCredentialsEnabled();
void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled);
void setAttribute(String name, String value);
default void setAttribute(String name, Boolean value) {
setAttribute(name, value.toString());

View file

@ -185,7 +185,7 @@ public class AccountConsole implements AccountResourceProvider {
map.put("isViewGroupsEnabled", isViewGroupsEnabled);
map.put("isViewOrganizationsEnabled", realm.isOrganizationsEnabled());
map.put("isOid4VciEnabled", Profile.isFeatureEnabled(Profile.Feature.OID4VC_VCI));
map.put("isOid4VciEnabled", realm.isVerifiableCredentialsEnabled());
map.put("updateEmailFeatureEnabled", Profile.isFeatureEnabled(Profile.Feature.UPDATE_EMAIL));
RequiredActionProviderModel updateEmailActionProvider = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_EMAIL.name());