From 894f3cd2246b053c422a436916d5a38dff3dd244 Mon Sep 17 00:00:00 2001 From: Mike McKay <mfmckay@wustl.edu> Date: Thu, 28 Jul 2016 11:40:15 -0500 Subject: [PATCH] XNAT-4387 Made auth methods fully customizable via plugins. If none are specified, it will default to db authentication, but if any are specified, only the specified ones will show up. I also added support for an order property to control the order of the methods in the dropdown, as well as the order in which they are checked when users attempt authentication. I see this plugin-based way of configuring providers remaining as an alternative to AdminUI configuration once that is implemented. --- .../xnat/security/XnatProviderManager.java | 8 +- .../AliasTokenAuthenticationProvider.java | 11 +++ .../AuthenticationProviderAggregator.java | 75 +++++++++++++------ ...aseAuthenticationProviderConfigurator.java | 11 ++- ...dapAuthenticationProviderConfigurator.java | 3 + .../provider/XnatAuthenticationProvider.java | 9 +++ .../XnatDatabaseAuthenticationProvider.java | 11 +++ .../XnatLdapAuthenticationProvider.java | 11 +++ 8 files changed, 112 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/nrg/xnat/security/XnatProviderManager.java b/src/main/java/org/nrg/xnat/security/XnatProviderManager.java index 3ca88bb0..dcd35888 100644 --- a/src/main/java/org/nrg/xnat/security/XnatProviderManager.java +++ b/src/main/java/org/nrg/xnat/security/XnatProviderManager.java @@ -253,9 +253,11 @@ public class XnatProviderManager extends ProviderManager { private XnatAuthenticationProvider findAuthenticationProvider(XnatAuthenticationProviderMatcher matcher) { List<AuthenticationProvider> prov = getProviders(); for (AuthenticationProvider ap : prov) { - XnatAuthenticationProvider xap = (XnatAuthenticationProvider) ap; - if (matcher.matches(xap)) { - return xap; + if(XnatAuthenticationProvider.class.isAssignableFrom(ap.getClass())) { + XnatAuthenticationProvider xap = (XnatAuthenticationProvider) ap; + if (matcher.matches(xap)) { + return xap; + } } } return null; diff --git a/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java b/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java index e0de6d27..51840c2d 100644 --- a/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java +++ b/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java @@ -110,6 +110,16 @@ public class AliasTokenAuthenticationProvider extends AbstractUserDetailsAuthent return getName(); } + @Override + public int getOrder() { + return _order; + } + + @Override + public void setOrder(int order) { + _order = order; + } + @Override protected void additionalAuthenticationChecks(final UserDetails userDetails, final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { @@ -173,4 +183,5 @@ public class AliasTokenAuthenticationProvider extends AbstractUserDetailsAuthent private final AliasTokenService _aliasTokenService; private final XdatUserAuthService _userAuthService; + private int _order = -1; } diff --git a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java index 6990368e..f7ac4ba6 100644 --- a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java +++ b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java @@ -2,6 +2,7 @@ package org.nrg.xnat.security.config; import org.apache.commons.lang3.StringUtils; import org.nrg.framework.utilities.BasicXnatResourceLocator; +import org.nrg.xnat.security.provider.XnatAuthenticationProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; @@ -13,29 +14,33 @@ import java.util.*; public class AuthenticationProviderAggregator extends ArrayList<AuthenticationProvider> { public AuthenticationProviderAggregator(List<AuthenticationProvider> standaloneProviders, Map<String, AuthenticationProviderConfigurator> configurators) { - ArrayList<String> providerArray = new ArrayList<>(); - String dbName = "Database"; - String dbId = "localdb"; - String dbType = "db"; - providerArray.add(dbType); - HashMap<String, HashMap<String, String>> providerMap = new HashMap<>(); - providerMap.put(dbType, new HashMap<String, String>()); - providerMap.get(dbType).put("name", dbName); - providerMap.get(dbType).put("id", dbId); - providerMap.get(dbType).put("type", dbType); + ArrayList<HashMap<String, String>> providerList = new ArrayList<>(); // Populate map of properties try { String filenameEnd = "-provider.properties"; final List<Resource> resources = BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/auth/**/*" + filenameEnd); - for (final Resource resource : resources) { - String filename = resource.getFilename(); - String id = filename.substring(0, (filename.length() - filenameEnd.length())); - providerMap.put(id, new HashMap<String, String>()); - providerArray.add(id); - final Properties provider = PropertiesLoaderUtils.loadProperties(resource); - for (Map.Entry<Object, Object> providerProperty : provider.entrySet()) { - providerMap.get(id).put(providerProperty.getKey().toString(), providerProperty.getValue().toString()); + if(resources==null || resources.isEmpty()){ + String dbName = "Database"; + String dbId = "localdb"; + String dbType = "db"; + HashMap<String, String> dbProv = new HashMap<String, String>(); + dbProv.put("name", dbName); + dbProv.put("id", dbId); + dbProv.put("type", dbType); + providerList.add(dbProv); + } + else { + for (final Resource resource : resources) { + String filename = resource.getFilename(); + String id = filename.substring(0, (filename.length() - filenameEnd.length())); + HashMap<String, String> newProv = new HashMap<String, String>(); + + final Properties provider = PropertiesLoaderUtils.loadProperties(resource); + for (Map.Entry<Object, Object> providerProperty : provider.entrySet()) { + newProv.put(providerProperty.getKey().toString(), providerProperty.getValue().toString()); + } + providerList.add(newProv); } } } catch (Exception e) { @@ -43,10 +48,10 @@ public class AuthenticationProviderAggregator extends ArrayList<AuthenticationPr } // Create providers - for (String prov : providerArray) { - String name = providerMap.get(prov).get("name"); - String id = providerMap.get(prov).get("id"); - String type = providerMap.get(prov).get("type"); + for (HashMap<String, String> prov : providerList) { + String name = prov.get("name"); + String id = prov.get("id"); + String type = prov.get("type"); assert !StringUtils.isBlank(name) : "You must provide a name for all authentication provider configurations"; assert !StringUtils.isBlank(id) : "You must provide an ID for all authentication provider configurations"; @@ -54,13 +59,37 @@ public class AuthenticationProviderAggregator extends ArrayList<AuthenticationPr if (configurators.containsKey(type)) { AuthenticationProviderConfigurator configurator = configurators.get(type); - addAll(configurator.getAuthenticationProviders(id, name, providerMap.get(prov))); + + addAll(configurator.getAuthenticationProviders(id, name, prov)); } } if (standaloneProviders != null) { addAll(standaloneProviders); } + + Collections.sort(this, new Comparator<AuthenticationProvider>(){ + public int compare(AuthenticationProvider o1, AuthenticationProvider o2){ + if(XnatAuthenticationProvider.class.isAssignableFrom(o1.getClass())){ + if(XnatAuthenticationProvider.class.isAssignableFrom(o2.getClass())){ + if(((XnatAuthenticationProvider)o1).getOrder() == ((XnatAuthenticationProvider)o2).getOrder()) + return 0; + return ((XnatAuthenticationProvider)o1).getOrder() < ((XnatAuthenticationProvider)o2).getOrder() ? -1 : 1; + } + else{ + return 1; + } + } + else{ + if(XnatAuthenticationProvider.class.isAssignableFrom(o2.getClass())){ + return -1; + } + else{ + return 0; + } + } + } + }); } private static final Logger _log = LoggerFactory.getLogger(AuthenticationProviderAggregator.class); diff --git a/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java b/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java index 0b306e52..f9fba0b3 100644 --- a/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java +++ b/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java @@ -11,6 +11,7 @@ package org.nrg.xnat.security.config; import org.nrg.xdat.preferences.SiteConfigPreferences; +import org.nrg.xnat.security.provider.XnatAuthenticationProvider; import org.nrg.xnat.security.provider.XnatDatabaseAuthenticationProvider; import org.nrg.xnat.security.userdetailsservices.XnatDatabaseUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; @@ -51,7 +52,15 @@ public class DatabaseAuthenticationProviderConfigurator extends AbstractAuthenti @Override public List<AuthenticationProvider> getAuthenticationProviders(String id, String name, Map<String, String> properties) { - return getAuthenticationProviders(id, name); + List<AuthenticationProvider> provs = getAuthenticationProviders(id, name); + for(AuthenticationProvider prov : provs){ + if(XnatAuthenticationProvider.class.isAssignableFrom(prov.getClass())){ + if (properties.get("order") != null) { + ((XnatAuthenticationProvider)prov).setOrder(Integer.parseInt(properties.get("order"))); + } + } + } + return provs; } private final XnatDatabaseUserDetailsService _userDetailsService; diff --git a/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java b/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java index 851f87ef..1956761f 100644 --- a/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java +++ b/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java @@ -49,6 +49,9 @@ public class LdapAuthenticationProviderConfigurator extends AbstractAuthenticati ldapAuthProvider.setUserDetailsContextMapper(new XnatLdapUserDetailsMapper(id, properties, _userAuthService, _preferences)); ldapAuthProvider.setName(name); ldapAuthProvider.setProviderId(id); + if (properties.get("order") != null) { + ldapAuthProvider.setOrder(Integer.parseInt(properties.get("order"))); + } return Arrays.asList(new AuthenticationProvider[] { ldapAuthProvider }); } catch (Exception exception) { _log.error("Something went wrong when configuring the LDAP authentication provider", exception); diff --git a/src/main/java/org/nrg/xnat/security/provider/XnatAuthenticationProvider.java b/src/main/java/org/nrg/xnat/security/provider/XnatAuthenticationProvider.java index 76535627..a23011a5 100644 --- a/src/main/java/org/nrg/xnat/security/provider/XnatAuthenticationProvider.java +++ b/src/main/java/org/nrg/xnat/security/provider/XnatAuthenticationProvider.java @@ -40,4 +40,13 @@ public interface XnatAuthenticationProvider extends AuthenticationProvider { * @return The authentication method for this provider. */ String getAuthMethod(); + + /** + * Indicates the order associated with this provider. This is used to determine the order in which the providers + * show up in the login dropdown and the order in which they are checked when a login is attempted. + * @return The order for this provider. + */ + int getOrder(); + + void setOrder(int order); } diff --git a/src/main/java/org/nrg/xnat/security/provider/XnatDatabaseAuthenticationProvider.java b/src/main/java/org/nrg/xnat/security/provider/XnatDatabaseAuthenticationProvider.java index 8dac7b24..3ad7b36a 100644 --- a/src/main/java/org/nrg/xnat/security/provider/XnatDatabaseAuthenticationProvider.java +++ b/src/main/java/org/nrg/xnat/security/provider/XnatDatabaseAuthenticationProvider.java @@ -74,6 +74,16 @@ public class XnatDatabaseAuthenticationProvider extends DaoAuthenticationProvide return XdatUserAuthService.LOCALDB; } + @Override + public int getOrder() { + return _order; + } + + @Override + public void setOrder(int order) { + _order = order; + } + @Override protected void additionalAuthenticationChecks(final UserDetails userDetails, final UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (!UserI.class.isAssignableFrom(userDetails.getClass())) { @@ -143,4 +153,5 @@ public class XnatDatabaseAuthenticationProvider extends DaoAuthenticationProvide } private final boolean _requireEmailVerification; + private int _order = -1; } diff --git a/src/main/java/org/nrg/xnat/security/provider/XnatLdapAuthenticationProvider.java b/src/main/java/org/nrg/xnat/security/provider/XnatLdapAuthenticationProvider.java index c57c49ed..20fa4dc3 100644 --- a/src/main/java/org/nrg/xnat/security/provider/XnatLdapAuthenticationProvider.java +++ b/src/main/java/org/nrg/xnat/security/provider/XnatLdapAuthenticationProvider.java @@ -94,6 +94,16 @@ public class XnatLdapAuthenticationProvider extends LdapAuthenticationProvider i return XdatUserAuthService.LDAP; } + @Override + public int getOrder() { + return _order; + } + + @Override + public void setOrder(int order) { + _order = order; + } + /** * Indicates whether the provider should be visible to and selectable by users. <b>false</b> usually indicates an * internal authentication provider, e.g. token authentication. @@ -109,4 +119,5 @@ public class XnatLdapAuthenticationProvider extends LdapAuthenticationProvider i private String _displayName = ""; private String _providerId = ""; + private int _order = -1; } -- GitLab