Merge pull request #1125 from velias/KEYCLOAK-1195

KEYCLOAK-1195 - added identity brokes ordering for login page
This commit is contained in:
Stian Thorgersen 2015-04-09 15:44:43 +02:00
commit 4955f36115
8 changed files with 147 additions and 9 deletions

View file

@ -284,6 +284,15 @@
during the authentication process. during the authentication process.
</entry> </entry>
</row> </row>
<row>
<entry>
<literal>GUI order</literal>
</entry>
<entry>
Allows you to define order of the provider when shown on login page.
You can put number into this field, providers with lower numbers are shown first.
</entry>
</row>
<!--<row>--> <!--<row>-->
<!--<entry>--> <!--<entry>-->
<!--<literal>Store Tokens</literal>--> <!--<literal>Store Tokens</literal>-->

View file

@ -55,6 +55,13 @@
</div> </div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group">
<label class="col-sm-2 control-label" for="guiOrder">GUI order</label>
<div class="col-sm-4">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
<span tooltip-placement="right" tooltip="Number defining order of the provider in GUI (eg. on Login page)." class="fa fa-info-circle"></span>
</div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend uncollapsed><span class="text">OpenID Connect Config</span> <span tooltip-placement="right" tooltip="OIDC SP and external IDP configuration." class="fa fa-info-circle"></span></legend> <legend uncollapsed><span class="text">OpenID Connect Config</span> <span tooltip-placement="right" tooltip="OIDC SP and external IDP configuration." class="fa fa-info-circle"></span></legend>

View file

@ -55,6 +55,13 @@
</div> </div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group">
<label class="col-sm-2 control-label" for="guiOrder">GUI order</label>
<div class="col-sm-4">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
<span tooltip-placement="right" tooltip="Number defining order of the provider in GUI (eg. on Login page)." class="fa fa-info-circle"></span>
</div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend uncollapsed><span class="text">SAML Config</span> <span tooltip-placement="right" tooltip="SAML SP and external IDP configuration." class="fa fa-info-circle"></span></legend> <legend uncollapsed><span class="text">SAML Config</span> <span tooltip-placement="right" tooltip="SAML SP and external IDP configuration." class="fa fa-info-circle"></span></legend>

View file

@ -73,6 +73,13 @@
</div> </div>
<span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group">
<label class="col-sm-2 control-label" for="guiOrder">GUI order</label>
<div class="col-sm-4">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
<span tooltip-placement="right" tooltip="Number defining order of the provider in GUI (eg. on Login page)." class="fa fa-info-circle"></span>
</div>
</fieldset> </fieldset>
<div class="pull-right form-actions"> <div class="pull-right form-actions">

View file

@ -12,7 +12,7 @@
<caption class="hidden">Table of identity providers</caption> <caption class="hidden">Table of identity providers</caption>
<thead> <thead>
<tr> <tr>
<th colspan="5" class="kc-table-actions"> <th colspan="3" class="kc-table-actions">
<div class="pull-right"> <div class="pull-right">
<div class="select-kc"> <div class="select-kc">
<select ng-model="provider" <select ng-model="provider"
@ -27,6 +27,7 @@
<tr ng-show="configuredProviders.length > 0"> <tr ng-show="configuredProviders.length > 0">
<th>Name</th> <th>Name</th>
<th>Provider</th> <th>Provider</th>
<th width="15%">GUI order</th>
</tr> </tr>
</thead> </thead>
<tbody ng-show="configuredProviders.length > 0"> <tbody ng-show="configuredProviders.length > 0">
@ -35,6 +36,7 @@
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a> <a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a>
</td> </td>
<td>{{identityProvider.providerId}}</td> <td>{{identityProvider.providerId}}</td>
<td>{{identityProvider.config.guiOrder}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -71,6 +71,12 @@
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -27,11 +27,15 @@ import org.keycloak.services.resources.flows.Urls;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.net.URI; import java.net.URI;
import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
* @author Vlastimil Elias (velias at redhat dot com)
*/ */
public class IdentityProviderBean { public class IdentityProviderBean {
@ -45,23 +49,24 @@ public class IdentityProviderBean {
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders(); List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
if (!identityProviders.isEmpty()) { if (!identityProviders.isEmpty()) {
providers = new LinkedList<IdentityProvider>(); Set<IdentityProvider> orderedSet = new TreeSet<>(IdentityProviderComparator.INSTANCE);
for (IdentityProviderModel identityProvider : identityProviders) { for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isEnabled()) { if (identityProvider.isEnabled()) {
addIdentityProvider(realm, baseURI, identityProvider); addIdentityProvider(orderedSet, realm, baseURI, identityProvider);
} }
} }
if (!providers.isEmpty()) { if (!orderedSet.isEmpty()) {
providers = new LinkedList<IdentityProvider>(orderedSet);
displaySocial = true; displaySocial = true;
} }
} }
} }
private void addIdentityProvider(RealmModel realm, URI baseURI, IdentityProviderModel identityProvider) { private void addIdentityProvider(Set<IdentityProvider> orderedSet, RealmModel realm, URI baseURI, IdentityProviderModel identityProvider) {
String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getAlias(), realm.getName()).toString(); String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getAlias(), realm.getName()).toString();
providers.add(new IdentityProvider(identityProvider.getAlias(), identityProvider.getProviderId(), loginUrl)); orderedSet.add(new IdentityProvider(identityProvider.getAlias(), identityProvider.getProviderId(), loginUrl,
identityProvider.getConfig() != null ? identityProvider.getConfig().get("guiOrder") : null));
} }
public List<IdentityProvider> getProviders() { public List<IdentityProvider> getProviders() {
@ -77,12 +82,13 @@ public class IdentityProviderBean {
private final String alias; private final String alias;
private final String providerId; // This refer to providerType (facebook, google, etc.) private final String providerId; // This refer to providerType (facebook, google, etc.)
private final String loginUrl; private final String loginUrl;
private final String guiOrder;
public IdentityProvider(String alias, String providerId,String loginUrl) { public IdentityProvider(String alias, String providerId, String loginUrl, String guiOrder) {
this.alias = alias; this.alias = alias;
this.providerId = providerId; this.providerId = providerId;
this.loginUrl = loginUrl; this.loginUrl = loginUrl;
this.guiOrder = guiOrder;
} }
public String getAlias() { public String getAlias() {
@ -96,5 +102,44 @@ public class IdentityProviderBean {
public String getProviderId() { public String getProviderId() {
return providerId; return providerId;
} }
public String getGuiOrder() {
return guiOrder;
}
}
public static class IdentityProviderComparator implements Comparator<IdentityProvider> {
public static IdentityProviderComparator INSTANCE = new IdentityProviderComparator();
private IdentityProviderComparator() {
}
@Override
public int compare(IdentityProvider o1, IdentityProvider o2) {
int o1order = parseOrder(o1);
int o2order = parseOrder(o2);
if (o1order > o2order)
return 1;
else if (o1order < o2order)
return -1;
return 1;
}
private int parseOrder(IdentityProvider ip) {
if (ip != null && ip.getGuiOrder() != null) {
try {
return Integer.parseInt(ip.getGuiOrder());
} catch (NumberFormatException e) {
// ignore it and use defaulr
}
}
return 10000;
}
} }
} }

View file

@ -0,0 +1,55 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.keycloak.login.freemarker.model;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.login.freemarker.model.IdentityProviderBean.IdentityProvider;
import org.keycloak.login.freemarker.model.IdentityProviderBean.IdentityProviderComparator;
/**
* Unit test for {@link IdentityProviderBean}
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public class IdentityProviderBeanTest {
@Test
public void testIdentityProviderComparator() {
IdentityProvider o1 = new IdentityProvider("alias1", "id1", "ur1", null);
IdentityProvider o2 = new IdentityProvider("alias2", "id2", "ur2", null);
// guiOrder not defined at any object - first is always lower
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o1, o2));
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o2, o1));
// guiOrder is not a number so it is same as not defined - first is always lower
o1 = new IdentityProvider("alias1", "id1", "ur1", "not a number");
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o1, o2));
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o2, o1));
// guiOrder is defined for one only to it is always first
o1 = new IdentityProvider("alias1", "id1", "ur1", "0");
Assert.assertEquals(-1, IdentityProviderComparator.INSTANCE.compare(o1, o2));
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o2, o1));
// guiOrder is defined for both but is same - first is always lower
o1 = new IdentityProvider("alias1", "id1", "ur1", "0");
o2 = new IdentityProvider("alias2", "id2", "ur2", "0");
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o1, o2));
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o2, o1));
// guiOrder is reflected
o1 = new IdentityProvider("alias1", "id1", "ur1", "0");
o2 = new IdentityProvider("alias2", "id2", "ur2", "1");
Assert.assertEquals(-1, IdentityProviderComparator.INSTANCE.compare(o1, o2));
Assert.assertEquals(1, IdentityProviderComparator.INSTANCE.compare(o2, o1));
}
}