Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
This commit is contained in:
commit
96a47ebdcb
|
@ -27,4 +27,5 @@ module org.eclipse.jetty.jaas
|
|||
|
||||
// Only required if using JDBCLoginModule.
|
||||
requires static java.sql;
|
||||
requires org.eclipse.jetty.util;
|
||||
}
|
||||
|
|
|
@ -20,15 +20,16 @@ package org.eclipse.jetty.jaas;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
|
@ -37,9 +38,6 @@ import javax.security.auth.login.LoginException;
|
|||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import org.eclipse.jetty.jaas.callback.DefaultCallbackHandler;
|
||||
import org.eclipse.jetty.jaas.callback.ObjectCallback;
|
||||
import org.eclipse.jetty.jaas.callback.RequestParameterCallback;
|
||||
import org.eclipse.jetty.jaas.callback.ServletRequestCallback;
|
||||
import org.eclipse.jetty.security.DefaultIdentityService;
|
||||
import org.eclipse.jetty.security.IdentityService;
|
||||
import org.eclipse.jetty.security.LoginService;
|
||||
|
@ -47,7 +45,7 @@ import org.eclipse.jetty.server.Request;
|
|||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.ArrayUtil;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -58,13 +56,14 @@ import org.slf4j.LoggerFactory;
|
|||
* Implementation of jetty's LoginService that works with JAAS for
|
||||
* authorization and authentication.
|
||||
*/
|
||||
public class JAASLoginService extends AbstractLifeCycle implements LoginService
|
||||
public class JAASLoginService extends ContainerLifeCycle implements LoginService
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JAASLoginService.class);
|
||||
|
||||
public static final String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.jaas.JAASRole";
|
||||
public static final String[] DEFAULT_ROLE_CLASS_NAMES = {DEFAULT_ROLE_CLASS_NAME};
|
||||
|
||||
public static final ThreadLocal<JAASLoginService> INSTANCE = new ThreadLocal<>();
|
||||
|
||||
protected String[] _roleClassNames = DEFAULT_ROLE_CLASS_NAMES;
|
||||
protected String _callbackHandlerClass;
|
||||
protected String _realmName;
|
||||
|
@ -183,6 +182,7 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
|
|||
{
|
||||
if (_identityService == null)
|
||||
_identityService = new DefaultIdentityService();
|
||||
addBean(new PropertyUserStoreManager());
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
|
@ -193,59 +193,27 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
|
|||
{
|
||||
CallbackHandler callbackHandler = null;
|
||||
if (_callbackHandlerClass == null)
|
||||
{
|
||||
callbackHandler = new CallbackHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
|
||||
{
|
||||
for (Callback callback : callbacks)
|
||||
{
|
||||
if (callback instanceof NameCallback)
|
||||
{
|
||||
((NameCallback)callback).setName(username);
|
||||
}
|
||||
else if (callback instanceof PasswordCallback)
|
||||
{
|
||||
((PasswordCallback)callback).setPassword(credentials.toString().toCharArray());
|
||||
}
|
||||
else if (callback instanceof ObjectCallback)
|
||||
{
|
||||
((ObjectCallback)callback).setObject(credentials);
|
||||
}
|
||||
else if (callback instanceof RequestParameterCallback)
|
||||
{
|
||||
RequestParameterCallback rpc = (RequestParameterCallback)callback;
|
||||
if (request != null)
|
||||
rpc.setParameterValues(Arrays.asList(request.getParameterValues(rpc.getParameterName())));
|
||||
}
|
||||
else if (callback instanceof ServletRequestCallback)
|
||||
{
|
||||
((ServletRequestCallback)callback).setRequest(request);
|
||||
}
|
||||
else
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
callbackHandler = new DefaultCallbackHandler();
|
||||
else
|
||||
{
|
||||
Class<?> clazz = Loader.loadClass(_callbackHandlerClass);
|
||||
callbackHandler = (CallbackHandler)clazz.getDeclaredConstructor().newInstance();
|
||||
if (DefaultCallbackHandler.class.isAssignableFrom(clazz))
|
||||
{
|
||||
DefaultCallbackHandler dch = (DefaultCallbackHandler)callbackHandler;
|
||||
if (request instanceof Request)
|
||||
dch.setRequest((Request)request);
|
||||
dch.setCredential(credentials);
|
||||
dch.setUserName(username);
|
||||
}
|
||||
}
|
||||
|
||||
if (callbackHandler instanceof DefaultCallbackHandler)
|
||||
{
|
||||
DefaultCallbackHandler dch = (DefaultCallbackHandler)callbackHandler;
|
||||
if (request instanceof Request)
|
||||
dch.setRequest((Request)request);
|
||||
dch.setCredential(credentials);
|
||||
dch.setUserName(username);
|
||||
}
|
||||
|
||||
//set up the login context
|
||||
Subject subject = new Subject();
|
||||
LoginContext loginContext = (_configuration == null ? new LoginContext(_loginModuleName, subject, callbackHandler)
|
||||
INSTANCE.set(this);
|
||||
LoginContext loginContext =
|
||||
(_configuration == null ? new LoginContext(_loginModuleName, subject, callbackHandler)
|
||||
: new LoginContext(_loginModuleName, subject, callbackHandler, _configuration));
|
||||
|
||||
loginContext.login();
|
||||
|
@ -265,6 +233,10 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
|
|||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
INSTANCE.remove();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -306,52 +278,36 @@ public class JAASLoginService extends AbstractLifeCycle implements LoginService
|
|||
protected String[] getGroups(Subject subject)
|
||||
{
|
||||
Collection<String> groups = new LinkedHashSet<>();
|
||||
Set<Principal> principals = subject.getPrincipals();
|
||||
for (Principal principal : principals)
|
||||
for (Principal principal : subject.getPrincipals())
|
||||
{
|
||||
Class<?> c = principal.getClass();
|
||||
while (c != null)
|
||||
{
|
||||
if (roleClassNameMatches(c.getName()))
|
||||
{
|
||||
groups.add(principal.getName());
|
||||
break;
|
||||
}
|
||||
|
||||
boolean added = false;
|
||||
for (Class<?> ci : c.getInterfaces())
|
||||
{
|
||||
if (roleClassNameMatches(ci.getName()))
|
||||
{
|
||||
groups.add(principal.getName());
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added)
|
||||
{
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isRoleClass(principal.getClass(), Arrays.asList(getRoleClassNames())))
|
||||
groups.add(principal.getName());
|
||||
}
|
||||
|
||||
return groups.toArray(new String[groups.size()]);
|
||||
}
|
||||
|
||||
private boolean roleClassNameMatches(String classname)
|
||||
|
||||
/**
|
||||
* Check whether the class, its superclasses or any interfaces they implement
|
||||
* is one of the classes that represents a role.
|
||||
*
|
||||
* @param clazz the class to check
|
||||
* @param roleClassNames the list of classnames that represent roles
|
||||
* @return true if the class is a role class
|
||||
*/
|
||||
private static boolean isRoleClass(Class<?> clazz, List<String> roleClassNames)
|
||||
{
|
||||
boolean result = false;
|
||||
for (String roleClassName : getRoleClassNames())
|
||||
Class<?> c = clazz;
|
||||
|
||||
//add the class, its interfaces and superclasses to the list to test
|
||||
List<String> classnames = new ArrayList<>();
|
||||
while (c != null)
|
||||
{
|
||||
if (roleClassName.equals(classname))
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
classnames.add(c.getName());
|
||||
Arrays.stream(c.getInterfaces()).map(Class::getName).forEach(classnames::add);
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
|
||||
return roleClassNames.stream().anyMatch(classnames::contains);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.jaas;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.security.PropertyUserStore;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* PropertyUserStoreManager
|
||||
*
|
||||
* Maintains a map of PropertyUserStores, keyed off the location of the property file containing
|
||||
* the authentication and authorization information.
|
||||
*
|
||||
* This class is used to enable the PropertyUserStores to be cached and shared. This is essential
|
||||
* for the PropertyFileLoginModules, whose lifecycle is controlled by the JAAS api and instantiated
|
||||
* afresh whenever a user needs to be authenticated. Without this class, every PropertyFileLoginModule
|
||||
* instantiation would re-read and reload in all the user information just to authenticate a single user.
|
||||
*/
|
||||
public class PropertyUserStoreManager extends AbstractLifeCycle
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PropertyUserStoreManager.class);
|
||||
/**
|
||||
* Map of user authentication and authorization information loaded in from a property file.
|
||||
* The map is keyed off the location of the file.
|
||||
*/
|
||||
private Map<String, PropertyUserStore> _propertyUserStores;
|
||||
|
||||
public PropertyUserStore getPropertyUserStore(String file)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (_propertyUserStores == null)
|
||||
return null;
|
||||
|
||||
return _propertyUserStores.get(file);
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyUserStore addPropertyUserStore(String file, PropertyUserStore store)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
Objects.requireNonNull(_propertyUserStores);
|
||||
PropertyUserStore existing = _propertyUserStores.get(file);
|
||||
if (existing != null)
|
||||
return existing;
|
||||
|
||||
_propertyUserStores.put(file, store);
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
_propertyUserStores = new HashMap<String, PropertyUserStore>();
|
||||
super.doStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
for (Map.Entry<String,PropertyUserStore> entry: _propertyUserStores.entrySet())
|
||||
{
|
||||
try
|
||||
{
|
||||
entry.getValue().stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Error stopping PropertyUserStore at {}", entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
_propertyUserStores = null;
|
||||
super.doStop();
|
||||
}
|
||||
}
|
|
@ -26,7 +26,6 @@ import javax.security.auth.callback.PasswordCallback;
|
|||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.security.Password;
|
||||
|
||||
/**
|
||||
* DefaultCallbackHandler
|
||||
|
@ -47,39 +46,34 @@ public class DefaultCallbackHandler extends AbstractCallbackHandler
|
|||
public void handle(Callback[] callbacks)
|
||||
throws IOException, UnsupportedCallbackException
|
||||
{
|
||||
for (int i = 0; i < callbacks.length; i++)
|
||||
for (Callback callback : callbacks)
|
||||
{
|
||||
if (callbacks[i] instanceof NameCallback)
|
||||
if (callback instanceof NameCallback)
|
||||
{
|
||||
((NameCallback)callbacks[i]).setName(getUserName());
|
||||
((NameCallback)callback).setName(getUserName());
|
||||
}
|
||||
else if (callbacks[i] instanceof ObjectCallback)
|
||||
else if (callback instanceof ObjectCallback)
|
||||
{
|
||||
((ObjectCallback)callbacks[i]).setObject(getCredential());
|
||||
((ObjectCallback)callback).setObject(getCredential());
|
||||
}
|
||||
else if (callbacks[i] instanceof PasswordCallback)
|
||||
else if (callback instanceof PasswordCallback)
|
||||
{
|
||||
if (getCredential() instanceof Password)
|
||||
((PasswordCallback)callbacks[i]).setPassword(((Password)getCredential()).toString().toCharArray());
|
||||
else if (getCredential() instanceof String)
|
||||
((PasswordCallback)callback).setPassword(getCredential().toString().toCharArray());
|
||||
}
|
||||
else if (callback instanceof RequestParameterCallback)
|
||||
{
|
||||
if (_request != null)
|
||||
{
|
||||
((PasswordCallback)callbacks[i]).setPassword(((String)getCredential()).toCharArray());
|
||||
RequestParameterCallback rpc = (RequestParameterCallback)callback;
|
||||
rpc.setParameterValues(Arrays.asList(_request.getParameterValues(rpc.getParameterName())));
|
||||
}
|
||||
else
|
||||
throw new UnsupportedCallbackException(callbacks[i], "User supplied credentials cannot be converted to char[] for PasswordCallback: try using an ObjectCallback instead");
|
||||
}
|
||||
else if (callbacks[i] instanceof RequestParameterCallback)
|
||||
else if (callback instanceof ServletRequestCallback)
|
||||
{
|
||||
RequestParameterCallback callback = (RequestParameterCallback)callbacks[i];
|
||||
callback.setParameterValues(Arrays.asList(_request.getParameterValues(callback.getParameterName())));
|
||||
}
|
||||
else if (callbacks[i] instanceof ServletRequestCallback)
|
||||
{
|
||||
((ServletRequestCallback)callbacks[i]).setRequest(_request);
|
||||
((ServletRequestCallback)callback).setRequest(_request);
|
||||
}
|
||||
else
|
||||
throw new UnsupportedCallbackException(callbacks[i]);
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,12 @@ package org.eclipse.jetty.jaas.spi;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
||||
import org.eclipse.jetty.jaas.JAASLoginService;
|
||||
import org.eclipse.jetty.jaas.PropertyUserStoreManager;
|
||||
import org.eclipse.jetty.security.AbstractLoginService;
|
||||
import org.eclipse.jetty.security.PropertyUserStore;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
|
@ -39,16 +40,13 @@ import org.slf4j.LoggerFactory;
|
|||
public class PropertyFileLoginModule extends AbstractLoginModule
|
||||
{
|
||||
public static final String DEFAULT_FILENAME = "realm.properties";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PropertyFileLoginModule.class);
|
||||
|
||||
private static ConcurrentHashMap<String, PropertyUserStore> _propertyUserStores = new ConcurrentHashMap<String, PropertyUserStore>();
|
||||
|
||||
private int _refreshInterval = 0;
|
||||
private String _filename = DEFAULT_FILENAME;
|
||||
private PropertyUserStore _store;
|
||||
|
||||
/**
|
||||
* Read contents of the configured property file.
|
||||
* Use a PropertyUserStore to read the authentication and authorizaton information contained in
|
||||
* the file named by the option "file".
|
||||
*
|
||||
* @param subject the subject
|
||||
* @param callbackHandler the callback handler
|
||||
|
@ -64,40 +62,61 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
setupPropertyUserStore(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing, or create a new PropertyUserStore to read the
|
||||
* authentication and authorization information from the file named by
|
||||
* the option "file".
|
||||
*
|
||||
* @param options configuration options
|
||||
*/
|
||||
private void setupPropertyUserStore(Map<String, ?> options)
|
||||
{
|
||||
parseConfig(options);
|
||||
String filename = (String)options.get("file");
|
||||
filename = (filename == null ? DEFAULT_FILENAME : filename);
|
||||
|
||||
if (_propertyUserStores.get(_filename) == null)
|
||||
PropertyUserStoreManager mgr = JAASLoginService.INSTANCE.get().getBean(PropertyUserStoreManager.class);
|
||||
if (mgr == null)
|
||||
throw new IllegalStateException("No PropertyUserStoreManager");
|
||||
|
||||
_store = mgr.getPropertyUserStore(filename);
|
||||
if (_store == null)
|
||||
{
|
||||
PropertyUserStore propertyUserStore = new PropertyUserStore();
|
||||
propertyUserStore.setConfig(_filename);
|
||||
|
||||
PropertyUserStore prev = _propertyUserStores.putIfAbsent(_filename, propertyUserStore);
|
||||
if (prev == null)
|
||||
boolean hotReload = false;
|
||||
String tmp = (String)options.get("hotReload");
|
||||
if (tmp != null)
|
||||
hotReload = Boolean.parseBoolean(tmp);
|
||||
else
|
||||
{
|
||||
LOG.debug("setupPropertyUserStore: Starting new PropertyUserStore. PropertiesFile: {} refreshInterval: {}", _filename, _refreshInterval);
|
||||
|
||||
try
|
||||
//refreshInterval is deprecated, use hotReload instead
|
||||
tmp = (String)options.get("refreshInterval");
|
||||
if (tmp != null)
|
||||
{
|
||||
propertyUserStore.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Exception while starting propertyUserStore: ", e);
|
||||
LOG.warn("Use 'hotReload' boolean property instead of 'refreshInterval'");
|
||||
try
|
||||
{
|
||||
hotReload = (Integer.parseInt(tmp) > 0);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
LOG.warn("'refreshInterval' is not an integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
PropertyUserStore newStore = new PropertyUserStore();
|
||||
newStore.setConfig(filename);
|
||||
newStore.setHotReload(hotReload);
|
||||
_store = mgr.addPropertyUserStore(filename, newStore);
|
||||
try
|
||||
{
|
||||
_store.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Exception starting propertyUserStore {} ", filename, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseConfig(Map<String, ?> options)
|
||||
{
|
||||
String tmp = (String)options.get("file");
|
||||
_filename = (tmp == null ? DEFAULT_FILENAME : tmp);
|
||||
tmp = (String)options.get("refreshInterval");
|
||||
_refreshInterval = (tmp == null ? _refreshInterval : Integer.parseInt(tmp));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userName the user name
|
||||
* @throws Exception if unable to get the user information
|
||||
|
@ -105,13 +124,8 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
@Override
|
||||
public UserInfo getUserInfo(String userName) throws Exception
|
||||
{
|
||||
PropertyUserStore propertyUserStore = _propertyUserStores.get(_filename);
|
||||
if (propertyUserStore == null)
|
||||
throw new IllegalStateException("PropertyUserStore should never be null here!");
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Checking PropertyUserStore {} for {}", _filename, userName);
|
||||
UserIdentity userIdentity = propertyUserStore.getUserIdentity(userName);
|
||||
LOG.debug("Checking PropertyUserStore {} for {}", _store.getConfig(), userName);
|
||||
UserIdentity userIdentity = _store.getUserIdentity(userName);
|
||||
if (userIdentity == null)
|
||||
return null;
|
||||
|
||||
|
@ -124,8 +138,6 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
.collect(Collectors.toList());
|
||||
|
||||
Credential credential = (Credential)userIdentity.getSubject().getPrivateCredentials().iterator().next();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Found: {} in PropertyUserStore {}", userName, _filename);
|
||||
return new UserInfo(userName, credential, roles);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.jaas;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
|
@ -29,25 +30,17 @@ import org.eclipse.jetty.security.DefaultIdentityService;
|
|||
import org.eclipse.jetty.server.Request;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* JAASLoginServiceTest
|
||||
*/
|
||||
public class JAASLoginServiceTest
|
||||
{
|
||||
public static class TestConfiguration extends Configuration
|
||||
{
|
||||
AppConfigurationEntry _entry = new AppConfigurationEntry(TestLoginModule.class.getCanonicalName(), LoginModuleControlFlag.REQUIRED, Collections.emptyMap());
|
||||
|
||||
@Override
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name)
|
||||
{
|
||||
return new AppConfigurationEntry[]{_entry};
|
||||
}
|
||||
}
|
||||
|
||||
interface SomeRole
|
||||
{
|
||||
|
||||
|
@ -94,18 +87,31 @@ public class JAASLoginServiceTest
|
|||
@Test
|
||||
public void testServletRequestCallback() throws Exception
|
||||
{
|
||||
Configuration config = new Configuration()
|
||||
{
|
||||
@Override
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name)
|
||||
{
|
||||
return new AppConfigurationEntry[] {
|
||||
new AppConfigurationEntry(TestLoginModule.class.getCanonicalName(),
|
||||
LoginModuleControlFlag.REQUIRED,
|
||||
Collections.emptyMap())
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
//Test with the DefaultCallbackHandler
|
||||
JAASLoginService ls = new JAASLoginService("foo");
|
||||
ls.setCallbackHandlerClass("org.eclipse.jetty.jaas.callback.DefaultCallbackHandler");
|
||||
ls.setIdentityService(new DefaultIdentityService());
|
||||
ls.setConfiguration(new TestConfiguration());
|
||||
ls.setConfiguration(config);
|
||||
Request request = new Request(null, null);
|
||||
ls.login("aaardvaark", "aaa", request);
|
||||
|
||||
//Test with the fallback CallbackHandler
|
||||
ls = new JAASLoginService("foo");
|
||||
ls.setIdentityService(new DefaultIdentityService());
|
||||
ls.setConfiguration(new TestConfiguration());
|
||||
ls.setConfiguration(config);
|
||||
ls.login("aaardvaark", "aaa", request);
|
||||
}
|
||||
|
||||
|
@ -137,12 +143,8 @@ public class JAASLoginServiceTest
|
|||
subject.getPrincipals().add(new AnotherTestRole("z"));
|
||||
|
||||
String[] groups = ls.getGroups(subject);
|
||||
assertEquals(3, groups.length);
|
||||
for (String g : groups)
|
||||
{
|
||||
assertTrue(g.equals("x") || g.equals("y") || g.equals("z"));
|
||||
}
|
||||
|
||||
assertThat(Arrays.asList(groups), containsInAnyOrder("x", "y", "z"));
|
||||
|
||||
//test a custom role class
|
||||
ls.setRoleClassNames(new String[]{AnotherTestRole.class.getName()});
|
||||
Subject subject2 = new Subject();
|
||||
|
@ -150,8 +152,9 @@ public class JAASLoginServiceTest
|
|||
subject2.getPrincipals().add(new TestRole("x"));
|
||||
subject2.getPrincipals().add(new TestRole("y"));
|
||||
subject2.getPrincipals().add(new AnotherTestRole("z"));
|
||||
assertEquals(1, ls.getGroups(subject2).length);
|
||||
assertEquals("z", ls.getGroups(subject2)[0]);
|
||||
String[] s2groups = ls.getGroups(subject2);
|
||||
assertThat(s2groups, is(notNullValue()));
|
||||
assertThat(Arrays.asList(s2groups), containsInAnyOrder("z"));
|
||||
|
||||
//test a custom role class that implements an interface
|
||||
ls.setRoleClassNames(new String[]{SomeRole.class.getName()});
|
||||
|
@ -160,11 +163,9 @@ public class JAASLoginServiceTest
|
|||
subject3.getPrincipals().add(new TestRole("x"));
|
||||
subject3.getPrincipals().add(new TestRole("y"));
|
||||
subject3.getPrincipals().add(new AnotherTestRole("z"));
|
||||
assertEquals(3, ls.getGroups(subject3).length);
|
||||
for (String g : groups)
|
||||
{
|
||||
assertTrue(g.equals("x") || g.equals("y") || g.equals("z"));
|
||||
}
|
||||
String[] s3groups = ls.getGroups(subject3);
|
||||
assertThat(s3groups, is(notNullValue()));
|
||||
assertThat(Arrays.asList(s3groups), containsInAnyOrder("x", "y", "z"));
|
||||
|
||||
//test a class that doesn't match
|
||||
ls.setRoleClassNames(new String[]{NotTestRole.class.getName()});
|
||||
|
|
|
@ -19,34 +19,72 @@
|
|||
package org.eclipse.jetty.jaas.spi;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import javax.security.auth.Subject;
|
||||
import java.util.Collections;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
|
||||
import javax.security.auth.login.Configuration;
|
||||
|
||||
import org.eclipse.jetty.jaas.callback.DefaultCallbackHandler;
|
||||
import org.eclipse.jetty.jaas.JAASLoginService;
|
||||
import org.eclipse.jetty.jaas.PropertyUserStoreManager;
|
||||
import org.eclipse.jetty.security.DefaultIdentityService;
|
||||
import org.eclipse.jetty.security.PropertyUserStore;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class PropertyFileLoginModuleTest
|
||||
{
|
||||
@Test
|
||||
public void testRoles()
|
||||
throws Exception
|
||||
public void testPropertyFileLoginModule() throws Exception
|
||||
{
|
||||
File file = MavenTestingUtils.getTestResourceFile("login.properties");
|
||||
PropertyFileLoginModule module = new PropertyFileLoginModule();
|
||||
Subject subject = new Subject();
|
||||
HashMap<String, String> options = new HashMap<>();
|
||||
options.put("file", file.getCanonicalPath());
|
||||
module.initialize(subject, new DefaultCallbackHandler(), new HashMap<String, String>(), options);
|
||||
UserInfo fred = module.getUserInfo("fred");
|
||||
assertEquals("fred", fred.getUserName());
|
||||
assertThat(fred.getRoleNames(), containsInAnyOrder("role1", "role2", "role3"));
|
||||
assertThat(fred.getRoleNames(), not(contains("fred")));
|
||||
//configure for PropertyFileLoginModule
|
||||
File loginProperties = MavenTestingUtils.getTestResourceFile("login.properties");
|
||||
|
||||
Configuration testConfig = new Configuration()
|
||||
{
|
||||
@Override
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name)
|
||||
{
|
||||
return new AppConfigurationEntry[]{new AppConfigurationEntry(PropertyFileLoginModule.class.getName(),
|
||||
LoginModuleControlFlag.REQUIRED,
|
||||
Collections.singletonMap("file", loginProperties.getAbsolutePath()))};
|
||||
}
|
||||
};
|
||||
|
||||
JAASLoginService ls = new JAASLoginService("foo");
|
||||
ls.setCallbackHandlerClass("org.eclipse.jetty.jaas.callback.DefaultCallbackHandler");
|
||||
ls.setIdentityService(new DefaultIdentityService());
|
||||
ls.setConfiguration(testConfig);
|
||||
ls.start();
|
||||
|
||||
//test that the manager is created when the JAASLoginService starts
|
||||
PropertyUserStoreManager mgr = ls.getBean(PropertyUserStoreManager.class);
|
||||
assertThat(mgr, notNullValue());
|
||||
|
||||
//test the PropertyFileLoginModule authentication and authorization
|
||||
Request request = new Request(null, null);
|
||||
UserIdentity uid = ls.login("fred", "pwd", request);
|
||||
assertThat(uid.isUserInRole("role1", null), is(true));
|
||||
assertThat(uid.isUserInRole("role2", null), is(true));
|
||||
assertThat(uid.isUserInRole("role3", null), is(true));
|
||||
assertThat(uid.isUserInRole("role4", null), is(false));
|
||||
|
||||
//Test that the PropertyUserStore is created by the PropertyFileLoginModule
|
||||
PropertyUserStore store = mgr.getPropertyUserStore(loginProperties.getAbsolutePath());
|
||||
assertThat(store, is(notNullValue()));
|
||||
assertThat(store.isRunning(), is(true));
|
||||
assertThat(store.isHotReload(), is(false));
|
||||
|
||||
//test that the PropertyUserStoreManager is stopped and all PropertyUserStores stopped
|
||||
ls.stop();
|
||||
assertThat(mgr.isStopped(), is(true));
|
||||
assertThat(mgr.getPropertyUserStore(loginProperties.getAbsolutePath()), is(nullValue()));
|
||||
assertThat(store.isStopped(), is(true));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue