Merge pull request #3762 from sldab/hide-providers
KEYCLOAK-4224 Allow hiding identity providers on login page
This commit is contained in:
commit
3653d7ed9a
13 changed files with 170 additions and 15 deletions
|
@ -27,6 +27,7 @@ import java.net.URI;
|
|||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
@ -63,10 +64,13 @@ public class IdentityProviderBean {
|
|||
private void addIdentityProvider(Set<IdentityProvider> orderedSet, RealmModel realm, URI baseURI, IdentityProviderModel identityProvider) {
|
||||
String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getAlias(), realm.getName()).toString();
|
||||
String displayName = KeycloakModelUtils.getIdentityProviderDisplayName(session, identityProvider);
|
||||
|
||||
orderedSet.add(new IdentityProvider(identityProvider.getAlias(),
|
||||
displayName, identityProvider.getProviderId(), loginUrl,
|
||||
identityProvider.getConfig() != null ? identityProvider.getConfig().get("guiOrder") : null));
|
||||
Map<String, String> config = identityProvider.getConfig();
|
||||
boolean hideOnLoginPage = config != null && Boolean.parseBoolean(config.get("hideOnLoginPage"));
|
||||
if (!hideOnLoginPage) {
|
||||
orderedSet.add(new IdentityProvider(identityProvider.getAlias(),
|
||||
displayName, identityProvider.getProviderId(), loginUrl,
|
||||
config != null ? config.get("guiOrder") : null));
|
||||
}
|
||||
}
|
||||
|
||||
public List<IdentityProvider> getProviders() {
|
||||
|
|
|
@ -50,13 +50,23 @@ public class AccountFederatedIdentityPage extends AbstractAccountPage {
|
|||
public boolean isCurrent() {
|
||||
return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Federated Identities");
|
||||
}
|
||||
|
||||
public void clickAddProvider(String providerId) {
|
||||
driver.findElement(By.id("add-" + providerId)).click();
|
||||
|
||||
public WebElement findAddProviderButton(String alias) {
|
||||
return driver.findElement(By.id("add-" + alias));
|
||||
}
|
||||
|
||||
public WebElement findRemoveProviderButton(String alias) {
|
||||
return driver.findElement(By.id("remove-" + alias));
|
||||
}
|
||||
|
||||
public void clickRemoveProvider(String providerId) {
|
||||
driver.findElement(By.id("remove-" + providerId)).click();
|
||||
public void clickAddProvider(String alias) {
|
||||
WebElement addButton = findAddProviderButton(alias);
|
||||
addButton.click();
|
||||
}
|
||||
|
||||
public void clickRemoveProvider(String alias) {
|
||||
WebElement addButton = findRemoveProviderButton(alias);
|
||||
addButton.click();
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
|||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.drone.Different;
|
||||
import org.keycloak.testsuite.pages.AccountApplicationsPage;
|
||||
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
|
||||
import org.keycloak.testsuite.pages.AccountLogPage;
|
||||
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
||||
import org.keycloak.testsuite.pages.AccountSessionsPage;
|
||||
|
@ -95,6 +96,12 @@ public class AccountTest extends AbstractTestRealmKeycloakTest {
|
|||
.alias("myoidc")
|
||||
.displayName("MyOIDC")
|
||||
.build());
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("oidc")
|
||||
.alias("myhiddenoidc")
|
||||
.displayName("MyHiddenOIDC")
|
||||
.hideOnLoginPage()
|
||||
.build());
|
||||
|
||||
RealmBuilder.edit(testRealm)
|
||||
.user(user2);
|
||||
|
@ -138,6 +145,9 @@ public class AccountTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Page
|
||||
protected AccountApplicationsPage applicationsPage;
|
||||
|
||||
@Page
|
||||
protected AccountFederatedIdentityPage federatedIdentityPage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
@ -918,7 +928,13 @@ public class AccountTest extends AbstractTestRealmKeycloakTest {
|
|||
Assert.assertEquals("GitHub", loginPage.findSocialButton("github").getText());
|
||||
Assert.assertEquals("mysaml", loginPage.findSocialButton("mysaml").getText());
|
||||
Assert.assertEquals("MyOIDC", loginPage.findSocialButton("myoidc").getText());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdentityProviderHiddenOnLoginPageIsVisbleInAccount(){
|
||||
federatedIdentityPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
Assert.assertNotNull(federatedIdentityPage.findAddProviderButton("myhiddenoidc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2016 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.forms;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
|
||||
public class HiddenProviderTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Override
|
||||
protected RealmResource testRealm() {
|
||||
return adminClient.realm("realm-with-broker");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("oidc")
|
||||
.alias("visible-oidc")
|
||||
.displayName("VisibleOIDC")
|
||||
.build());
|
||||
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
|
||||
.providerId("oidc")
|
||||
.alias("hidden-oidc")
|
||||
.displayName("HiddenOIDC")
|
||||
.hideOnLoginPage()
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibleProviderButton() {
|
||||
loginPage.open();
|
||||
Assert.assertNotNull(loginPage.findSocialButton("visible-oidc"));
|
||||
}
|
||||
|
||||
@Test(expected=org.openqa.selenium.NoSuchElementException.class)
|
||||
public void testHiddenProviderButton() {
|
||||
loginPage.open();
|
||||
Assert.assertNull(loginPage.findSocialButton("hidden-oidc"));
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.testsuite.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
/**
|
||||
|
@ -48,6 +49,14 @@ public class IdentityProviderBuilder {
|
|||
rep.setDisplayName(displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityProviderBuilder hideOnLoginPage() {
|
||||
if (rep.getConfig() == null) {
|
||||
rep.setConfig(new HashMap<>());
|
||||
}
|
||||
rep.getConfig().put("hideOnLoginPage", "true");
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityProviderRepresentation build() {
|
||||
return rep;
|
||||
|
|
|
@ -87,6 +87,13 @@ public class IdentityProviderHintTest {
|
|||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
|
||||
assertTrue(this.driver.getPageSource().contains("idToken"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulRedirectToProviderHiddenOnLoginPage() {
|
||||
this.driver.navigate().to("http://localhost:8081/test-app?kc_idp_hint=kc-oidc-idp-hidden");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidIdentityProviderHint() {
|
||||
|
|
|
@ -143,13 +143,13 @@ public class LoginPage extends AbstractPage {
|
|||
registerLink.click();
|
||||
}
|
||||
|
||||
public void clickSocial(String providerId) {
|
||||
WebElement socialButton = findSocialButton(providerId);
|
||||
public void clickSocial(String alias) {
|
||||
WebElement socialButton = findSocialButton(alias);
|
||||
socialButton.click();
|
||||
}
|
||||
|
||||
public WebElement findSocialButton(String providerId) {
|
||||
String id = "zocial-" + providerId;
|
||||
public WebElement findSocialButton(String alias) {
|
||||
String id = "zocial-" + alias;
|
||||
return this.driver.findElement(By.id(id));
|
||||
}
|
||||
|
||||
|
|
|
@ -205,6 +205,24 @@
|
|||
"defaultScope": "email profile",
|
||||
"backchannelSupported": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"alias" : "kc-oidc-idp-hidden",
|
||||
"providerId" : "keycloak-oidc",
|
||||
"enabled": true,
|
||||
"storeToken" : true,
|
||||
"addReadTokenRoleOnCreate": true,
|
||||
"config": {
|
||||
"clientId": "broker-app",
|
||||
"clientSecret": "secret",
|
||||
"authorizationUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-hidden/protocol/openid-connect/auth",
|
||||
"tokenUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-hidden/protocol/openid-connect/token",
|
||||
"userInfoUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-hidden/protocol/openid-connect/userinfo",
|
||||
"logoutUrl": "http://localhost:8082/auth/realms/realm-with-oidc-idp-hidden/protocol/openid-connect/logout",
|
||||
"defaultScope": "email profile",
|
||||
"backchannelSupported": "true",
|
||||
"hideOnLoginPage": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"identityProviderMappers": [
|
||||
|
|
|
@ -2,6 +2,7 @@ consoleTitle=Keycloak Admin Console
|
|||
|
||||
# Common messages
|
||||
enabled=Enabled
|
||||
hidden=Hidden
|
||||
name=Name
|
||||
displayName=Display name
|
||||
displayNameHtml=HTML Display name
|
||||
|
@ -466,6 +467,8 @@ off=Off
|
|||
update-profile-on-first-login.tooltip=Define conditions under which a user has to update their profile during first-time login.
|
||||
trust-email=Trust Email
|
||||
trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm.
|
||||
hide-on-login-page=Hide on Login Page
|
||||
hide-on-login-page.tooltip=If hidden, then login with this provider is possible only if requested explicitly, e.g. using the 'kc_idp_hint' parameter.
|
||||
gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page).
|
||||
first-broker-login-flow.tooltip=Alias of authentication flow, which is triggered after first login with this identity provider. Term 'First Login' means that there is not yet existing Keycloak account linked with the authenticated identity provider account.
|
||||
post-broker-login-flow.tooltip=Alias of authentication flow, which is triggered after each login with this identity provider. Useful if you want additional verification of each user authenticated with this identity provider (for example OTP). Leave this empty if you don't want any additional authenticators to be triggered after login with this identity provider. Also note, that authenticator implementations must assume that user is already set in ClientSession as identity provider already set it.
|
||||
|
|
|
@ -64,6 +64,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="identityProvider.config.hideOnLoginPage" name="identityProvider.config.hideOnLoginPage" id="hideOnLoginPage" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'hide-on-login-page.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
|
@ -61,6 +61,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="identityProvider.config.hideOnLoginPage" name="identityProvider.config.hideOnLoginPage" id="hideOnLoginPage" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'hide-on-login-page.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
|
@ -78,6 +78,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="identityProvider.config.hideOnLoginPage" name="identityProvider.config.hideOnLoginPage" id="hideOnLoginPage" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'hide-on-login-page.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<caption class="hidden">{{:: 'table-of-identity-providers' | translate}}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="6" class="kc-table-actions">
|
||||
<th colspan="7" class="kc-table-actions">
|
||||
<div class="dropdown pull-right" data-ng-show="access.manageIdentityProviders">
|
||||
<select class="form-control" ng-model="provider"
|
||||
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
|
||||
|
@ -47,6 +47,7 @@
|
|||
<th>{{:: 'name' | translate}}</th>
|
||||
<th>{{:: 'provider' | translate}}</th>
|
||||
<th>{{:: 'enabled' | translate}}</th>
|
||||
<th>{{:: 'hidden' | translate}}</th>
|
||||
<th width="15%">{{:: 'gui-order' | translate}}</th>
|
||||
<th colspan="2">{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
|
@ -62,6 +63,7 @@
|
|||
</td>
|
||||
<td>{{identityProvider.providerId}}</td>
|
||||
<td translate="{{identityProvider.enabled}}"></td>
|
||||
<td translate="{{identityProvider.config.hideOnLoginPage == 'true'}}"></td>
|
||||
<td>{{identityProvider.config.guiOrder}}</td>
|
||||
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{:: 'edit' | translate}}</td>
|
||||
<td class="kc-action-cell" data-ng-show="access.manageIdentityProviders" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</td>
|
||||
|
|
Loading…
Reference in a new issue