[KEYCLOAK-10168] Handle microprofile-jwt client scope migration
This commit is contained in:
parent
68d7abac3a
commit
f1acdc000e
12 changed files with 154 additions and 11 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue