[KEYCLOAK-10168] Handle microprofile-jwt client scope migration

This commit is contained in:
Stefan Guilhen 2019-05-03 14:32:21 -03:00 committed by Pedro Igor
parent 68d7abac3a
commit f1acdc000e
12 changed files with 154 additions and 11 deletions

View file

@ -41,6 +41,7 @@ import org.keycloak.migration.migrators.MigrateTo3_4_2;
import org.keycloak.migration.migrators.MigrateTo4_0_0; import org.keycloak.migration.migrators.MigrateTo4_0_0;
import org.keycloak.migration.migrators.MigrateTo4_2_0; import org.keycloak.migration.migrators.MigrateTo4_2_0;
import org.keycloak.migration.migrators.MigrateTo4_6_0; import org.keycloak.migration.migrators.MigrateTo4_6_0;
import org.keycloak.migration.migrators.MigrateTo6_0_0;
import org.keycloak.migration.migrators.Migration; import org.keycloak.migration.migrators.Migration;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -76,7 +77,8 @@ public class MigrationModelManager {
new MigrateTo3_4_2(), new MigrateTo3_4_2(),
new MigrateTo4_0_0(), new MigrateTo4_0_0(),
new MigrateTo4_2_0(), new MigrateTo4_2_0(),
new MigrateTo4_6_0() new MigrateTo4_6_0(),
new MigrateTo6_0_0()
}; };
public static void migrate(KeycloakSession session) { public static void migrate(KeycloakSession session) {

View file

@ -61,4 +61,12 @@ public interface MigrationProvider extends Provider {
*/ */
ClientScopeModel addOIDCWebOriginsClientScope(RealmModel realm); ClientScopeModel addOIDCWebOriginsClientScope(RealmModel realm);
/**
* Adds the {@code microprofile-jwt} optional client scope to the realm and returns the created scope. If the scope
* already exists in the realm then the existing scope is returned.
*
* @param realm the realm to which the scope is to be added.
* @return a reference to the {@code microprofile-jwt} client scope that was either created or already exists in the realm.
*/
ClientScopeModel addOIDCMicroprofileJWTClientScope(RealmModel realm);
} }

View file

@ -0,0 +1,74 @@
/*
* Copyright 2019 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.migration.MigrationProvider;
import org.keycloak.migration.ModelVersion;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
/**
* Implements the migration necessary for version 6.0.0.
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/
public class MigrateTo6_0_0 implements Migration {
public static final ModelVersion VERSION = new ModelVersion("6.0.0");
private static final Logger LOG = Logger.getLogger(MigrateTo6_0_0.class);
@Override
public ModelVersion getVersion() {
return VERSION;
}
@Override
public void migrate(KeycloakSession session) {
session.realms().getRealms().stream().forEach(r -> {
migrateRealm(session, r, false);
});
}
@Override
public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
migrateRealm(session, realm, true);
}
protected void migrateRealm(KeycloakSession session, RealmModel realm, boolean jsn) {
MigrationProvider migrationProvider = session.getProvider(MigrationProvider.class);
// create 'microprofile-jwt' optional client scope in the realm.
ClientScopeModel mpJWTScope = migrationProvider.addOIDCMicroprofileJWTClientScope(realm);
LOG.debugf("Added '%s' optional client scope", mpJWTScope.getName());
// assign 'microprofile-jwt' optional client scope to all the OIDC clients.
for (ClientModel client : realm.getClients()) {
if ((client.getProtocol() == null || "openid-connect".equals(client.getProtocol())) && (!client.isBearerOnly())) {
client.addClientScope(mpJWTScope, false);
}
}
LOG.debugf("Client scope '%s' assigned to all the clients", mpJWTScope.getName());
}
}

View file

@ -267,15 +267,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
addRolesClientScope(newRealm); addRolesClientScope(newRealm);
addWebOriginsClientScope(newRealm); addWebOriginsClientScope(newRealm);
addMicroprofileJWTClientScope(newRealm);
ClientScopeModel microprofileScope = newRealm.addClientScope(MICROPROFILE_JWT_SCOPE);
microprofileScope.setDescription("Microprofile - JWT built-in scope");
microprofileScope.setDisplayOnConsentScreen(false);
microprofileScope.setIncludeInTokenScope(true);
microprofileScope.setProtocol(getId());
microprofileScope.addProtocolMapper(builtins.get(UPN));
microprofileScope.addProtocolMapper(builtins.get(GROUPS));
newRealm.addDefaultClientScope(microprofileScope, false);
} }
@ -322,6 +314,31 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
return originsScope; return originsScope;
} }
/**
* Adds the {@code microprofile-jwt} optional client scope to the specified realm. If a {@code microprofile-jwt} client scope
* already exists in the realm then the existing scope is returned. Otherwise, a new scope is created and returned.
*
* @param newRealm the realm to which the {@code microprofile-jwt} scope is to be added.
* @return a reference to the {@code microprofile-jwt} client scope that was either created or already exists in the realm.
*/
public static ClientScopeModel addMicroprofileJWTClientScope(RealmModel newRealm) {
ClientScopeModel microprofileScope = KeycloakModelUtils.getClientScopeByName(newRealm, MICROPROFILE_JWT_SCOPE);
if (microprofileScope == null) {
microprofileScope = newRealm.addClientScope(MICROPROFILE_JWT_SCOPE);
microprofileScope.setDescription("Microprofile - JWT built-in scope");
microprofileScope.setDisplayOnConsentScreen(false);
microprofileScope.setIncludeInTokenScope(true);
microprofileScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
microprofileScope.addProtocolMapper(builtins.get(UPN));
microprofileScope.addProtocolMapper(builtins.get(GROUPS));
newRealm.addDefaultClientScope(microprofileScope, false);
} else {
logger.debugf("Client scope '%s' already exists in realm '%s'. Skip creating it.", MICROPROFILE_JWT_SCOPE, newRealm.getName());
}
return microprofileScope;
}
@Override @Override
protected void addDefaults(ClientModel client) { protected void addDefaults(ClientModel client) {
} }

View file

@ -96,6 +96,10 @@ public class DefaultMigrationProvider implements MigrationProvider {
return OIDCLoginProtocolFactory.addWebOriginsClientScope(realm); return OIDCLoginProtocolFactory.addWebOriginsClientScope(realm);
} }
@Override
public ClientScopeModel addOIDCMicroprofileJWTClientScope(RealmModel realm) {
return OIDCLoginProtocolFactory.addMicroprofileJWTClientScope(realm);
}
@Override @Override
public void close() { public void close() {

View file

@ -680,6 +680,7 @@ public class ExportImportUtil {
OAuth2Constants.OFFLINE_ACCESS, OAuth2Constants.OFFLINE_ACCESS,
OIDCLoginProtocolFactory.ROLES_SCOPE, OIDCLoginProtocolFactory.ROLES_SCOPE,
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE, OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE,
SamlProtocolFactory.SCOPE_ROLE_LIST SamlProtocolFactory.SCOPE_ROLE_LIST
)); ));
@ -708,7 +709,8 @@ public class ExportImportUtil {
assertThat(optionalClientScopes, Matchers.hasItems( assertThat(optionalClientScopes, Matchers.hasItems(
OAuth2Constants.SCOPE_ADDRESS, OAuth2Constants.SCOPE_ADDRESS,
OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_PHONE,
OAuth2Constants.OFFLINE_ACCESS OAuth2Constants.OFFLINE_ACCESS,
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE
)); ));
} }
} }

View file

@ -232,6 +232,13 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testRolesAndWebOriginsScopesAddedToClient(); testRolesAndWebOriginsScopesAddedToClient();
} }
protected void testMigrationTo6_0_0() {
// check that all expected scopes exist in the migrated realm.
testRealmDefaultClientScopes(migrationRealm);
// check that the 'microprofile-jwt' scope was added to the migrated clients.
testMicroprofileJWTScopeAddedToClient();
}
private void testGroupPolicyTypeFineGrainedAdminPermission() { private void testGroupPolicyTypeFineGrainedAdminPermission() {
ClientsResource clients = migrationRealm.clients(); ClientsResource clients = migrationRealm.clients();
ClientRepresentation clientRepresentation = clients.findByClientId("realm-management").get(0); ClientRepresentation clientRepresentation = clients.findByClientId("realm-management").get(0);
@ -519,6 +526,23 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
} }
/**
* Checks if the {@code microprofile-jwt} optional client scope has been added to the clients.
*/
private void testMicroprofileJWTScopeAddedToClient() {
log.infof("Testing microprofile-jwt optional scope present in realm %s for client migration-test-client", migrationRealm.toRepresentation().getRealm());
List<ClientScopeRepresentation> optionalClientScopes = ApiUtil.findClientByClientId(this.migrationRealm, "migration-test-client").getOptionalClientScopes();
Set<String> defaultClientScopeNames = optionalClientScopes.stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toSet());
if (!defaultClientScopeNames.contains(OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE)) {
Assert.fail("Client scope 'microprofile-jwt' not found as optional scope of client migration-test-client");
}
}
private void testRequiredActionsPriority(RealmResource... realms) { private void testRequiredActionsPriority(RealmResource... realms) {
log.info("testing required action's priority"); log.info("testing required action's priority");
for (RealmResource realm : realms) { for (RealmResource realm : realms) {
@ -572,4 +596,8 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
protected void testMigrationTo5_x() { protected void testMigrationTo5_x() {
// so far nothing // so far nothing
} }
protected void testMigrationTo6_x() {
testMigrationTo6_0_0();
}
} }

View file

@ -74,6 +74,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo3_x(); testMigrationTo3_x();
testMigrationTo4_x(false, false); testMigrationTo4_x(false, false);
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
@Override @Override

View file

@ -67,6 +67,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo3_x(); testMigrationTo3_x();
testMigrationTo4_x(true, false); testMigrationTo4_x(true, false);
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
} }

View file

@ -66,6 +66,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
checkRealmsImported(); checkRealmsImported();
testMigrationTo4_x(true, false); testMigrationTo4_x(true, false);
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
} }

View file

@ -60,6 +60,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
public void migration4_8_3Test() throws Exception { public void migration4_8_3Test() throws Exception {
checkRealmsImported(); checkRealmsImported();
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
} }

View file

@ -70,6 +70,7 @@ public class MigrationTest extends AbstractMigrationTest {
public void migration4_xTest() { public void migration4_xTest() {
testMigratedData(); testMigratedData();
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
@Test @Test
@ -78,6 +79,7 @@ public class MigrationTest extends AbstractMigrationTest {
testMigratedData(); testMigratedData();
testMigrationTo4_x(); testMigrationTo4_x();
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
@Test @Test
@ -87,6 +89,7 @@ public class MigrationTest extends AbstractMigrationTest {
testMigrationTo3_x(); testMigrationTo3_x();
testMigrationTo4_x(); testMigrationTo4_x();
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
@Test @Test
@ -97,6 +100,7 @@ public class MigrationTest extends AbstractMigrationTest {
testMigrationTo3_x(); testMigrationTo3_x();
testMigrationTo4_x(false, false); testMigrationTo4_x(false, false);
testMigrationTo5_x(); testMigrationTo5_x();
testMigrationTo6_x();
} }
} }