KEYCLOAK-10029 Offline token migration fix. Always test offline-token migration when run MigrationTest
This commit is contained in:
parent
db26520046
commit
bc1146ac2f
10 changed files with 178 additions and 34 deletions
|
@ -179,6 +179,7 @@ public class TokenManager {
|
|||
if (oldTokenScope == null && userSession.isOffline()) {
|
||||
logger.debugf("Migrating offline token of user '%s' for client '%s' of realm '%s'", user.getUsername(), client.getClientId(), realm.getName());
|
||||
MigrationUtils.migrateOldOfflineToken(session, realm, client, user);
|
||||
oldTokenScope = OAuth2Constants.OFFLINE_ACCESS;
|
||||
}
|
||||
|
||||
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope, session);
|
||||
|
|
|
@ -44,7 +44,7 @@ and adapter are all in the same JVM and you can debug them easily. If it is not
|
|||
|
||||
Or slightly longer version (that allows you to specify debugging port as well as wait till you attach the debugger):
|
||||
|
||||
-Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006 -Xnoagent -Djava.compiler=NONE"
|
||||
-Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5006 -Xnoagent -Djava.compiler=NONE"
|
||||
|
||||
|
||||
and you will be able to attach remote debugger to the test. Unfortunately server and adapter are running in different JVMs, so this won't help to debug those.
|
||||
|
|
|
@ -43,7 +43,7 @@ public class MigrationContext {
|
|||
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
String offlineToken = StreamUtil.readString(fis, Charset.forName("UTF-8"));
|
||||
|
||||
logger.infof("Successfully read offline token: %s", offlineToken);
|
||||
File f = new File(file);
|
||||
f.delete();
|
||||
logger.infof("Deleted file with offline token: %s", file);
|
||||
|
@ -67,7 +67,7 @@ public class MigrationContext {
|
|||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.realm("Migration");
|
||||
oauth.clientId("migration-test-client");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("b2c07929-69e3-44c6-8d7f-76939000b3e4", "migration-test-user", "admin");
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret", "offline-test-user", "password2");
|
||||
return tokenResponse.getRefreshToken();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -77,7 +77,7 @@ public class MigrationContext {
|
|||
|
||||
private void saveOfflineToken(String offlineToken) throws Exception {
|
||||
String file = getOfflineTokenLocation();
|
||||
logger.infof("Saving offline token to file: %s", file);
|
||||
logger.infof("Saving offline token to file: %s, Offline token is: %s", file, offlineToken);
|
||||
|
||||
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file)))) {
|
||||
writer.print(offlineToken);
|
||||
|
@ -85,10 +85,12 @@ public class MigrationContext {
|
|||
}
|
||||
|
||||
|
||||
// Needs to save offline token inside "basedir". There are issues with saving into directory "target" as it's cleared among restarts and
|
||||
// using "mvn install" instead of "mvn clean install" doesn't work ATM. Improve if needed...
|
||||
private String getOfflineTokenLocation() {
|
||||
return System.getProperty("basedir") + "/offline-token.txt";
|
||||
String tmpDir = System.getProperty("java.io.tmpdir", "");
|
||||
if (tmpDir == null) {
|
||||
tmpDir = System.getProperty("basedir");
|
||||
}
|
||||
return tmpDir + "/offline-token.txt";
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@ package org.keycloak.testsuite.migration;
|
|||
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
|
@ -44,6 +45,7 @@ import org.keycloak.models.utils.TimeBasedOTP;
|
|||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
|
@ -62,9 +64,12 @@ import org.keycloak.storage.UserStorageProvider;
|
|||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.migration.MigrationContext;
|
||||
import org.keycloak.testsuite.exportimport.ExportImportUtil;
|
||||
import org.keycloak.testsuite.runonserver.RunHelpers;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.keycloak.util.TokenUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
@ -126,7 +131,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
assertNames(migrationRealm.clients().findAll(), expectedClientIds.toArray(new String[expectedClientIds.size()]));
|
||||
String id2 = migrationRealm.clients().findByClientId("migration-test-client").get(0).getId();
|
||||
assertNames(migrationRealm.clients().get(id2).roles().list(), "migration-test-client-role");
|
||||
assertNames(migrationRealm.users().search("", 0, 5), "migration-test-user");
|
||||
assertNames(migrationRealm.users().search("", 0, 5), "migration-test-user", "offline-test-user");
|
||||
assertNames(migrationRealm.groups().groups(), "migration-test-group");
|
||||
}
|
||||
|
||||
|
@ -180,10 +185,6 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testDuplicateEmailSupport(masterRealm, migrationRealm);
|
||||
}
|
||||
|
||||
protected void testMigrationTo2_5_1() throws Exception {
|
||||
testOfflineTokenLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.keycloak.migration.migrators.MigrateTo3_0_0
|
||||
*/
|
||||
|
@ -649,9 +650,32 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
|
||||
oauth.realm(MIGRATION);
|
||||
oauth.clientId("migration-test-client");
|
||||
OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(oldOfflineToken, "b2c07929-69e3-44c6-8d7f-76939000b3e4");
|
||||
OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(oldOfflineToken, "secret");
|
||||
|
||||
if (response.getError() != null) {
|
||||
String errorMessage = String.format("Error when refreshing offline token. Error: %s, Error details: %s, offline token from previous version: %s",
|
||||
response.getError(), response.getErrorDescription(), oldOfflineToken);
|
||||
log.error(errorMessage);
|
||||
Assert.fail(errorMessage);
|
||||
}
|
||||
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
assertEquals("migration-test-user", accessToken.getPreferredUsername());
|
||||
assertEquals("offline-test-user", accessToken.getPreferredUsername());
|
||||
|
||||
// KEYCLOAK-10029 - Doublecheck that refresh token in the response is also offline token. Doublecheck that it can be used to another successful refresh
|
||||
String newOfflineToken1 = response.getRefreshToken();
|
||||
assertOfflineToken(newOfflineToken1);
|
||||
|
||||
response = oauth.doRefreshTokenRequest(newOfflineToken1, "secret");
|
||||
String newOfflineToken2 = response.getRefreshToken();
|
||||
assertOfflineToken(newOfflineToken2);
|
||||
}
|
||||
|
||||
private void assertOfflineToken(String offlineToken) {
|
||||
RefreshToken offlineTokenParsed = oauth.parseRefreshToken(offlineToken);
|
||||
assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineTokenParsed.getType());
|
||||
assertEquals(0, offlineTokenParsed.getExpiration());
|
||||
assertTrue(TokenUtil.hasScope(offlineTokenParsed.getScope(), OAuth2Constants.OFFLINE_ACCESS));
|
||||
}
|
||||
|
||||
private void testRealmDefaultClientScopes(RealmResource realm) {
|
||||
|
@ -748,19 +772,19 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
String otp = otpGenerator.generateTOTP("dSdmuHLQhkm54oIm0A0S");
|
||||
|
||||
// Try invalid password first
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("b2c07929-69e3-44c6-8d7f-76939000b3e4",
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret",
|
||||
"migration-test-user", "password", otp);
|
||||
Assert.assertNull(response.getAccessToken());
|
||||
Assert.assertNotNull(response.getError());
|
||||
|
||||
// Try invalid OTP then
|
||||
response = oauth.doGrantAccessTokenRequest("b2c07929-69e3-44c6-8d7f-76939000b3e4",
|
||||
response = oauth.doGrantAccessTokenRequest("secret",
|
||||
"migration-test-user", "password2", "invalid");
|
||||
Assert.assertNull(response.getAccessToken());
|
||||
Assert.assertNotNull(response.getError());
|
||||
|
||||
// Try successful login now
|
||||
response = oauth.doGrantAccessTokenRequest("b2c07929-69e3-44c6-8d7f-76939000b3e4",
|
||||
response = oauth.doGrantAccessTokenRequest("secret",
|
||||
"migration-test-user", "password2", otp);
|
||||
Assert.assertNull(response.getError());
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
|
@ -770,7 +794,6 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected void testOTPAuthenticatorsMigratedToConditionalFlow() {
|
||||
log.info("testing optional authentication executions migrated");
|
||||
|
||||
|
@ -831,7 +854,6 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testMigrationTo2_2_0();
|
||||
testMigrationTo2_3_0();
|
||||
testMigrationTo2_5_0();
|
||||
testMigrationTo2_5_1();
|
||||
}
|
||||
|
||||
protected void testMigrationTo3_x() {
|
||||
|
|
|
@ -63,7 +63,6 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo2_2_0();
|
||||
testMigrationTo2_3_0();
|
||||
testMigrationTo2_5_0();
|
||||
//testMigrationTo2_5_1(); // Offline tokens migration is skipped for JSON
|
||||
testMigrationTo3_x();
|
||||
testMigrationTo4_x(false, false);
|
||||
testMigrationTo5_x();
|
||||
|
|
|
@ -59,18 +59,21 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
|
||||
@Test
|
||||
@Migration(versionFrom = "4.")
|
||||
public void migration4_xTest() {
|
||||
public void migration4_xTest() throws Exception {
|
||||
testMigratedData();
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Migration(versionFrom = "3.")
|
||||
public void migration3_xTest() {
|
||||
public void migration3_xTest() throws Exception {
|
||||
testMigratedData();
|
||||
testMigrationTo4_x();
|
||||
testMigrationTo5_x();
|
||||
|
@ -78,11 +81,14 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo7_x(true);
|
||||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Migration(versionFrom = "2.")
|
||||
public void migration2_xTest() {
|
||||
public void migration2_xTest() throws Exception {
|
||||
testMigratedData();
|
||||
testMigrationTo3_x();
|
||||
testMigrationTo4_x();
|
||||
|
@ -91,6 +97,9 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo7_x(true);
|
||||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -105,6 +114,9 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo7_x(false);
|
||||
testMigrationTo8_x();
|
||||
testMigrationTo9_x();
|
||||
|
||||
// Always test offline-token login during migration test
|
||||
testOfflineTokenLogin();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1769,7 +1769,32 @@
|
|||
"account" : [ "manage-account", "view-profile" ]
|
||||
},
|
||||
"groups" : [ "/migration-test-group" ]
|
||||
} ],
|
||||
},
|
||||
{
|
||||
"id" : "9aa0d4f7-399e-4520-92df-77403d5d2a33",
|
||||
"createdTimestamp" : 1476260593350,
|
||||
"username" : "offline-test-user",
|
||||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"credentials" : [ {
|
||||
"type" : "password",
|
||||
"hashedSaltedValue" : "D3F6cEj0pNv1UvkPq2XhnH5TTg2BaR2qKQd+vMoT8Pj+cHEGvISbBujjD9+889LIhWUSbQS8nkZH0yEnrTKBAA==",
|
||||
"salt" : "C2vKhAsajS53Xu816IcKIw==",
|
||||
"hashIterations" : 20000,
|
||||
"counter" : 0,
|
||||
"algorithm" : "pbkdf2",
|
||||
"digits" : 0,
|
||||
"period" : 0,
|
||||
"createdDate" : 1582099686822
|
||||
} ],
|
||||
"requiredActions" : [ ],
|
||||
"realmRoles" : [ "offline_access" ],
|
||||
"clientRoles" : {
|
||||
"account" : [ "manage-account", "view-profile" ]
|
||||
},
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
"clientScopeMappings" : {
|
||||
"realm-management" : [ {
|
||||
"client" : "admin-cli",
|
||||
|
@ -2100,7 +2125,7 @@
|
|||
"surrogateAuthRequired" : false,
|
||||
"enabled" : true,
|
||||
"clientAuthenticatorType" : "client-secret",
|
||||
"secret" : "b2c07929-69e3-44c6-8d7f-76939000b3e4",
|
||||
"secret" : "secret",
|
||||
"redirectUris" : [ ],
|
||||
"webOrigins" : [ ],
|
||||
"notBefore" : 0,
|
||||
|
|
|
@ -2121,7 +2121,34 @@
|
|||
"account" : [ "view-profile", "manage-account" ]
|
||||
},
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
},
|
||||
{
|
||||
"id" : "556eb430-d574-4956-908a-83527a77932a",
|
||||
"createdTimestamp" : 1489756947105,
|
||||
"username" : "offline-test-user",
|
||||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"credentials" : [ {
|
||||
"type" : "password",
|
||||
"hashedSaltedValue" : "D3F6cEj0pNv1UvkPq2XhnH5TTg2BaR2qKQd+vMoT8Pj+cHEGvISbBujjD9+889LIhWUSbQS8nkZH0yEnrTKBAA==",
|
||||
"salt" : "C2vKhAsajS53Xu816IcKIw==",
|
||||
"hashIterations" : 20000,
|
||||
"counter" : 0,
|
||||
"algorithm" : "pbkdf2",
|
||||
"digits" : 0,
|
||||
"period" : 0,
|
||||
"createdDate" : 1582099686822,
|
||||
"config" : { }
|
||||
} ],
|
||||
"disableableCredentialTypes" : [ "password" ],
|
||||
"requiredActions" : [ ],
|
||||
"realmRoles" : [ "uma_authorization", "offline_access" ],
|
||||
"clientRoles" : {
|
||||
"account" : [ "view-profile", "manage-account" ]
|
||||
},
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
"clientScopeMappings" : {
|
||||
"realm-management" : [ {
|
||||
"client" : "admin-cli",
|
||||
|
@ -2471,7 +2498,7 @@
|
|||
"surrogateAuthRequired" : false,
|
||||
"enabled" : true,
|
||||
"clientAuthenticatorType" : "client-secret",
|
||||
"secret" : "75da9358-22e0-4ab5-9609-5c74c40dd70f",
|
||||
"secret" : "secret",
|
||||
"redirectUris" : [ ],
|
||||
"webOrigins" : [ ],
|
||||
"notBefore" : 0,
|
||||
|
@ -2481,7 +2508,7 @@
|
|||
"implicitFlowEnabled" : false,
|
||||
"directAccessGrantsEnabled" : true,
|
||||
"serviceAccountsEnabled" : false,
|
||||
"publicClient" : true,
|
||||
"publicClient" : false,
|
||||
"frontchannelLogout" : false,
|
||||
"protocol" : "openid-connect",
|
||||
"attributes" : { },
|
||||
|
|
|
@ -333,7 +333,35 @@
|
|||
},
|
||||
"notBefore" : 0,
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
},
|
||||
{
|
||||
"id" : "3a15a4f3-0e14-4b57-8753-2d774ef02fce",
|
||||
"createdTimestamp" : 1531933208712,
|
||||
"username" : "offline-test-user",
|
||||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"credentials" : [ {
|
||||
"type" : "password",
|
||||
"hashedSaltedValue" : "kNwotFPNeuwelpT1HWt+E4ONXFK6wjd+h0zbzNBRGwOqacAjeY7vYN9QZQ46DlEKSdn04cEU/3RvX8WPcRegxg==",
|
||||
"salt" : "rEIJDbs+BQqpx31v8mONWA==",
|
||||
"hashIterations" : 27500,
|
||||
"counter" : 0,
|
||||
"algorithm" : "pbkdf2-sha256",
|
||||
"digits" : 0,
|
||||
"period" : 0,
|
||||
"createdDate" : 1570002786025,
|
||||
"config" : { }
|
||||
} ],
|
||||
"disableableCredentialTypes" : [ ],
|
||||
"requiredActions" : [ ],
|
||||
"realmRoles" : [ "offline_access", "uma_authorization" ],
|
||||
"clientRoles" : {
|
||||
"account" : [ "manage-account", "view-profile" ]
|
||||
},
|
||||
"notBefore" : 0,
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
"clientScopeMappings" : {
|
||||
"migration-test-client": [
|
||||
{
|
||||
|
@ -679,7 +707,7 @@
|
|||
"surrogateAuthRequired" : false,
|
||||
"enabled" : true,
|
||||
"clientAuthenticatorType" : "client-secret",
|
||||
"secret" : "d926c1c0-056a-4418-86d5-f103112dec43",
|
||||
"secret" : "secret",
|
||||
"redirectUris" : [ ],
|
||||
"webOrigins" : [ ],
|
||||
"notBefore" : 0,
|
||||
|
@ -689,7 +717,7 @@
|
|||
"implicitFlowEnabled" : false,
|
||||
"directAccessGrantsEnabled" : true,
|
||||
"serviceAccountsEnabled" : false,
|
||||
"publicClient" : true,
|
||||
"publicClient" : false,
|
||||
"frontchannelLogout" : false,
|
||||
"protocol" : "openid-connect",
|
||||
"attributes" : { },
|
||||
|
|
|
@ -338,7 +338,35 @@
|
|||
},
|
||||
"notBefore" : 0,
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
},
|
||||
{
|
||||
"id" : "189110e3-0b38-4ae3-b019-dce1f1b34512",
|
||||
"createdTimestamp" : 1550760939539,
|
||||
"username" : "offline-test-user",
|
||||
"enabled" : true,
|
||||
"totp" : false,
|
||||
"emailVerified" : false,
|
||||
"credentials" : [ {
|
||||
"type" : "password",
|
||||
"hashedSaltedValue" : "kNwotFPNeuwelpT1HWt+E4ONXFK6wjd+h0zbzNBRGwOqacAjeY7vYN9QZQ46DlEKSdn04cEU/3RvX8WPcRegxg==",
|
||||
"salt" : "rEIJDbs+BQqpx31v8mONWA==",
|
||||
"hashIterations" : 27500,
|
||||
"counter" : 0,
|
||||
"algorithm" : "pbkdf2-sha256",
|
||||
"digits" : 0,
|
||||
"period" : 0,
|
||||
"createdDate" : 1570002786025,
|
||||
"config" : { }
|
||||
} ],
|
||||
"disableableCredentialTypes" : [ ],
|
||||
"requiredActions" : [ ],
|
||||
"realmRoles" : [ "uma_authorization", "offline_access" ],
|
||||
"clientRoles" : {
|
||||
"account" : [ "view-profile", "manage-account" ]
|
||||
},
|
||||
"notBefore" : 0,
|
||||
"groups" : [ ]
|
||||
} ],
|
||||
"scopeMappings" : [ {
|
||||
"clientScope" : "offline_access",
|
||||
"roles" : [ "offline_access" ]
|
||||
|
@ -480,7 +508,7 @@
|
|||
"surrogateAuthRequired" : false,
|
||||
"enabled" : true,
|
||||
"clientAuthenticatorType" : "client-secret",
|
||||
"secret" : "ce99063e-6d4e-4342-b4ec-62a54a46e9dd",
|
||||
"secret" : "secret",
|
||||
"redirectUris" : [ ],
|
||||
"webOrigins" : [ ],
|
||||
"notBefore" : 0,
|
||||
|
@ -490,7 +518,7 @@
|
|||
"implicitFlowEnabled" : false,
|
||||
"directAccessGrantsEnabled" : true,
|
||||
"serviceAccountsEnabled" : false,
|
||||
"publicClient" : true,
|
||||
"publicClient" : false,
|
||||
"frontchannelLogout" : false,
|
||||
"protocol" : "openid-connect",
|
||||
"attributes" : { },
|
||||
|
|
Loading…
Reference in a new issue