[KEYCLOAK-4900] Pass login_hint parameter to idp & review (#4421)

This commit is contained in:
Stian Thorgersen authored and GitHub committed 2017-08-25 10:14:38 +02:00
1 parent e173bf33ba
commit d58c6ad4e0
8 files changed
+239 -6

No files matched your search

@@ -39,6 +39,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.messages.Messages;
import org.keycloak.sessions.AuthenticationSessionModel;
@@ -225,12 +226,18 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
final UriBuilder uriBuilder = UriBuilder.fromUri(getConfig().getAuthorizationUrl())
.queryParam(OAUTH2_PARAMETER_SCOPE, getConfig().getDefaultScope())
.queryParam(OAUTH2_PARAMETER_STATE, request.getState().getEncodedState())
.queryParam(OAUTH2_PARAMETER_RESPONSE_TYPE, "code")
.queryParam(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
String loginHint = request.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
if (getConfig().isLoginHint() && loginHint != null) {
uriBuilder.queryParam(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
}
return uriBuilder;
}
/**
@@ -74,4 +74,12 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
public void setDefaultScope(String defaultScope) {
getConfig().put("defaultScope", defaultScope);
}
public boolean isLoginHint() {
return Boolean.valueOf(getConfig().get("loginHint"));
}
public void setLoginHint(boolean loginHint) {
getConfig().put("loginHint", String.valueOf(loginHint));
}
}
@@ -24,7 +24,6 @@ import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import java.io.IOException;
import java.util.HashMap;
@@ -121,10 +120,18 @@ public class AbstractOAuth2IdentityProviderTest {
Assert.assertEquals("458rtf", fi.getId());
}
private TestProvider getTested() {
private TestProvider getTested() {
return new TestProvider(getConfig(null, null, null, Boolean.FALSE));
}
private OAuth2IdentityProviderConfig getConfig(final String autorizationUrl, final String defaultScope, final String clientId, final Boolean isLoginHint) {
IdentityProviderModel model = new IdentityProviderModel();
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
return new TestProvider(config);
config.setAuthorizationUrl(autorizationUrl);
config.setDefaultScope(defaultScope);
config.setClientId(clientId);
config.setLoginHint(isLoginHint);
return config;
}
private static class TestProvider extends AbstractOAuth2IdentityProvider<OAuth2IdentityProviderConfig> {
@@ -105,7 +105,12 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
Map<String, String> config = idp.getConfig();
applyDefaultConfiguration(suiteContext, config);
return idp;
}
protected void applyDefaultConfiguration(final SuiteContext suiteContext, final Map<String, String> config) {
config.put("clientId", CLIENT_ID);
config.put("clientSecret", CLIENT_SECRET);
config.put("prompt", "login");
@@ -115,8 +120,6 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
config.put("userInfoUrl", getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/userinfo");
config.put("defaultScope", "email profile");
config.put("backchannelSupported", "true");
return idp;
}
@Override
@@ -0,0 +1,114 @@
package org.keycloak.testsuite.broker;
import java.util.List;
import java.util.Map;
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID;
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.arquillian.SuiteContext;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
public class KcOidcBrokerLoginHintTest extends AbstractBrokerTest {
@Override
protected BrokerConfiguration getBrokerConfiguration() {
return new KcOidcBrokerConfigurationWithLoginHint();
}
@Override
protected String getAccountUrl(String realmName) {
return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/account";
}
@Override
protected Iterable<IdentityProviderMapperRepresentation> createIdentityProviderMappers() {
IdentityProviderMapperRepresentation attrMapper1 = new IdentityProviderMapperRepresentation();
attrMapper1.setName("manager-role-mapper");
attrMapper1.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
attrMapper1.setConfig(ImmutableMap.<String,String>builder()
.put("external.role", "manager")
.put("role", "manager")
.build());
IdentityProviderMapperRepresentation attrMapper2 = new IdentityProviderMapperRepresentation();
attrMapper2.setName("user-role-mapper");
attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
attrMapper2.setConfig(ImmutableMap.<String,String>builder()
.put("external.role", "user")
.put("role", "user")
.build());
return Lists.newArrayList(attrMapper1, attrMapper2);
}
private class KcOidcBrokerConfigurationWithLoginHint extends KcOidcBrokerConfiguration {
@Override
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
Map<String, String> config = idp.getConfig();
applyDefaultConfiguration(suiteContext, config);
config.put("loginHint", "true");
return idp;
}
}
@Override
protected void loginUser() {
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
driver.navigate().to(driver.getCurrentUrl() + "&login_hint=" + USER_EMAIL);
log.debug("Clicking social " + bc.getIDPAlias());
accountLoginPage.clickSocial(bc.getIDPAlias());
waitForPage(driver, "log in to");
Assert.assertTrue("Driver should be on the provider realm page right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
Assert.assertTrue("User identifiant should be fullfilled",
accountLoginPage.getUsername().equalsIgnoreCase(USER_EMAIL));
log.debug("Logging in");
accountLoginPage.login(bc.getUserPassword());
waitForPage(driver, "update account information");
updateAccountInformationPage.assertCurrent();
Assert.assertTrue("We must be on correct realm right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
log.debug("Updating info on updateAccount page");
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users();
int userCount = consumerUsers.count();
Assert.assertTrue("There must be at least one user", userCount > 0);
List<UserRepresentation> users = consumerUsers.search("", 0, userCount);
boolean isUserFound = false;
for (UserRepresentation user : users) {
if (user.getUsername().equals(bc.getUserLogin()) && user.getEmail().equals(bc.getUserEmail())) {
isUserFound = true;
break;
}
}
Assert.assertTrue("There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName(),
isUserFound);
}
}
@@ -0,0 +1,85 @@
package org.keycloak.testsuite.broker;
import java.util.List;
import java.util.Map;
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID;
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
import org.apache.commons.lang3.StringUtils;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.arquillian.SuiteContext;
public class KcOidcBrokerNoLoginHintTest extends KcOidcBrokerLoginHintTest {
@Override
protected BrokerConfiguration getBrokerConfiguration() {
return new KcOidcBrokerConfigurationWithNoLoginHint();
}
private class KcOidcBrokerConfigurationWithNoLoginHint extends KcOidcBrokerConfiguration {
@Override
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
Map<String, String> config = idp.getConfig();
applyDefaultConfiguration(suiteContext, config);
config.put("loginHint", "false");
return idp;
}
}
@Override
protected void loginUser() {
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
driver.navigate().to(driver.getCurrentUrl() + "&login_hint=" + USER_EMAIL);
log.debug("Clicking social " + bc.getIDPAlias());
accountLoginPage.clickSocial(bc.getIDPAlias());
waitForPage(driver, "log in to");
Assert.assertTrue("Driver should be on the provider realm page right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
Assert.assertTrue("User identifiant should not be fullfilled",
StringUtils.isBlank(accountLoginPage.getUsername()));
log.debug("Logging in");
accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
waitForPage(driver, "update account information");
updateAccountInformationPage.assertCurrent();
Assert.assertTrue("We must be on correct realm right now",
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
log.debug("Updating info on updateAccount page");
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users();
int userCount = consumerUsers.count();
Assert.assertTrue("There must be at least one user", userCount > 0);
List<UserRepresentation> users = consumerUsers.search("", 0, userCount);
boolean isUserFound = false;
for (UserRepresentation user : users) {
if (user.getUsername().equals(bc.getUserLogin()) && user.getEmail().equals(bc.getUserEmail())) {
isUserFound = true;
break;
}
}
Assert.assertTrue("There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName(),
isUserFound);
}
}
@@ -504,6 +504,8 @@ authorization-url=Authorization URL
authorization-url.tooltip=The Authorization Url.
token-url=Token URL
token-url.tooltip=The Token URL.
loginHint=Pass login_hint
loginHint.tooltip=Pass login_hint to identity provider.
logout-url=Logout URL
identity-provider.logout-url.tooltip=End session endpoint to use to logout user from external IDP.
backchannel-logout=Backchannel Logout
@@ -120,6 +120,13 @@
</div>
<kc-tooltip>{{:: 'authorization-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" for="loginHint">{{:: 'loginHint' | translate}}</label>
<div class="col-sm-4">
<input ng-model="identityProvider.config.loginHint" id="loginHint" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
<kc-tooltip>{{:: 'loginHint.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="tokenUrl"><span class="required">*</span> {{:: 'token-url' | translate}}</label>
<div class="col-md-6">