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

View file

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

View file

@ -3165,6 +3165,8 @@ logo=Logo
avatarImage=Avatar image avatarImage=Avatar image
organizationsEnabled=Organizations 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. 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 organizations=Organizations
organizationDetails=Organization details organizationDetails=Organization details
organizationsList=Organizations organizationsList=Organizations

View file

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

View file

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

View file

@ -1821,8 +1821,25 @@ public class RealmAdapter implements CachedRealmModel {
updated.setOrganizationsEnabled(organizationsEnabled); 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) { private boolean featureAwareIsOrganizationsEnabled(boolean isOrganizationsEnabled) {
if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return false; if (!Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) return false;
return isOrganizationsEnabled; 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 identityFederationEnabled;
protected boolean editUsernameAllowed; protected boolean editUsernameAllowed;
protected boolean organizationsEnabled; protected boolean organizationsEnabled;
protected boolean verifiableCredentialsEnabled;
//--- brute force settings //--- brute force settings
protected boolean bruteForceProtected; protected boolean bruteForceProtected;
protected boolean permanentLockout; protected boolean permanentLockout;
@ -191,6 +192,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
resetPasswordAllowed = model.isResetPasswordAllowed(); resetPasswordAllowed = model.isResetPasswordAllowed();
editUsernameAllowed = model.isEditUsernameAllowed(); editUsernameAllowed = model.isEditUsernameAllowed();
organizationsEnabled = model.isOrganizationsEnabled(); organizationsEnabled = model.isOrganizationsEnabled();
verifiableCredentialsEnabled = model.isVerifiableCredentialsEnabled();
//--- brute force settings //--- brute force settings
bruteForceProtected = model.isBruteForceProtected(); bruteForceProtected = model.isBruteForceProtected();
permanentLockout = model.isPermanentLockout(); permanentLockout = model.isPermanentLockout();
@ -431,6 +433,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return organizationsEnabled; return organizationsEnabled;
} }
public boolean isVerifiableCredentialsEnabled() {
return verifiableCredentialsEnabled;
}
public String getDefaultSignatureAlgorithm() { public String getDefaultSignatureAlgorithm() {
return defaultSignatureAlgorithm; return defaultSignatureAlgorithm;
} }

View file

@ -1199,6 +1199,16 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
setAttribute(RealmAttributes.ORGANIZATIONS_ENABLED, organizationsEnabled); 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 @Override
public ClientModel getMasterAdminClient() { public ClientModel getMasterAdminClient() {
String masterAdminClientId = realm.getMasterAdminClient(); String masterAdminClientId = realm.getMasterAdminClient();

View file

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

View file

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

View file

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

View file

@ -1160,4 +1160,14 @@ public class RealmModelDelegate implements RealmModel {
public void setOrganizationsEnabled(boolean organizationsEnabled) { public void setOrganizationsEnabled(boolean organizationsEnabled) {
delegate.setOrganizationsEnabled(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 @Override
public void setOrganizationsEnabled(boolean organizationsEnabled) { 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); void setOrganizationsEnabled(boolean organizationsEnabled);
boolean isVerifiableCredentialsEnabled();
void setVerifiableCredentialsEnabled(boolean verifiableCredentialsEnabled);
void setAttribute(String name, String value); void setAttribute(String name, String value);
default void setAttribute(String name, Boolean value) { default void setAttribute(String name, Boolean value) {
setAttribute(name, value.toString()); setAttribute(name, value.toString());

View file

@ -185,7 +185,7 @@ public class AccountConsole implements AccountResourceProvider {
map.put("isViewGroupsEnabled", isViewGroupsEnabled); map.put("isViewGroupsEnabled", isViewGroupsEnabled);
map.put("isViewOrganizationsEnabled", realm.isOrganizationsEnabled()); 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)); map.put("updateEmailFeatureEnabled", Profile.isFeatureEnabled(Profile.Feature.UPDATE_EMAIL));
RequiredActionProviderModel updateEmailActionProvider = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_EMAIL.name()); RequiredActionProviderModel updateEmailActionProvider = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_EMAIL.name());