parent
8ee7ae24de
commit
9e12587181
19 changed files with 302 additions and 40 deletions
|
@ -24,6 +24,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.common.Version;
|
||||
import org.keycloak.migration.migrators.MigrateTo12_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo14_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo18_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_2_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_3_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_4_0;
|
||||
|
@ -96,7 +97,8 @@ public class MigrationModelManager {
|
|||
new MigrateTo9_0_0(),
|
||||
new MigrateTo9_0_4(),
|
||||
new MigrateTo12_0_0(),
|
||||
new MigrateTo14_0_0()
|
||||
new MigrateTo14_0_0(),
|
||||
new MigrateTo18_0_0()
|
||||
};
|
||||
|
||||
public static void migrate(KeycloakSession session) {
|
||||
|
|
|
@ -69,4 +69,12 @@ public interface MigrationProvider extends Provider {
|
|||
* @return a reference to the {@code microprofile-jwt} client scope that was either created or already exists in the realm.
|
||||
*/
|
||||
ClientScopeModel addOIDCMicroprofileJWTClientScope(RealmModel realm);
|
||||
|
||||
/**
|
||||
* Add 'acr' client scope or return it if already exists
|
||||
*
|
||||
* @param realm
|
||||
* @return created or already existing client scope 'acr'
|
||||
*/
|
||||
void addOIDCAcrClientScope(RealmModel realm);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2022 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.migration.migrators;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.migration.MigrationProvider;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MigrateTo18_0_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("18.0.0");
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MigrateTo18_0_0.class);
|
||||
|
||||
@Override
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(KeycloakSession session) {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
session.realms().getRealmsStream().forEach(realm -> migrateRealm(session, realm));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
|
||||
migrateRealm(session, realm);
|
||||
}
|
||||
|
||||
protected void migrateRealm(KeycloakSession session, RealmModel realm) {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
MigrationProvider migrationProvider = session.getProvider(MigrationProvider.class);
|
||||
|
||||
// create 'acr' default client scope in the realm.
|
||||
migrationProvider.addOIDCAcrClientScope(realm);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.keycloak.protocol.oidc;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
|
@ -32,6 +33,7 @@ import org.keycloak.models.utils.DefaultClientScopes;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.AbstractLoginProtocolFactory;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.AcrProtocolMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.AddressMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.AllowedWebOriginsProtocolMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.AudienceResolveProtocolMapper;
|
||||
|
@ -83,6 +85,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
public static final String CLIENT_ROLES = "client roles";
|
||||
public static final String AUDIENCE_RESOLVE = "audience resolve";
|
||||
public static final String ALLOWED_WEB_ORIGINS = "allowed web origins";
|
||||
public static final String ACR = "acr loa level";
|
||||
// microprofile-jwt claims
|
||||
public static final String UPN = "upn";
|
||||
public static final String GROUPS = "groups";
|
||||
|
@ -90,6 +93,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
public static final String ROLES_SCOPE = "roles";
|
||||
public static final String WEB_ORIGINS_SCOPE = "web-origins";
|
||||
public static final String MICROPROFILE_JWT_SCOPE = "microprofile-jwt";
|
||||
public static final String ACR_SCOPE = "acr";
|
||||
|
||||
public static final String PROFILE_SCOPE_CONSENT_TEXT = "${profileScopeConsentText}";
|
||||
public static final String EMAIL_SCOPE_CONSENT_TEXT = "${emailScopeConsentText}";
|
||||
|
@ -191,6 +195,11 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
|
||||
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true);
|
||||
builtins.put(GROUPS, model);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
model = AcrProtocolMapper.create(ACR, true, true);
|
||||
builtins.put(ACR, model);
|
||||
}
|
||||
}
|
||||
|
||||
private static void createUserAttributeMapper(String name, String attrName, String claimName, String type) {
|
||||
|
@ -268,6 +277,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
addRolesClientScope(newRealm);
|
||||
addWebOriginsClientScope(newRealm);
|
||||
addMicroprofileJWTClientScope(newRealm);
|
||||
addAcrClientScope(newRealm);
|
||||
}
|
||||
|
||||
|
||||
|
@ -339,6 +349,30 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
return microprofileScope;
|
||||
}
|
||||
|
||||
|
||||
public static void addAcrClientScope(RealmModel newRealm) {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
ClientScopeModel acrScope = KeycloakModelUtils.getClientScopeByName(newRealm, ACR_SCOPE);
|
||||
if (acrScope == null) {
|
||||
acrScope = newRealm.addClientScope(ACR_SCOPE);
|
||||
acrScope.setDescription("OpenID Connect scope for add acr (authentication context class reference) to the token");
|
||||
acrScope.setDisplayOnConsentScreen(false);
|
||||
acrScope.setIncludeInTokenScope(false);
|
||||
acrScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
acrScope.addProtocolMapper(builtins.get(ACR));
|
||||
|
||||
// acr will be realm 'default' client scope
|
||||
newRealm.addDefaultClientScope(acrScope, true);
|
||||
|
||||
logger.debugf("Client scope '%s' created in the realm '%s'.", ACR_SCOPE, newRealm.getName());
|
||||
} else {
|
||||
logger.debugf("Client scope '%s' already exists in realm '%s'. Skip creating it.", ACR_SCOPE, newRealm.getName());
|
||||
}
|
||||
} else {
|
||||
logger.debugf("Skip creating client scope '%s' in the realm '%s' due the step-up authentication feature is disabled.", ACR_SCOPE, newRealm.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDefaults(ClientModel client) {
|
||||
}
|
||||
|
|
|
@ -884,10 +884,9 @@ public class TokenManager {
|
|||
token.setNonce(clientSessionCtx.getAttribute(OIDCLoginProtocol.NONCE_PARAM, String.class));
|
||||
token.setScope(clientSessionCtx.getScopeString());
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
token.setAcr(getAcr(clientSession));
|
||||
} else {
|
||||
// Backwards compatibility behaviour prior step-up authentication was introduced
|
||||
// Protocol mapper is supposed to set this in case "step_up_authentication" feature enabled
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
String acr = AuthenticationManager.isSSOAuthentication(clientSession) ? "0" : "1";
|
||||
token.setAcr(acr);
|
||||
}
|
||||
|
@ -907,31 +906,6 @@ public class TokenManager {
|
|||
return token;
|
||||
}
|
||||
|
||||
private String getAcr(AuthenticatedClientSessionModel clientSession) {
|
||||
int loa = LoAUtil.getCurrentLevelOfAuthentication(clientSession);
|
||||
if (loa < Constants.MINIMUM_LOA) {
|
||||
loa = AuthenticationManager.isSSOAuthentication(clientSession) ? 0 : 1;
|
||||
}
|
||||
|
||||
Map<String, Integer> acrLoaMap = AcrUtils.getAcrLoaMap(clientSession.getClient());
|
||||
String acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, AcrUtils.getRequiredAcrValues(
|
||||
clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM)));
|
||||
if (acr == null) {
|
||||
acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, AcrUtils.getAcrValues(
|
||||
clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM),
|
||||
clientSession.getNote(OIDCLoginProtocol.ACR_PARAM), clientSession.getClient()));
|
||||
if (acr == null) {
|
||||
acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, acrLoaMap.keySet());
|
||||
if (acr == null) {
|
||||
acr = String.valueOf(loa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.tracef("Level sent in the token to client %s: %s. Original loa from the authentication: %d", clientSession.getClient().getClientId(), acr, loa);
|
||||
return acr;
|
||||
}
|
||||
|
||||
private int getTokenExpiration(RealmModel realm, ClientModel client, UserSessionModel userSession,
|
||||
AuthenticatedClientSessionModel clientSession, boolean offlineTokenRequested) {
|
||||
boolean implicitFlow = false;
|
||||
|
@ -1191,7 +1165,12 @@ public class TokenManager {
|
|||
idToken.setAuthTime(accessToken.getAuthTime());
|
||||
idToken.setSessionState(accessToken.getSessionState());
|
||||
idToken.expiration(accessToken.getExpiration());
|
||||
|
||||
// Protocol mapper is supposed to set this in case "step_up_authentication" feature enabled
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||
idToken.setAcr(accessToken.getAcr());
|
||||
}
|
||||
|
||||
if (isIdTokenAsDetachedSignature == false) {
|
||||
transformIDToken(session, idToken, userSession, clientSessionCtx);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2022 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.protocol.oidc.mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.authenticators.util.LoAUtil;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientSessionContext;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.utils.AcrUtils;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, EnvironmentDependentProviderFactory {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class);
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
|
||||
static {
|
||||
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AcrProtocolMapper.class);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-acr-mapper";
|
||||
|
||||
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Authentication Context Class Reference (ACR)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return TOKEN_MAPPER_CATEGORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Maps the achieved LoA (Level of Authentication) to the 'acr' claim of the token";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession,
|
||||
ClientSessionContext clientSessionCtx) {
|
||||
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
|
||||
String acr = getAcr(clientSession);
|
||||
token.setAcr(acr);
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken) {
|
||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||
mapper.setName(name);
|
||||
mapper.setProtocolMapper(PROVIDER_ID);
|
||||
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
Map<String, String> config = new HashMap<>();
|
||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||
mapper.setConfig(config);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
protected String getAcr(AuthenticatedClientSessionModel clientSession) {
|
||||
int loa = LoAUtil.getCurrentLevelOfAuthentication(clientSession);
|
||||
logger.tracef("Loa level when authenticated to client %s: %d", clientSession.getClient().getClientId(), loa);
|
||||
if (loa < Constants.MINIMUM_LOA) {
|
||||
loa = AuthenticationManager.isSSOAuthentication(clientSession) ? 0 : 1;
|
||||
}
|
||||
|
||||
Map<String, Integer> acrLoaMap = AcrUtils.getAcrLoaMap(clientSession.getClient());
|
||||
String acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, AcrUtils.getRequiredAcrValues(
|
||||
clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM)));
|
||||
if (acr == null) {
|
||||
acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, AcrUtils.getAcrValues(
|
||||
clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM),
|
||||
clientSession.getNote(OIDCLoginProtocol.ACR_PARAM), clientSession.getClient()));
|
||||
if (acr == null) {
|
||||
acr = AcrUtils.mapLoaToAcr(loa, acrLoaMap, acrLoaMap.keySet());
|
||||
if (acr == null) {
|
||||
acr = String.valueOf(loa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.tracef("Level sent in the token to client %s: %s. Original loa from the authentication: %d", clientSession.getClient().getClientId(), acr, loa);
|
||||
return acr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION);
|
||||
}
|
||||
}
|
|
@ -89,6 +89,9 @@ public class OIDCAttributeMapperHelper {
|
|||
tmpToken.put("azp", (claim, mapperName, token, value) -> {
|
||||
token.issuedFor(value.toString());
|
||||
});
|
||||
tmpToken.put(IDToken.ACR, (claim, mapperName, token, value) -> {
|
||||
token.setAcr(value.toString());
|
||||
});
|
||||
tmpToken.put("aud", (claim, mapperName, token, value) -> {
|
||||
if (value instanceof Collection) {
|
||||
String[] audiences = ((Collection<?>) value).stream().map(Object::toString).toArray(String[]::new);
|
||||
|
@ -108,7 +111,6 @@ public class OIDCAttributeMapperHelper {
|
|||
tmpToken.put("iss", notAllowedInToken);
|
||||
tmpToken.put("scope", notAllowedInToken);
|
||||
tmpToken.put(IDToken.NONCE, notAllowedInToken);
|
||||
tmpToken.put(IDToken.ACR, notAllowedInToken);
|
||||
tmpToken.put(IDToken.AUTH_TIME, notAllowedInToken);
|
||||
tmpToken.put(IDToken.SESSION_STATE, notAllowedInToken);
|
||||
tokenPropertySetters = Collections.unmodifiableMap(tmpToken);
|
||||
|
|
|
@ -101,6 +101,11 @@ public class DefaultMigrationProvider implements MigrationProvider {
|
|||
return OIDCLoginProtocolFactory.addMicroprofileJWTClientScope(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOIDCAcrClientScope(RealmModel realm) {
|
||||
OIDCLoginProtocolFactory.addAcrClientScope(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ org.keycloak.protocol.oidc.mappers.GroupMembershipMapper
|
|||
org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper
|
||||
org.keycloak.protocol.oidc.mappers.AudienceResolveProtocolMapper
|
||||
org.keycloak.protocol.oidc.mappers.AllowedWebOriginsProtocolMapper
|
||||
org.keycloak.protocol.oidc.mappers.AcrProtocolMapper
|
||||
org.keycloak.protocol.saml.mappers.RoleListMapper
|
||||
org.keycloak.protocol.saml.mappers.RoleNameMapper
|
||||
org.keycloak.protocol.saml.mappers.HardcodedRole
|
||||
|
|
|
@ -720,6 +720,7 @@ public class ExportImportUtil {
|
|||
OIDCLoginProtocolFactory.ROLES_SCOPE,
|
||||
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
|
||||
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE,
|
||||
OIDCLoginProtocolFactory.ACR_SCOPE,
|
||||
SamlProtocolFactory.SCOPE_ROLE_LIST
|
||||
));
|
||||
|
||||
|
@ -740,7 +741,8 @@ public class ExportImportUtil {
|
|||
OAuth2Constants.SCOPE_PROFILE,
|
||||
OAuth2Constants.SCOPE_EMAIL,
|
||||
OIDCLoginProtocolFactory.ROLES_SCOPE,
|
||||
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE
|
||||
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
|
||||
OIDCLoginProtocolFactory.ACR_SCOPE
|
||||
));
|
||||
|
||||
Set<String> optionalClientScopes = realm.getDefaultOptionalClientScopes()
|
||||
|
|
|
@ -31,8 +31,8 @@ import org.junit.Assert;
|
|||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.authentication.authenticators.conditional.ConditionalLoaAuthenticator;
|
||||
|
@ -44,9 +44,9 @@ import org.keycloak.models.Constants;
|
|||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||
import org.keycloak.representations.ClaimsRepresentation;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
@ -54,25 +54,22 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
|
||||
import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
|
||||
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginTotpPage;
|
||||
import org.keycloak.testsuite.pages.PushTheButtonPage;
|
||||
import org.keycloak.testsuite.util.FlowUtil;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.RealmRepUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||
|
||||
/**
|
||||
|
@ -643,6 +640,23 @@ public class LevelOfAssuranceFlowTest extends AbstractTestRealmKeycloakTest {
|
|||
testingClient.server(TEST_REALM_NAME).run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow("browser - Level of Authentication FLow"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFeature(value = Profile.Feature.STEP_UP_AUTHENTICATION, skipRestart = true)
|
||||
public void testDisableStepupFeatureInNewRealm() {
|
||||
RealmRepresentation rep = new RealmRepresentation();
|
||||
rep.setRealm("new-realm");
|
||||
adminClient.realms().create(rep);
|
||||
RealmResource newRealm = adminClient.realms().realm("new-realm");
|
||||
try {
|
||||
// Test client scope was not created in the new realm when feature is disabled
|
||||
boolean acrScopeExists = newRealm.clientScopes().findAll().stream()
|
||||
.anyMatch(clientScope -> OIDCLoginProtocolFactory.ACR_SCOPE.equals(clientScope.getName()));
|
||||
Assert.assertThat(false, is(acrScopeExists));
|
||||
} finally {
|
||||
newRealm.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void openLoginFormWithAcrClaim(boolean essential, String... acrValues) {
|
||||
openLoginFormWithAcrClaim(oauth, essential, acrValues);
|
||||
|
|
|
@ -310,6 +310,11 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testSamlAttributes(migrationRealm);
|
||||
}
|
||||
|
||||
protected void testMigrationTo18_0_0() {
|
||||
// check that all expected scopes exist in the migrated realm.
|
||||
testRealmDefaultClientScopes(migrationRealm);
|
||||
}
|
||||
|
||||
protected void testDeleteAccount(RealmResource realm) {
|
||||
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
||||
|
@ -931,6 +936,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testMigrationTo14_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo18_x() {
|
||||
testMigrationTo18_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
||||
if (supportedAuthzServices) {
|
||||
testDecisionStrategySetOnResourceServer();
|
||||
|
|
|
@ -69,6 +69,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -71,6 +71,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat
|
|||
public void migration9_0_3Test() throws Exception {
|
||||
checkRealmsImported();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
public void migration9_xTest() throws Exception {
|
||||
testMigratedData(false);
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -80,6 +81,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -97,6 +99,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(true);
|
||||
testMigrationTo18_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -122,6 +125,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
@ -140,6 +144,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
testMigrationTo12_x(false);
|
||||
testMigrationTo18_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
|
|
|
@ -384,7 +384,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
private void assertScopesSupportedMatchesWithRealm(OIDCConfigurationRepresentation oidcConfig) {
|
||||
Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS,
|
||||
OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS,
|
||||
OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS, OIDCLoginProtocolFactory.ACR_SCOPE,
|
||||
OIDCLoginProtocolFactory.ROLES_SCOPE, OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE, OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue