KEYCLOAK-14023 Instagram User Endpoint change
Co-authored-by: Jean-Baptiste PIN <jibet.pin@gmail.com>
This commit is contained in:
parent
1db1deb066
commit
7087c081f0
11 changed files with 331 additions and 21 deletions
|
@ -34,6 +34,7 @@ import java.util.Map;
|
||||||
public class BrokeredIdentityContext {
|
public class BrokeredIdentityContext {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
|
private String legacyId;
|
||||||
private String username;
|
private String username;
|
||||||
private String modelUsername;
|
private String modelUsername;
|
||||||
private String email;
|
private String email;
|
||||||
|
@ -64,6 +65,19 @@ public class BrokeredIdentityContext {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID from older API version. For API migrations.
|
||||||
|
*
|
||||||
|
* @return legacy ID
|
||||||
|
*/
|
||||||
|
public String getLegacyId() {
|
||||||
|
return legacyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLegacyId(String legacyId) {
|
||||||
|
this.legacyId = legacyId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Username in remote idp
|
* Username in remote idp
|
||||||
*
|
*
|
||||||
|
|
|
@ -38,6 +38,13 @@ public class FederatedIdentityModel {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FederatedIdentityModel(FederatedIdentityModel originalIdentity, String userId) {
|
||||||
|
identityProvider = originalIdentity.getIdentityProvider();
|
||||||
|
this.userId = userId;
|
||||||
|
userName = originalIdentity.getUserName();
|
||||||
|
token = originalIdentity.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
public String getUserId() {
|
public String getUserId() {
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -546,6 +546,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
|
.detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
|
||||||
|
|
||||||
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
||||||
|
boolean shouldMigrateId = false;
|
||||||
|
// try to find the user using legacy ID
|
||||||
|
if (federatedUser == null && context.getLegacyId() != null) {
|
||||||
|
federatedIdentityModel = new FederatedIdentityModel(federatedIdentityModel, context.getLegacyId());
|
||||||
|
federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
||||||
|
shouldMigrateId = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
|
// Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
|
||||||
UserSessionModel userSession = new AuthenticationSessionManager(session).getUserSession(authenticationSession);
|
UserSessionModel userSession = new AuthenticationSessionManager(session).getUserSession(authenticationSession);
|
||||||
|
@ -608,6 +615,9 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFederatedIdentity(context, federatedUser);
|
updateFederatedIdentity(context, federatedUser);
|
||||||
|
if (shouldMigrateId) {
|
||||||
|
migrateFederatedIdentityId(context, federatedUser);
|
||||||
|
}
|
||||||
authenticationSession.setAuthenticatedUser(federatedUser);
|
authenticationSession.setAuthenticatedUser(federatedUser);
|
||||||
|
|
||||||
return finishOrRedirectToPostBrokerLogin(authenticationSession, context, false, parsedCode.clientSessionCode);
|
return finishOrRedirectToPostBrokerLogin(authenticationSession, context, false, parsedCode.clientSessionCode);
|
||||||
|
@ -1006,6 +1016,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void migrateFederatedIdentityId(BrokeredIdentityContext context, UserModel federatedUser) {
|
||||||
|
FederatedIdentityModel identityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
||||||
|
FederatedIdentityModel migratedIdentityModel = new FederatedIdentityModel(identityModel, context.getId());
|
||||||
|
|
||||||
|
// since ID is a partial key we need to recreate the identity
|
||||||
|
session.users().removeFederatedIdentity(realmModel, federatedUser, identityModel.getIdentityProvider());
|
||||||
|
session.users().addFederatedIdentity(realmModel, federatedUser, migratedIdentityModel);
|
||||||
|
logger.debugf("Federated user ID was migrated from %s to %s", identityModel.getUserId(), migratedIdentityModel.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateToken(BrokeredIdentityContext context, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
private void updateToken(BrokeredIdentityContext context, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
||||||
if (context.getIdpConfig().isStoreToken() && !ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) {
|
if (context.getIdpConfig().isStoreToken() && !ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) {
|
||||||
federatedIdentityModel.setToken(context.getToken());
|
federatedIdentityModel.setToken(context.getToken());
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
import org.keycloak.broker.social.SocialIdentityProvider;
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -34,9 +36,11 @@ public class InstagramIdentityProvider extends AbstractOAuth2IdentityProvider im
|
||||||
|
|
||||||
public static final String AUTH_URL = "https://api.instagram.com/oauth/authorize";
|
public static final String AUTH_URL = "https://api.instagram.com/oauth/authorize";
|
||||||
public static final String TOKEN_URL = "https://api.instagram.com/oauth/access_token";
|
public static final String TOKEN_URL = "https://api.instagram.com/oauth/access_token";
|
||||||
public static final String PROFILE_URL = "https://api.instagram.com/v1/users/self";
|
public static final String PROFILE_URL = "https://graph.instagram.com/me";
|
||||||
public static final String DEFAULT_SCOPE = "basic";
|
public static final String PROFILE_FIELDS = "id,username";
|
||||||
|
public static final String DEFAULT_SCOPE = "user_profile";
|
||||||
|
public static final String LEGACY_ID_FIELD = "ig_id";
|
||||||
|
|
||||||
public InstagramIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
|
public InstagramIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
|
||||||
super(session, config);
|
super(session, config);
|
||||||
config.setAuthorizationUrl(AUTH_URL);
|
config.setAuthorizationUrl(AUTH_URL);
|
||||||
|
@ -46,25 +50,29 @@ public class InstagramIdentityProvider extends AbstractOAuth2IdentityProvider im
|
||||||
|
|
||||||
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
|
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
|
||||||
try {
|
try {
|
||||||
JsonNode raw = SimpleHttp.doGet(PROFILE_URL,session).param("access_token", accessToken).asJson();
|
// try to get the profile incl. legacy Instagram ID to allow existing users to log in
|
||||||
|
JsonNode profile = fetchUserProfile(accessToken, true);
|
||||||
JsonNode profile = raw.get("data");
|
// ig_id field will get deprecated in the future and eventually might stop working (returning error)
|
||||||
|
if (!profile.has("id")) {
|
||||||
|
logger.debugf("Could not fetch user profile from instagram. Trying without %s.", LEGACY_ID_FIELD);
|
||||||
|
profile = fetchUserProfile(accessToken, false);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(profile.toString());
|
logger.debug(profile.toString());
|
||||||
|
|
||||||
String id = getJsonProperty(profile, "id");
|
// it's not documented whether the new ID system can or cannot have conflicts with the legacy system, therefore
|
||||||
|
// we're using a custom prefix just to be sure
|
||||||
|
String id = "graph_" + getJsonProperty(profile, "id");
|
||||||
|
String username = getJsonProperty(profile, "username");
|
||||||
|
String legacyId = getJsonProperty(profile, LEGACY_ID_FIELD);
|
||||||
|
|
||||||
BrokeredIdentityContext user = new BrokeredIdentityContext(id);
|
BrokeredIdentityContext user = new BrokeredIdentityContext(id);
|
||||||
|
|
||||||
String username = getJsonProperty(profile, "username");
|
|
||||||
|
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
|
|
||||||
String full_name = getJsonProperty(profile, "full_name");
|
|
||||||
|
|
||||||
user.setName(full_name);
|
|
||||||
user.setIdpConfig(getConfig());
|
user.setIdpConfig(getConfig());
|
||||||
user.setIdp(this);
|
user.setIdp(this);
|
||||||
|
if (legacyId != null && !legacyId.isEmpty()) {
|
||||||
|
user.setLegacyId(legacyId);
|
||||||
|
}
|
||||||
|
|
||||||
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
|
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, profile, getConfig().getAlias());
|
||||||
|
|
||||||
|
@ -74,6 +82,18 @@ public class InstagramIdentityProvider extends AbstractOAuth2IdentityProvider im
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected JsonNode fetchUserProfile(String accessToken, boolean includeIgId) throws IOException {
|
||||||
|
String fields = PROFILE_FIELDS;
|
||||||
|
if (includeIgId) {
|
||||||
|
fields += "," + LEGACY_ID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SimpleHttp.doGet(PROFILE_URL,session)
|
||||||
|
.param("access_token", accessToken)
|
||||||
|
.param("fields", fields)
|
||||||
|
.asJson();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDefaultScopes() {
|
protected String getDefaultScopes() {
|
||||||
return DEFAULT_SCOPE;
|
return DEFAULT_SCOPE;
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.testsuite.broker.oidc;
|
||||||
|
|
||||||
|
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||||
|
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
*/
|
||||||
|
public class LegacyIdIdentityProvider extends KeycloakOIDCIdentityProvider {
|
||||||
|
public static final String LEGACY_ID = "3.14159265359";
|
||||||
|
|
||||||
|
public LegacyIdIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) {
|
||||||
|
super(session, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BrokeredIdentityContext getFederatedIdentity(String response) {
|
||||||
|
BrokeredIdentityContext user = super.getFederatedIdentity(response);
|
||||||
|
user.setLegacyId(LEGACY_ID);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.testsuite.broker.oidc;
|
||||||
|
|
||||||
|
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||||
|
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
|
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
*/
|
||||||
|
public class LegacyIdIdentityProviderFactory extends OIDCIdentityProviderFactory {
|
||||||
|
|
||||||
|
public static final String PROVIDER_ID = "legacy-id-idp";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakOIDCIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
|
||||||
|
return new LegacyIdIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# Copyright 2020 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProviderFactory
|
|
@ -18,9 +18,12 @@
|
||||||
package org.keycloak.testsuite.pages.social;
|
package org.keycloak.testsuite.pages.social;
|
||||||
|
|
||||||
import org.openqa.selenium.Keys;
|
import org.openqa.selenium.Keys;
|
||||||
|
import org.openqa.selenium.NoSuchElementException;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.FindBy;
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
*/
|
*/
|
||||||
|
@ -31,11 +34,42 @@ public class InstagramLoginPage extends AbstractSocialLoginPage {
|
||||||
@FindBy(name = "password")
|
@FindBy(name = "password")
|
||||||
private WebElement passwordInput;
|
private WebElement passwordInput;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//button[text()='Save Info']")
|
||||||
|
private WebElement saveInfoBtn;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//button[text()='Authorize']")
|
||||||
|
private WebElement authorizeBtn;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//button[text()='Continue']")
|
||||||
|
private WebElement continueBtn;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void login(String user, String password) {
|
public void login(String user, String password) {
|
||||||
usernameInput.clear();
|
try {
|
||||||
usernameInput.sendKeys(user);
|
usernameInput.clear();
|
||||||
passwordInput.sendKeys(password);
|
usernameInput.sendKeys(user);
|
||||||
passwordInput.sendKeys(Keys.RETURN);
|
passwordInput.sendKeys(password);
|
||||||
|
passwordInput.sendKeys(Keys.RETURN);
|
||||||
|
pause(2000); // wait for the login screen a bit
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveInfoBtn.click();
|
||||||
|
}
|
||||||
|
catch (NoSuchElementException e) {
|
||||||
|
log.info("'Save Info' button not found, ignoring");
|
||||||
|
pause(2000); // wait for the login screen a bit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchElementException e) {
|
||||||
|
log.info("Instagram is already logged in, just confirmation is expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
continueBtn.click();
|
||||||
|
}
|
||||||
|
catch (NoSuchElementException e) {
|
||||||
|
log.info("'Continue' button not found, trying 'Authorize'...");
|
||||||
|
authorizeBtn.click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,10 +316,14 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
|
|
||||||
protected void assertLoggedInAccountManagement() {
|
protected void assertLoggedInAccountManagement() {
|
||||||
|
assertLoggedInAccountManagement(bc.getUserLogin(), bc.getUserEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertLoggedInAccountManagement(String username, String email) {
|
||||||
waitForAccountManagementTitle();
|
waitForAccountManagementTitle();
|
||||||
Assert.assertTrue(accountUpdateProfilePage.isCurrent());
|
Assert.assertTrue(accountUpdateProfilePage.isCurrent());
|
||||||
Assert.assertEquals(accountUpdateProfilePage.getUsername(), bc.getUserLogin());
|
Assert.assertEquals(accountUpdateProfilePage.getUsername(), username);
|
||||||
Assert.assertEquals(accountUpdateProfilePage.getEmail(), bc.getUserEmail());
|
Assert.assertEquals(accountUpdateProfilePage.getEmail(), email);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void waitForAccountManagementTitle() {
|
protected void waitForAccountManagementTitle() {
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.testsuite.broker;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
|
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProviderFactory;
|
||||||
|
import org.keycloak.testsuite.util.FederatedIdentityBuilder;
|
||||||
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.getProviderRoot;
|
||||||
|
import static org.keycloak.testsuite.broker.oidc.LegacyIdIdentityProvider.LEGACY_ID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
*/
|
||||||
|
public class BrokerWithLegacyIdTest extends AbstractInitializedBaseBrokerTest {
|
||||||
|
private static final UserRepresentation consumerUser = UserBuilder.create()
|
||||||
|
.username("anakin")
|
||||||
|
.firstName("Darth")
|
||||||
|
.lastName("Vader")
|
||||||
|
.email("anakin@skywalker.tatooine")
|
||||||
|
.password("Come to the Dark Side. We have cookies")
|
||||||
|
.build();
|
||||||
|
private UserResource consumerUserResource;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BrokerConfiguration getBrokerConfiguration() {
|
||||||
|
return new KcOidcBrokerConfiguration() {
|
||||||
|
@Override
|
||||||
|
public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) {
|
||||||
|
IdentityProviderRepresentation idp = super.setUpIdentityProvider(syncMode);
|
||||||
|
idp.setProviderId(LegacyIdIdentityProviderFactory.PROVIDER_ID);
|
||||||
|
return idp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeBrokerTest() {
|
||||||
|
super.beforeBrokerTest();
|
||||||
|
RealmResource consumerRealm = realmsResouce().realm(bc.consumerRealmName());
|
||||||
|
|
||||||
|
String consumerUserId = createUserWithAdminClient(consumerRealm, consumerUser);
|
||||||
|
|
||||||
|
FederatedIdentityRepresentation identity = FederatedIdentityBuilder.create()
|
||||||
|
.userId(LEGACY_ID)
|
||||||
|
.userName(bc.getUserLogin())
|
||||||
|
.identityProvider(IDP_OIDC_ALIAS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
consumerUserResource = consumerRealm.users().get(consumerUserId);
|
||||||
|
consumerUserResource.addFederatedIdentity(IDP_OIDC_ALIAS, identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loginWithLegacyId() {
|
||||||
|
assertEquals(LEGACY_ID, getFederatedIdentity().getUserId());
|
||||||
|
// login as existing user with legacy ID (from e.g. a deprecated API)
|
||||||
|
logInAsUserInIDP();
|
||||||
|
// id should be migrated to new one
|
||||||
|
assertEquals(userId, getFederatedIdentity().getUserId());
|
||||||
|
assertLoggedInAccountManagement(consumerUser.getUsername(), consumerUser.getEmail());
|
||||||
|
|
||||||
|
logoutFromRealm(getProviderRoot(), bc.providerRealmName());
|
||||||
|
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
|
||||||
|
|
||||||
|
// try to login again to double check the new ID works
|
||||||
|
logInAsUserInIDP();
|
||||||
|
assertEquals(userId, getFederatedIdentity().getUserId());
|
||||||
|
assertLoggedInAccountManagement(consumerUser.getUsername(), consumerUser.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
private FederatedIdentityRepresentation getFederatedIdentity() {
|
||||||
|
List<FederatedIdentityRepresentation> identities = consumerUserResource.getFederatedIdentity();
|
||||||
|
assertEquals(1, identities.size());
|
||||||
|
return identities.get(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -333,7 +333,7 @@ public class SocialLoginTest extends AbstractKeycloakTest {
|
||||||
public void instagramLogin() throws InterruptedException {
|
public void instagramLogin() throws InterruptedException {
|
||||||
setTestProvider(INSTAGRAM);
|
setTestProvider(INSTAGRAM);
|
||||||
performLogin();
|
performLogin();
|
||||||
assertUpdateProfile(false, false, true);
|
assertUpdateProfile(true, true, true);
|
||||||
assertAccount();
|
assertAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue