@@ -33,6 +33,8 @@
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.naming.NamingException;
import javax.security.auth.Subject;
@@ -42,13 +44,10 @@
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.as.domain.management.connections.ldap.LdapConnectionManager;
import org.jboss.as.domain.management.security.BaseLdapGroupSearchResource.GroupName;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
@@ -63,12 +62,14 @@
*
* @author <a href="mailto:flemming.harms@gmail.com">Flemming Harms</a>
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class LdapSubjectSupplementalService implements Service<SubjectSupplementalService>, SubjectSupplementalService {
public class LdapSubjectSupplementalService implements Service, SubjectSupplementalService {

private final InjectedValue<LdapConnectionManager> connectionManager = new InjectedValue<LdapConnectionManager>();
private final InjectedValue<LdapSearcherCache<LdapEntry, String>> userSearcherInjector = new InjectedValue<LdapSearcherCache<LdapEntry, String>>();
private final InjectedValue<LdapSearcherCache<LdapEntry[], LdapEntry>> groupSearcherInjector = new InjectedValue<LdapSearcherCache<LdapEntry[], LdapEntry>>();
private final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer;
private final Supplier<LdapConnectionManager> connectionManagerSupplier;
private final Supplier<LdapSearcherCache<LdapEntry, String>> userSearcherSupplier;
private final Supplier<LdapSearcherCache<LdapEntry[], LdapEntry>> groupSearcherSupplier;

private LdapSearcherCache<LdapEntry, String> userSearcher;
private LdapSearcherCache<LdapEntry[], LdapEntry> groupSearcher;
@@ -81,25 +82,26 @@
private final boolean iterative;
private final GroupName groupName;

public LdapSubjectSupplementalService(final String realmName, final boolean shareConnection, final boolean forceUserDnSearch, final boolean iterative, final GroupName groupName) {
LdapSubjectSupplementalService(final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer,
final Supplier<LdapConnectionManager> connectionManagerSupplier,
final Supplier<LdapSearcherCache<LdapEntry, String>> userSearcherSupplier,
final Supplier<LdapSearcherCache<LdapEntry[], LdapEntry>> groupSearcherSupplier,
final String realmName, final boolean shareConnection, final boolean forceUserDnSearch,
final boolean iterative, final GroupName groupName) {
this.subjectSupplementalServiceConsumer = subjectSupplementalServiceConsumer;
this.connectionManagerSupplier = connectionManagerSupplier;
this.userSearcherSupplier = userSearcherSupplier;
this.groupSearcherSupplier = groupSearcherSupplier;
this.realmName = realmName;
this.shareConnection = shareConnection;
this.forceUserDnSearch = forceUserDnSearch;
this.iterative = iterative;
this.groupName = groupName;
}

/*
* Service Methods
*/

public SubjectSupplementalService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

public void start(StartContext context) throws StartException {
userSearcher = userSearcherInjector.getOptionalValue();
groupSearcher = groupSearcherInjector.getValue();
public void start(final StartContext context) {
userSearcher = userSearcherSupplier != null ? userSearcherSupplier.get() : null;
groupSearcher = groupSearcherSupplier.get();

if (SECURITY_LOGGER.isTraceEnabled()) {
SECURITY_LOGGER.tracef("LdapSubjectSupplementalService realmName=%s", realmName);
@@ -108,28 +110,15 @@ public void start(StartContext context) throws StartException {
SECURITY_LOGGER.tracef("LdapSubjectSupplementalService iterative=%b", iterative);
SECURITY_LOGGER.tracef("LdapSubjectSupplementalService groupName=%s", groupName);
}
subjectSupplementalServiceConsumer.accept(this);
}

public void stop(StopContext context) {
public void stop(final StopContext context) {
subjectSupplementalServiceConsumer.accept(null);
groupSearcher = null;
userSearcher = null;
}

/*
* Access to Injectors
*/
public Injector<LdapConnectionManager> getConnectionManagerInjector() {
return connectionManager;
}

public Injector<LdapSearcherCache<LdapEntry, String>> getLdapUserSearcherInjector() {
return userSearcherInjector;
}

public Injector<LdapSearcherCache<LdapEntry[], LdapEntry>> getLdapGroupSearcherInjector() {
return groupSearcherInjector;
}

/*
* SubjectSupplementalService Method
*/
@@ -199,7 +188,7 @@ protected LdapGroupSearcher(final Map<String, Object> sharedState) {
connectionHandler = (LdapConnectionHandler) sharedState.remove(LdapConnectionHandler.class.getName());
} else {
SECURITY_LOGGER.trace("Creating new LdapConnectionHandler.");
connectionHandler = LdapConnectionHandler.newInstance(connectionManager.getValue());
connectionHandler = LdapConnectionHandler.newInstance(connectionManagerSupplier.get());
}
try {
// In general we expect exactly one RealmUser, however we could cope with multiple
@@ -33,6 +33,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -43,10 +44,9 @@
import org.jboss.as.domain.management.AuthMechanism;
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.server.RealmIdentity;
@@ -58,18 +58,22 @@
* The Service providing the LocalCallbackHandler implementation.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
class LocalCallbackHandlerService implements Service<CallbackHandlerService>, CallbackHandlerService {
class LocalCallbackHandlerService implements Service, CallbackHandlerService {

private static final String SERVICE_SUFFIX = "local";

private final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer;
private final String defaultUser;
private final String allowedUsers;
private boolean allowAll;
private final Set<String> allowedUsersSet = new HashSet<String>();
private final boolean skipGroupLoading;

LocalCallbackHandlerService(final String defaultUser, final String allowedUsers, final boolean skipGroupLoading) {
LocalCallbackHandlerService(final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer,
final String defaultUser, final String allowedUsers, final boolean skipGroupLoading) {
this.callbackHandlerServiceConsumer = callbackHandlerServiceConsumer;
this.defaultUser = defaultUser;
this.allowedUsers = allowedUsers;
this.skipGroupLoading = skipGroupLoading;
@@ -192,15 +196,7 @@ public boolean exists() throws RealmUnavailableException {

}

/*
* Service Methods
*/

public CallbackHandlerService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

public void start(StartContext context) throws StartException {
public void start(final StartContext context) {
if (defaultUser != null) {
allowedUsersSet.add(defaultUser);
}
@@ -214,9 +210,11 @@ public void start(StartContext context) throws StartException {
}
}
}
callbackHandlerServiceConsumer.accept(this);
}

public void stop(StopContext context) {
public void stop(final StopContext context) {
callbackHandlerServiceConsumer.accept(null);
allowAll = false;
allowedUsersSet.clear(); // Effectively disables this CBH
}
@@ -41,6 +41,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -60,8 +62,10 @@
import org.jboss.as.domain.management.plugin.PasswordCredential;
import org.jboss.as.domain.management.plugin.PlugInConfigurationSupport;
import org.jboss.as.domain.management.plugin.ValidatePasswordCredential;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StopContext;
import org.wildfly.common.Assert;
import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.security.auth.SupportLevel;
@@ -83,31 +87,36 @@
* CallbackHandlerService to integrate the plug-ins.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PlugInAuthenticationCallbackHandler extends AbstractPlugInService implements Service<CallbackHandlerService>,
public class PlugInAuthenticationCallbackHandler extends AbstractPlugInService implements Service,
CallbackHandlerService {

private static final String SERVICE_SUFFIX = "plug-in-authentication";

private static UsernamePasswordHashUtil hashUtil = null;

private final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer;
private final AuthMechanism mechanism;

PlugInAuthenticationCallbackHandler(final String realmName,
PlugInAuthenticationCallbackHandler(final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer,
final Supplier<PlugInLoaderService> plugInLoaderSupplier,
final String realmName,
final String pluginName,
final Map<String, String> properties,
final AuthMechanism mechanism) {
super(realmName, pluginName, properties);
super(plugInLoaderSupplier, realmName, pluginName, properties);
this.callbackHandlerServiceConsumer = callbackHandlerServiceConsumer;
this.mechanism = mechanism;
}

/*
* Service Methods
*/
@Override
public void start(final StartContext context) {
callbackHandlerServiceConsumer.accept(this);
}

@Override
public CallbackHandlerService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
public void stop(final StopContext context) {
callbackHandlerServiceConsumer.accept(null);
}

private static UsernamePasswordHashUtil getHashUtil() {
@@ -26,6 +26,8 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.as.domain.management.SecurityRealm;
@@ -36,13 +38,11 @@
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;

/**
* Service responsible for loading plug-ins as needed.
@@ -51,34 +51,35 @@
* many plug-ins but will never load more than 2.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PlugInLoaderService implements Service<PlugInLoaderService> {
public class PlugInLoaderService implements Service {

private static final String SERVICE_SUFFIX = "plug-in-loader";

private final Consumer<PlugInLoaderService> plugInLoaderServiceConsumer;
private final List<String> plugInNames;
private final Map<String, List<PlugInProvider>> cachedProviders = new HashMap<String, List<PlugInProvider>>();
private final Map<String, PlugInProvider> authenticationProviders = new HashMap<String, PlugInProvider>();
private final Map<String, PlugInProvider> authorizationProviders = new HashMap<String, PlugInProvider>();

public PlugInLoaderService(final List<String> plugInNames) {
PlugInLoaderService(final Consumer<PlugInLoaderService> plugInLoaderServiceConsumer, final List<String> plugInNames) {
this.plugInLoaderServiceConsumer = plugInLoaderServiceConsumer;
this.plugInNames = plugInNames;
}

public void start(StartContext context) throws StartException {
public void start(final StartContext context) {
plugInLoaderServiceConsumer.accept(this);
}

public void stop(StopContext context) {
public void stop(final StopContext context) {
plugInLoaderServiceConsumer.accept(null);
// Clear any cached data so it can be reloaded on next start.
cachedProviders.clear();
authenticationProviders.clear();
authorizationProviders.clear();
}

public PlugInLoaderService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

private List<PlugInProvider> loadPlugInProvider(final String name) {
synchronized (cachedProviders) {
List<PlugInProvider> response;
@@ -176,10 +177,9 @@ public static ServiceName createServiceName(final String realmName) {
return SecurityRealm.ServiceUtil.createServiceName(realmName).append(SERVICE_SUFFIX);
}

public static ServiceBuilder<?> addDependency(ServiceBuilder<?> sb, InjectedValue<PlugInLoaderService> injector, String realmName) {
return sb.addDependency(createServiceName(realmName), PlugInLoaderService.class, injector);
public static Supplier<PlugInLoaderService> requires(final ServiceBuilder<?> sb, final String realmName) {
return sb.requires(createServiceName(realmName));
}

}

}
@@ -34,6 +34,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.security.auth.Subject;

@@ -43,8 +45,10 @@
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.as.domain.management.plugin.AuthorizationPlugIn;
import org.jboss.as.domain.management.plugin.PlugInConfigurationSupport;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StopContext;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
@@ -57,27 +61,33 @@
* The {@link SubjectSupplementalService} for Plug-Ins
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PlugInSubjectSupplemental extends AbstractPlugInService implements Service<SubjectSupplementalService>,
public class PlugInSubjectSupplemental extends AbstractPlugInService implements Service,
SubjectSupplementalService {

private static final String SERVICE_SUFFIX = "plug-in-authorization";

PlugInSubjectSupplemental(final String realmName, final String name, final Map<String, String> properties) {
super(realmName, name, properties);
}
private final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer;

/*
* Service Methods
*/
PlugInSubjectSupplemental(final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer,
final Supplier<PlugInLoaderService> plugInLoaderSupplier,
final String realmName, final String name, final Map<String, String> properties) {
super(plugInLoaderSupplier, realmName, name, properties);
this.subjectSupplementalServiceConsumer = subjectSupplementalServiceConsumer;
}

// The start/stop methods in the base class are sufficient.
@Override
public void start(final StartContext context) {
subjectSupplementalServiceConsumer.accept(this);
}

@Override
public SubjectSupplementalService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
public void stop(final StopContext context) {
subjectSupplementalServiceConsumer.accept(null);
}


@Override
public org.wildfly.security.auth.server.SecurityRealm getElytronSecurityRealm() {
return new SecurityRealmImpl();
@@ -44,6 +44,8 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -53,13 +55,15 @@
import javax.security.sasl.AuthorizeCallback;
import javax.security.sasl.RealmCallback;

import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.domain.management.AuthMechanism;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.wildfly.common.Assert;
import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.security.auth.SupportLevel;
@@ -86,19 +90,24 @@
* A CallbackHandler obtaining the users and their passwords from a properties file.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PropertiesCallbackHandler extends UserPropertiesFileLoader implements Service<CallbackHandlerService>,
public class PropertiesCallbackHandler extends UserPropertiesFileLoader implements Service,
CallbackHandlerService, CallbackHandler {

private static final String SERVICE_SUFFIX = "properties_authentication";

private static UsernamePasswordHashUtil hashUtil = null;

private final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer;
private final String realm;
private final boolean plainText;

public PropertiesCallbackHandler(String realm, String path, String relativeTo, boolean plainText) {
super(path, relativeTo);
PropertiesCallbackHandler(final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer,
final Supplier<PathManager> pathManagerSupplier,
final String realm, final String path, final String relativeTo, final boolean plainText) {
super(pathManagerSupplier, path, relativeTo);
this.callbackHandlerServiceConsumer = callbackHandlerServiceConsumer;
this.realm = realm;
this.plainText = plainText;
}
@@ -157,7 +166,7 @@ protected void verifyProperties(Properties properties) throws IOException {
}

@Override
public void start(StartContext context) throws StartException {
public void start(final StartContext context) throws StartException {
super.start(context);
try {
String fileRealm = getRealmName();
@@ -167,10 +176,13 @@ public void start(StartContext context) throws StartException {
} catch (IOException e) {
throw DomainManagementLogger.ROOT_LOGGER.unableToLoadProperties(e);
}
callbackHandlerServiceConsumer.accept(this);
}

public CallbackHandlerService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
@Override
public void stop(final StopContext context) {
callbackHandlerServiceConsumer.accept(null);
super.stop(context);
}

/*
@@ -38,6 +38,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@@ -47,17 +48,16 @@
import org.jboss.as.controller.services.path.PathManager.Event;
import org.jboss.as.controller.services.path.PathManager.PathEventContext;
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;

/**
* The base class for services depending on loading a properties file, loads the properties on
* start up and re-loads as required where updates to the file are detected.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PropertiesFileLoader {

@@ -103,7 +103,7 @@
public static final Pattern PROPERTY_PATTERN = Pattern.compile("#?+((?:[,.\\-@/a-zA-Z0-9]++|(?:\\\\[=\\\\])++)++)=(.*+)");
public static final String DISABLE_SUFFIX_KEY = "!disable";

private final InjectedValue<PathManager> pathManager = new InjectedValue<PathManager>();
private final Supplier<PathManager> pathManagerSupplier;

private final String path;
private final String relativeTo;
@@ -120,19 +120,20 @@
* End of state maintained during persistence.
*/

public PropertiesFileLoader(final String path, final String relativeTo) {
public PropertiesFileLoader(final Supplier<PathManager> pathManagerSupplier, final String path, final String relativeTo) {
this.pathManagerSupplier = pathManagerSupplier;
this.path = path;
this.relativeTo = relativeTo;
}

public Injector<PathManager> getPathManagerInjectorInjector() {
return pathManager;
public PropertiesFileLoader(final String path) {
this(null, path, null);
}

public void start(StartContext context) throws StartException {
String file = path;
if (relativeTo != null) {
PathManager pm = pathManager.getValue();
PathManager pm = pathManagerSupplier.get();

file = pm.resolveRelativePathEntry(file, relativeTo);
pm.registerCallback(relativeTo, new Callback() {
@@ -33,13 +33,16 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.security.auth.Subject;

import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.core.security.RealmGroup;
import org.jboss.as.core.security.RealmUser;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
@@ -53,35 +56,33 @@
import org.wildfly.security.evidence.Evidence;

/**
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class PropertiesSubjectSupplemental extends PropertiesFileLoader implements Service<SubjectSupplementalService>, SubjectSupplementalService,
public class PropertiesSubjectSupplemental extends PropertiesFileLoader implements Service, SubjectSupplementalService,
SubjectSupplemental {

private static final String SERVICE_SUFFIX = "properties_authorization";
private static final String COMMA = ",";

private final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer;
private final String realmName;

public PropertiesSubjectSupplemental(final String realmName, final String path, final String relativeTo) {
super(path, relativeTo);
PropertiesSubjectSupplemental(final Consumer<SubjectSupplementalService> subjectSupplementalServiceConsumer, final Supplier<PathManager> pathManagerSupplier, final String realmName, final String path, final String relativeTo) {
super(pathManagerSupplier, path, relativeTo);
this.subjectSupplementalServiceConsumer = subjectSupplementalServiceConsumer;
this.realmName = realmName;
}

/*
* Service Methods
*/

public SubjectSupplementalService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

public void start(StartContext context) throws StartException {
@Override
public void start(final StartContext context) throws StartException {
super.start(context);
subjectSupplementalServiceConsumer.accept(this);
}

public void stop(StopContext context) {
@Override
public void stop(final StopContext context) {
subjectSupplementalServiceConsumer.accept(null);
super.stop(context);
}

@@ -26,17 +26,21 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;

import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.EmptyProvider;
import org.wildfly.security.credential.source.CredentialSource;

/**
* Extension of {@link AbstractKeyManagerService} to load the KeyStore using a specified provider.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
class ProviderKeyManagerService extends AbstractKeyManagerService {

@@ -45,15 +49,17 @@

private volatile KeyStore theKeyStore;

ProviderKeyManagerService(final String provider, final char[] storePassword) {
super(null, null);
ProviderKeyManagerService(final Consumer<AbstractKeyManagerService> keyManagerServiceConsumer,
final ExceptionSupplier<CredentialSource, Exception> keyCredentialSourceSupplier,
final ExceptionSupplier<CredentialSource, Exception> keystoreCredentialSourceSupplier,
final String provider, final char[] storePassword) {
super(keyManagerServiceConsumer, keyCredentialSourceSupplier, keystoreCredentialSourceSupplier, null, null);
this.provider = provider;
this.storePassword = storePassword;
}

@Override
public void start(StartContext context) throws StartException {

try {
KeyStore theKeyStore = KeyStore.getInstance(provider);
synchronized (EmptyProvider.getInstance()) {
@@ -21,32 +21,39 @@
*/
package org.jboss.as.domain.management.security;


import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.function.Consumer;

import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.EmptyProvider;
import org.wildfly.security.credential.source.CredentialSource;

import javax.net.ssl.TrustManager;

/**
* Extension of {@link AbstractTrustManagerService} to load the KeyStore using a specified provider.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class ProviderTrustManagerService extends AbstractTrustManagerService {

private volatile String provider;

private volatile KeyStore theKeyStore;

ProviderTrustManagerService(final String provider, final char[] storePassword) {
super(storePassword);
ProviderTrustManagerService(final Consumer<TrustManager[]> trustManagersConsumer,
final ExceptionSupplier<CredentialSource, Exception> credentialSourceSupplier,
final String provider, final char[] storePassword) {
super(trustManagersConsumer, credentialSourceSupplier, storePassword);
this.provider = provider;
}

@@ -32,6 +32,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
@@ -44,31 +46,37 @@
import javax.net.ssl.TrustManager;

import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;

/**
* Service to handle the creation of a single SSLContext based on the injected key and trust managers.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class SSLContextService implements Service<SSLContext> {
public class SSLContextService implements Service {

private final InjectedValue<AbstractKeyManagerService> injectedKeyManagers = new InjectedValue<>();
private final InjectedValue<TrustManager[]> injectedtrustManagers = new InjectedValue<TrustManager[]>();
private final Consumer<SSLContext> sslContextConsumer;
private final Supplier<AbstractKeyManagerService> keyManagersSupplier;
private final Supplier<TrustManager[]> trustManagersSupplier;

private volatile String protocol;
private volatile Set<String> enabledCipherSuites;
private volatile Set<String> enabledProtocols;
private volatile SSLContext theSSLContext;

SSLContextService(final String protocol, final Set<String> enabledCipherSuites, final Set<String> enabledProtocols) {
SSLContextService(final Consumer<SSLContext> sslContextConsumer,
final Supplier<AbstractKeyManagerService> keyManagersSupplier,
final Supplier<TrustManager[]> trustManagersSupplier,
final String protocol, final Set<String> enabledCipherSuites, final Set<String> enabledProtocols) {
this.sslContextConsumer = sslContextConsumer;
this.keyManagersSupplier = keyManagersSupplier;
this.trustManagersSupplier = trustManagersSupplier;
this.protocol = protocol;
this.enabledCipherSuites = enabledCipherSuites;
this.enabledProtocols = enabledProtocols;
@@ -90,20 +98,20 @@ public void setProtocol(String protocol) {

@Override
public void start(final StartContext context) throws StartException {
AbstractKeyManagerService keyManagers = injectedKeyManagers.getOptionalValue();
TrustManager[] trustManagers = injectedtrustManagers.getOptionalValue();
AbstractKeyManagerService keyManagers = keyManagersSupplier != null ? keyManagersSupplier.get() : null;
TrustManager[] trustManagers = trustManagersSupplier != null ? trustManagersSupplier.get() : null;

try {
SSLContext sslContext = SSLContext.getInstance(protocol);
if(keyManagers != null && keyManagers.isLazy()) {
sslContext = new LazyInitSSLContext(sslContext, injectedKeyManagers, injectedtrustManagers, enabledCipherSuites, enabledProtocols);
if (keyManagers != null && keyManagers.isLazy()) {
sslContext = new LazyInitSSLContext(sslContext, keyManagersSupplier, trustManagersSupplier, enabledCipherSuites, enabledProtocols);
} else {
sslContext.init(keyManagers != null ? keyManagers.getKeyManagers() : null, trustManagers, null);
sslContext = wrapSslContext(sslContext, enabledCipherSuites, enabledProtocols);
}

this.theSSLContext = sslContext;

sslContextConsumer.accept(theSSLContext);
} catch (NoSuchAlgorithmException e) {
throw DomainManagementLogger.ROOT_LOGGER.unableToStart(e);
} catch (KeyManagementException e) {
@@ -158,30 +166,10 @@ protected static SSLContext wrapSslContext(SSLContext sslContext, Set<String> en

@Override
public void stop(final StopContext context) {
sslContextConsumer.accept(null);
theSSLContext = null;
}

/*
* Value factory method.
*/

@Override
public SSLContext getValue() throws IllegalStateException, IllegalArgumentException {
return theSSLContext;
}

/*
* Injector Accessor Methods
*/

public InjectedValue<AbstractKeyManagerService> getKeyManagerInjector() {
return injectedKeyManagers;
}

public InjectedValue<TrustManager[]> getTrustManagerInjector() {
return injectedtrustManagers;
}

public static final class ServiceUtil {

private static final String SERVICE_SUFFIX = "ssl-context";
@@ -191,19 +179,16 @@ public static ServiceName createServiceName(final ServiceName parentService, fin
return parentService.append(trustOnly ? TRUST_ONLY_SERVICE_SUFFIX : SERVICE_SUFFIX);
}

public static ServiceBuilder<?> addDependency(final ServiceBuilder<?> sb, final Injector<SSLContext> injector, final ServiceName parentService, final boolean trustOnly) {
sb.addDependency(createServiceName(parentService, trustOnly), SSLContext.class, injector);

return sb;
public static Supplier<SSLContext> requires(final ServiceBuilder<?> sb, final ServiceName parentService, final boolean trustOnly) {
return sb.requires(createServiceName(parentService, trustOnly));
}

}


static final class LazyInitSSLContext extends SSLContext {

LazyInitSSLContext(final SSLContext toWrap, InjectedValue<AbstractKeyManagerService> injectedKeyManagers, InjectedValue<TrustManager[]> injectedtrustManagers, Set<String> enabledCipherSuites, Set<String> enabledProtocols) {
super(new LazyInitSpi(toWrap, injectedKeyManagers, injectedtrustManagers, enabledCipherSuites, enabledProtocols), toWrap.getProvider(), toWrap.getProtocol());
LazyInitSSLContext(final SSLContext toWrap, final Supplier<AbstractKeyManagerService> keyManagersSupplier, final Supplier<TrustManager[]> trustManagersSupplier, Set<String> enabledCipherSuites, Set<String> enabledProtocols) {
super(new LazyInitSpi(toWrap, keyManagersSupplier, trustManagersSupplier, enabledCipherSuites, enabledProtocols), toWrap.getProvider(), toWrap.getProtocol());
}

private static class LazyInitSpi extends SSLContextSpi {
@@ -213,8 +198,8 @@ public static ServiceName createServiceName(final ServiceName parentService, fin
private volatile SSLServerSocketFactory serverSocketFactory;
private volatile SSLSocketFactory socketFactory;

final InjectedValue<AbstractKeyManagerService> injectedKeyManagers;
final InjectedValue<TrustManager[]> injectedtrustManagers;
final Supplier<AbstractKeyManagerService> keyManagersSupplier;
final Supplier<TrustManager[]> trustManagersSupplier;
private final Set<String> enabledCipherSuites;
private final Set<String> enabledProtocols;

@@ -223,8 +208,8 @@ private void doInit() {
synchronized (this) {
if(!init) {
try {
AbstractKeyManagerService keyManagers = injectedKeyManagers.getOptionalValue();
TrustManager[] trustManagers = injectedtrustManagers.getOptionalValue();
AbstractKeyManagerService keyManagers = keyManagersSupplier != null ? keyManagersSupplier.get() : null;
TrustManager[] trustManagers = trustManagersSupplier != null ? trustManagersSupplier.get() : null;
wrapped.init(keyManagers.getKeyManagers(), trustManagers, null);
wrapped = wrapSslContext(wrapped, enabledCipherSuites, enabledProtocols);
} catch (Exception e) {
@@ -238,10 +223,11 @@ private void doInit() {

}

private LazyInitSpi(final SSLContext wrapped, InjectedValue<AbstractKeyManagerService> injectedKeyManagers, InjectedValue<TrustManager[]> injectedtrustManagers, Set<String> enabledCipherSuites, Set<String> enabledProtocols) {
private LazyInitSpi(final SSLContext wrapped, final Supplier<AbstractKeyManagerService> keyManagersSupplier,
final Supplier<TrustManager[]> trustManagersSupplier, Set<String> enabledCipherSuites, Set<String> enabledProtocols) {
this.wrapped = wrapped;
this.injectedKeyManagers = injectedKeyManagers;
this.injectedtrustManagers = injectedtrustManagers;
this.keyManagersSupplier = keyManagersSupplier;
this.trustManagersSupplier = trustManagersSupplier;
this.enabledCipherSuites = enabledCipherSuites;
this.enabledProtocols = enabledProtocols;
}
@@ -27,14 +27,15 @@
import org.jboss.as.domain.management.CallbackHandlerFactory;
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.function.Consumer;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
@@ -43,8 +44,6 @@
import javax.security.sasl.RealmCallback;
import javax.security.sasl.RealmChoiceCallback;
import java.io.IOException;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.auth.callback.OptionalNameCallback;
import org.wildfly.security.credential.source.CredentialSource;
@@ -54,24 +53,29 @@
* A simple identity service for an identity represented by a single secret or password.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class SecretIdentityService implements Service<CallbackHandlerFactory> {
public class SecretIdentityService implements Service {

private static final String SERVICE_SUFFIX = "secret";

private final String password;
private final boolean base64;

private volatile CallbackHandlerFactory factory;
private final InjectedValue<ExceptionSupplier<CredentialSource, Exception>> credentialSourceSupplier = new InjectedValue<>();
private final Consumer<CallbackHandlerFactory> callbackHandlerFactoryConsumer;
private final ExceptionSupplier<CredentialSource, Exception> credentialSourceSupplier;

public SecretIdentityService(final String password, boolean base64) {
public SecretIdentityService(final Consumer<CallbackHandlerFactory> callbackHandlerFactoryConsumer,
final ExceptionSupplier<CredentialSource, Exception> credentialSourceSupplier,
final String password, boolean base64) {
this.callbackHandlerFactoryConsumer = callbackHandlerFactoryConsumer;
this.credentialSourceSupplier = credentialSourceSupplier;
this.password = password;
this.base64 = base64;
}

@Override
public void start(StartContext startContext) throws StartException {
public void start(final StartContext startContext) throws StartException {
final char[] thePassword;
if (base64) {
byte[] value = Base64.getDecoder().decode(password);
@@ -86,24 +90,16 @@ public void start(StartContext startContext) throws StartException {
thePassword = password.toCharArray();
}

factory = (String username) -> new SecretCallbackHandler(username, resolvePassword(thePassword));
callbackHandlerFactoryConsumer.accept((String username) -> new SecretCallbackHandler(username, resolvePassword(thePassword)));
}

public void stop(StopContext stopContext) {
factory = null;
}

public CallbackHandlerFactory getValue() throws IllegalStateException, IllegalArgumentException {
return factory;
}

Injector<ExceptionSupplier<CredentialSource, Exception>> getCredentialSourceSupplierInjector() {
return credentialSourceSupplier;
public void stop(final StopContext stopContext) {
callbackHandlerFactoryConsumer.accept(null);
}

private char[] resolvePassword(char[] thePassword) {
try {
ExceptionSupplier<CredentialSource, Exception> sourceSupplier = credentialSourceSupplier.getOptionalValue();
ExceptionSupplier<CredentialSource, Exception> sourceSupplier = credentialSourceSupplier;
if (sourceSupplier == null) {
return thePassword;
}

Large diffs are not rendered by default.

@@ -47,6 +47,8 @@
import java.util.function.Function;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.net.ssl.SSLContext;
@@ -78,7 +80,6 @@
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedSetValue;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.WildFlyElytronProvider;
@@ -117,7 +118,9 @@
* requiring any of the capabilities provided by the realm.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
// TODO: refactor org.jboss.msc.service.Service to org.jboss.msc.Service when WildFly codebase is fixed
public class SecurityRealmService implements Service<SecurityRealm>, SecurityRealm {

private static final String[] ADDITIONAL_PERMISSION = new String[] { "org.wildfly.transaction.client.RemoteTransactionPermission", "org.jboss.ejb.client.RemoteEJBPermission" };
@@ -139,14 +142,19 @@
}
}

private final InjectedValue<SubjectSupplementalService> subjectSupplemental = new InjectedValue<SubjectSupplementalService>();
private final Consumer<SecurityRealm> securityRealmConsumer;
private final Supplier<SubjectSupplementalService> subjectSupplementalSupplier;
private final Supplier<CallbackHandlerFactory> secretCallbackFactorySupplier;
private final Supplier<KeytabIdentityFactoryService> keytabFactorySupplier;
private final Supplier<SSLContext> sslContextSupplier;
// TODO: eliminate sslContext field when WildFly codebase is migrated to new constructor
@Deprecated
private final InjectedValue<SSLContext> sslContext = new InjectedValue<SSLContext>();

private final InjectedValue<CallbackHandlerFactory> secretCallbackFactory = new InjectedValue<CallbackHandlerFactory>();
private final InjectedValue<KeytabIdentityFactoryService> keytabFactory = new InjectedValue<KeytabIdentityFactoryService>();
private final InjectedSetValue<CallbackHandlerService> callbackHandlerServices = new InjectedSetValue<CallbackHandlerService>();

private final Supplier<String> tmpDirPathSupplier;
// TODO: eliminate tmpDirPath field when WildFly codebase is migrated to new constructor
@Deprecated
private final InjectedValue<String> tmpDirPath = new InjectedValue<>();
private final Set<Supplier<CallbackHandlerService>> callbackHandlerServices;

private final String name;
private final boolean mapGroupsToRoles;
@@ -159,29 +167,66 @@
private Map<String, String> mechanismConfiguration = new HashMap<>();
private MechanismConfigurationSelector mechanismConfigurationSelector;

public SecurityRealmService(String name, boolean mapGroupsToRoles) {
@Deprecated // TODO: eliminate this constructor when WildFly codebase is migrated to new constructor
public SecurityRealmService(final String name, final boolean mapGroupsToRoles) {
this(null, null, null, null, null, null, null, name, mapGroupsToRoles);
}

public SecurityRealmService(final Consumer<SecurityRealm> securityRealmConsumer,
final Supplier<SubjectSupplementalService> subjectSupplementalSupplier,
final Supplier<CallbackHandlerFactory> secretCallbackFactorySupplier,
final Supplier<KeytabIdentityFactoryService> keytabFactorySupplier,
final Supplier<SSLContext> sslContextSupplier,
final Supplier<String> tmpDirPathSupplier,
final Set<Supplier<CallbackHandlerService>> callbackHandlerServices,
final String name, final boolean mapGroupsToRoles) {
this.securityRealmConsumer = securityRealmConsumer;
this.subjectSupplementalSupplier = subjectSupplementalSupplier;
this.secretCallbackFactorySupplier = secretCallbackFactorySupplier;
this.keytabFactorySupplier = keytabFactorySupplier;
this.sslContextSupplier = sslContextSupplier;
this.tmpDirPathSupplier = tmpDirPathSupplier;
this.callbackHandlerServices = callbackHandlerServices;
this.name = name;
this.mapGroupsToRoles = mapGroupsToRoles;
}

@Deprecated // TODO: eliminate this method when WildFly codebase is migrated to new constructor
public InjectedValue<SSLContext> getSSLContextInjector() {
return sslContext;
}

@Deprecated // TODO: eliminate this method when WildFly codebase is migrated to new constructor
public Injector<String> getTmpDirPathInjector() {
return tmpDirPath;
}
/*
* Service Methods
*/

public void start(StartContext context) throws StartException {
@Deprecated // TODO: eliminate this method when WildFly codebase is migrated to new constructor
@Override
public SecurityRealm getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

@Override
public void start(final StartContext context) throws StartException {
ROOT_LOGGER.debugf("Starting '%s' Security Realm Service", name);
for (CallbackHandlerService current : callbackHandlerServices.getValue()) {
// accept this, but don't actually install it in the map
if (current instanceof DomainManagedServerCallbackHandler) {
domainManagedServersCallback = (DomainManagedServerCallbackHandler) current;
continue;
}
AuthMechanism mechanism = current.getPreferredMechanism();
if (registeredServices.containsKey(mechanism)) {
registeredServices.clear();
throw DomainManagementLogger.ROOT_LOGGER.multipleCallbackHandlerForMechanism(mechanism.name());
if (callbackHandlerServices != null) {
for (Supplier<CallbackHandlerService> current : callbackHandlerServices) {
// accept this, but don't actually install it in the map
if (current instanceof DomainManagedServerCallbackHandler) {
domainManagedServersCallback = (DomainManagedServerCallbackHandler) current;
continue;
}
AuthMechanism mechanism = current.get().getPreferredMechanism();
if (registeredServices.containsKey(mechanism)) {
registeredServices.clear();
throw DomainManagementLogger.ROOT_LOGGER.multipleCallbackHandlerForMechanism(mechanism.name());
}
registeredServices.put(mechanism, current.get());
}
registeredServices.put(mechanism, current);
}

/*
@@ -190,7 +235,7 @@ public void start(StartContext context) throws StartException {

final Map<AuthMechanism, MechanismConfiguration> configurationMap = new HashMap<>();

SubjectSupplementalService subjectSupplementalService = this.subjectSupplemental.getOptionalValue();
SubjectSupplementalService subjectSupplementalService = subjectSupplementalSupplier != null ? subjectSupplementalSupplier.get() : null;
org.wildfly.security.auth.server.SecurityRealm authorizationRealm = subjectSupplementalService != null ? subjectSupplementalService.getElytronSecurityRealm() : null;

SecurityDomain.Builder domainBuilder = SecurityDomain.builder();
@@ -231,7 +276,8 @@ public String getRealmMapping(Principal principal, Evidence evidence) {
}
}

mechanismConfiguration.put(LOCAL_USER_CHALLENGE_PATH, getAuthDir(tmpDirPath.getValue()));
// TODO: eliminate tmpDirPathSupplier null check when WildFly code base is migrated to new MSC API
mechanismConfiguration.put(LOCAL_USER_CHALLENGE_PATH, getAuthDir(tmpDirPathSupplier != null ? tmpDirPathSupplier.get() : tmpDirPath.getValue()));
mechanismConfiguration.put(WildFlySasl.ALTERNATIVE_PROTOCOLS, "remoting");

domainBuilder.addRealm("EMPTY", org.wildfly.security.auth.server.SecurityRealm.EMPTY_REALM).build();
@@ -318,6 +364,22 @@ public RealmIdentity getRealmIdentity(Evidence evidence) throws RealmUnavailable
saslBuilder.setFactory(saslServerFactory);
saslBuilder.setMechanismConfigurationSelector(mechanismConfigurationSelector);
saslAuthenticationFactory = saslBuilder.build();
if (securityRealmConsumer != null) {
// TODO: eliminate above null check when WildFly code base is migrated to new MSC API
securityRealmConsumer.accept(this);
}
}

@Override
public void stop(final StopContext context) {
ROOT_LOGGER.debugf("Stopping '%s' Security Realm Service", name);
if (securityRealmConsumer != null) {
// TODO: eliminate above null check when WildFly code base is migrated to new MSC API
securityRealmConsumer.accept(null);
}
registeredServices.clear();
saslAuthenticationFactory = null;
httpAuthenticationFactory = null;
}

public static PermissionVerifier createPermissionVerifier() {
@@ -410,18 +472,6 @@ private String getAuthDir(final String path) throws StartException {
return authDir.getAbsolutePath();
}


public void stop(StopContext context) {
ROOT_LOGGER.debugf("Stopping '%s' Security Realm Service", name);
registeredServices.clear();
saslAuthenticationFactory = null;
httpAuthenticationFactory = null;
}

public SecurityRealmService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

public String getName() {
return name;
}
@@ -528,7 +578,7 @@ public SubjectUserInfo createSubjectUserInfo(Collection<Principal> userPrincipal

Object skipGroupLoading = sharedState.get(SKIP_GROUP_LOADING_KEY);
if (skipGroupLoading == null || Boolean.parseBoolean(skipGroupLoading.toString()) == false) {
SubjectSupplementalService subjectSupplementalService = subjectSupplemental.getOptionalValue();
SubjectSupplementalService subjectSupplementalService = subjectSupplementalSupplier != null ? subjectSupplementalSupplier.get() : null;
if (subjectSupplementalService != null) {
SubjectSupplemental subjectSupplemental = subjectSupplementalService.getSubjectSupplemental(sharedState);
subjectSupplemental.supplementSubject(subject);
@@ -567,7 +617,7 @@ private CallbackHandlerService getCallbackHandlerService(final AuthMechanism mec

@Override
public SubjectIdentity getSubjectIdentity(String protocol, String forHost) {
KeytabIdentityFactoryService kifs = keytabFactory.getOptionalValue();
KeytabIdentityFactoryService kifs = keytabFactorySupplier != null ? keytabFactorySupplier.get() : null;

return kifs != null ? kifs.getSubjectIdentity(protocol, forHost) : null;
}
@@ -678,40 +728,13 @@ public HttpAuthenticationFactory getHttpAuthenticationFactory() {
return httpAuthenticationFactory;
}

/*
* Injectors
*/

public InjectedValue<SubjectSupplementalService> getSubjectSupplementalInjector() {
return subjectSupplemental;
}

public InjectedValue<SSLContext> getSSLContextInjector() {
return sslContext;
}

public InjectedValue<CallbackHandlerFactory> getSecretCallbackFactory() {
return secretCallbackFactory;
}

public InjectedValue<KeytabIdentityFactoryService> getKeytabIdentityFactoryInjector() {
return keytabFactory;
}

public InjectedSetValue<CallbackHandlerService> getCallbackHandlerService() {
return callbackHandlerServices;
}

public Injector<String> getTmpDirPathInjector() {
return tmpDirPath;
}

public SSLContext getSSLContext() {
return sslContext.getOptionalValue();
// TODO: replace sslContext.getOptionalValue() with null once WildFly code base is migrated to new MSC API
return sslContextSupplier != null ? sslContextSupplier.get() : sslContext.getOptionalValue();
}

public CallbackHandlerFactory getSecretCallbackHandlerFactory() {
return secretCallbackFactory.getOptionalValue();
return secretCallbackFactorySupplier != null ? secretCallbackFactorySupplier.get() : null;
}

private GSSKerberosCredential getGSSKerberosCredential(final String protocol, final String forHost)
@@ -23,8 +23,8 @@
package org.jboss.as.domain.management.security;

import java.util.Map;
import java.util.function.Supplier;

import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceName;
import org.wildfly.security.auth.server.SecurityRealm;
@@ -33,6 +33,7 @@
* Interface to be implemented by services supplying SubjectSupplemental implementations.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public interface SubjectSupplementalService {

@@ -63,8 +64,8 @@ default SecurityRealm getElytronSecurityRealm() {
private ServiceUtil() {
}

public static ServiceBuilder<?> addDependency(ServiceBuilder<?> sb, Injector<SubjectSupplementalService> injector, ServiceName serviceName) {
return sb.addDependency(serviceName, SubjectSupplementalService.class, injector);
public static Supplier<SubjectSupplementalService> requires(final ServiceBuilder<?> sb, final ServiceName serviceName) {
return sb.requires(serviceName);
}

}
@@ -41,6 +41,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -54,13 +55,10 @@
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.common.Assert;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.auth.SupportLevel;
@@ -83,6 +81,7 @@
* A CallbackHandler for users defined within the domain model.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com">Richard Opalka</a>
*/
public class UserDomainCallbackHandler implements Service<CallbackHandlerService>, CallbackHandlerService, CallbackHandler {

@@ -92,9 +91,14 @@
private final String realm;

private volatile ModelNode userDomain;
private final InjectedValue<Map<String, ExceptionSupplier<CredentialSource, Exception>>> credentialSourceSupplier = new InjectedValue<>();

public UserDomainCallbackHandler(String realm, ModelNode userDomain) {
private final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer;
private final Map<String, ExceptionSupplier<CredentialSource, Exception>> credentialSourceSupplier;

UserDomainCallbackHandler(final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer,
final Map<String, ExceptionSupplier<CredentialSource, Exception>> credentialSourceSupplier,
final String realm, final ModelNode userDomain) {
this.callbackHandlerServiceConsumer = callbackHandlerServiceConsumer;
this.credentialSourceSupplier = credentialSourceSupplier;
this.realm = realm;
setUserDomain(userDomain);
}
@@ -103,10 +107,6 @@ void setUserDomain(final ModelNode userDomain) {
this.userDomain = userDomain == null || !userDomain.isDefined() ? new ModelNode().setEmptyObject() : userDomain.clone();
}

Injector<Map<String, ExceptionSupplier<CredentialSource, Exception>>> getCredentialSourceSupplierInjector() {
return credentialSourceSupplier;
}

/*
* CallbackHandlerService Methods
*/
@@ -137,17 +137,18 @@ public CallbackHandler getCallbackHandler(Map<String, Object> sharedState) {
return new SecurityRealmImpl();
}

/*
* Service Methods
*/

public void start(StartContext context) throws StartException {
@Override
public void start(final StartContext context) {
callbackHandlerServiceConsumer.accept(this);
}

public void stop(StopContext context) {
@Override
public void stop(final StopContext context) {
callbackHandlerServiceConsumer.accept(null);
}

public UserDomainCallbackHandler getValue() throws IllegalStateException, IllegalArgumentException {
@Override
public CallbackHandlerService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

@@ -242,8 +243,8 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback

private ExceptionSupplier<CredentialSource, Exception> getUserCredentialSourceSupplier(String userName) {
ExceptionSupplier<CredentialSource, Exception> userCredentialSourceSupplier = null;
if (credentialSourceSupplier.getOptionalValue() != null && credentialSourceSupplier.getValue().containsKey(userName)) {
userCredentialSourceSupplier = credentialSourceSupplier.getValue().get(userName);
if (credentialSourceSupplier != null && credentialSourceSupplier.containsKey(userName)) {
userCredentialSourceSupplier = credentialSourceSupplier.get(userName);
}
return userCredentialSourceSupplier;
}
@@ -34,6 +34,8 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.Function;

import javax.naming.CommunicationException;
@@ -52,13 +54,10 @@
import org.jboss.as.domain.management.logging.DomainManagementLogger;
import org.jboss.as.domain.management.security.LdapSearcherCache.AttachmentKey;
import org.jboss.as.domain.management.security.LdapSearcherCache.SearchResult;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
import org.wildfly.security.auth.principal.NamePrincipal;
@@ -73,27 +72,43 @@
* A CallbackHandler for users within an LDAP directory.
*
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
* @author <a href="mailto:ropalka@redhat.com>Richard Opalka</a>
*/
public class UserLdapCallbackHandler implements Service<CallbackHandlerService>, CallbackHandlerService {
public class UserLdapCallbackHandler implements Service, CallbackHandlerService {
private static final AttachmentKey<PasswordCredential> PASSWORD_KEY = AttachmentKey.create(PasswordCredential.class);
private static final String SERVICE_SUFFIX = "ldap";
public static final String DEFAULT_USER_DN = "dn";
private final InjectedValue<LdapConnectionManager> connectionManager = new InjectedValue<LdapConnectionManager>();
private final InjectedValue<LdapSearcherCache<LdapEntry, String>> userSearcherInjector = new InjectedValue<LdapSearcherCache<LdapEntry, String>>();
private final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer;
private final Supplier<LdapConnectionManager> connectionManagerSupplier;
private final Supplier<LdapSearcherCache<LdapEntry, String>> userSearcherSupplier;
private final boolean allowEmptyPassword;
private final boolean shareConnection;
protected final int searchTimeLimit = 10000; // TODO - Maybe make configurable.
public UserLdapCallbackHandler(boolean allowEmptyPassword, boolean shareConnection) {
UserLdapCallbackHandler(final Consumer<CallbackHandlerService> callbackHandlerServiceConsumer,
final Supplier<LdapConnectionManager> connectionManagerSupplier,
final Supplier<LdapSearcherCache<LdapEntry, String>> userSearcherSupplier,
boolean allowEmptyPassword, boolean shareConnection) {
this.callbackHandlerServiceConsumer = callbackHandlerServiceConsumer;
this.connectionManagerSupplier = connectionManagerSupplier;
this.userSearcherSupplier = userSearcherSupplier;
this.allowEmptyPassword = allowEmptyPassword;
this.shareConnection = shareConnection;
}
public void start(final StartContext context) {
callbackHandlerServiceConsumer.accept(this);
}
public void stop(final StopContext context) {
callbackHandlerServiceConsumer.accept(null);
}
/*
* CallbackHandlerService Methods
*/
@@ -137,7 +152,7 @@ public CallbackHandler getCallbackHandler(Map<String, Object> sharedState) {
LdapConnectionHandler ldapConnectionHandler = createLdapConnectionHandler();
try {
try {
SearchResult<LdapEntry> searchResult = userSearcherInjector.getValue().search(ldapConnectionHandler, p.getName());
SearchResult<LdapEntry> searchResult = userSearcherSupplier.get().search(ldapConnectionHandler, p.getName());
return p instanceof RealmUser ? new MappedPrincipal(((RealmUser) p).getRealm(), searchResult.getResult().getSimpleName(), p.getName())
: new MappedPrincipal(searchResult.getResult().getSimpleName(), p.getName());
@@ -152,30 +167,8 @@ public CallbackHandler getCallbackHandler(Map<String, Object> sharedState) {
};
}
public void start(StartContext context) throws StartException {
}

public void stop(StopContext context) {
}

public CallbackHandlerService getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}

/*
* Access to Injectors
*/

public InjectedValue<LdapConnectionManager> getConnectionManagerInjector() {
return connectionManager;
}

public Injector<LdapSearcherCache<LdapEntry, String>> getLdapUserSearcherInjector() {
return userSearcherInjector;
}

private LdapConnectionHandler createLdapConnectionHandler() {
LdapConnectionManager connectionManager = this.connectionManager.getValue();
LdapConnectionManager connectionManager = this.connectionManagerSupplier.get();
return LdapConnectionHandler.newInstance(connectionManager);
}
@@ -251,7 +244,7 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
LdapConnectionHandler lch = createLdapConnectionHandler();
try {
// 2 - Search to identify the DN of the user connecting
SearchResult<LdapEntry> searchResult = userSearcherInjector.getValue().search(lch, username);
SearchResult<LdapEntry> searchResult = userSearcherSupplier.get().search(lch, username);
evidenceVerifyCallback.setVerified(verifyPassword(lch, searchResult, username, password, sharedState));
} catch (Exception e) {
@@ -342,7 +335,7 @@ public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailab
LdapConnectionHandler ldapConnectionHandler = createLdapConnectionHandler();
try {
SearchResult<LdapEntry> searchResult = userSearcherInjector.getValue().search(ldapConnectionHandler, name);
SearchResult<LdapEntry> searchResult = userSearcherSupplier.get().search(ldapConnectionHandler, name);
return new RealmIdentityImpl(new NamePrincipal(name), ldapConnectionHandler, searchResult, SecurityRealmService.SharedStateSecurityRealm.getSharedState());
} catch (IOException | CommunicationException e) {
@@ -22,13 +22,16 @@

package org.jboss.as.domain.management.security;

import org.jboss.as.controller.services.path.PathManager;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.regex.Matcher;

import static org.jboss.as.domain.management.logging.DomainManagementLogger.ROOT_LOGGER;
@@ -57,8 +60,12 @@
* End of state maintained during persistence.
*/

public UserPropertiesFileLoader(final String path, final String relativeTo) {
super(path, relativeTo);
public UserPropertiesFileLoader(final Supplier<PathManager> pathManagerSupplier, final String path, final String relativeTo) {
super(pathManagerSupplier, path, relativeTo);
}

public UserPropertiesFileLoader(final String path) {
super(null, path, null);
}

public String getRealmName() throws IOException {
@@ -161,7 +161,7 @@ public State execute() {
}

private UserPropertiesFileLoader loadUsersFile(File file) throws IOException {
UserPropertiesFileLoader fileLoader = new UserPropertiesFileLoader(file.getAbsolutePath(), null);
UserPropertiesFileLoader fileLoader = new UserPropertiesFileLoader(file.getAbsolutePath());
try {
fileLoader.start(null);
} catch (StartException e) {
@@ -176,7 +176,7 @@ private UserPropertiesFileLoader loadUsersFile(File file) throws IOException {
for (File file : foundGroupsFiles) {
PropertiesFileLoader propertiesLoad = null;
try {
propertiesLoad = new PropertiesFileLoader(file.getCanonicalPath(), null);
propertiesLoad = new PropertiesFileLoader(file.getCanonicalPath());
propertiesLoad.start(null);
loadedGroups.putAll((Map) propertiesLoad.getProperties());
} finally {
@@ -220,15 +220,15 @@ private boolean findFiles(final List<File> foundFiles, final String fileName) {
SERVER_CONFIG_DIR, SERVER_BASE_DIR, "standalone", fileName);
if (standaloneProps.exists()) {
foundFiles.add(standaloneProps);
}
} // TODO should this invalid --sc be an error regardless of whether domainConfigOpt points to a valid dir?
}

if (domainConfigOpt != null) {
File domainProps = buildFilePath(DOMAIN_CONFIG_USER_DIR, stateValues.getOptions().getDomainConfigDir(),
DOMAIN_CONFIG_DIR, DOMAIN_BASE_DIR, "domain", fileName);
if (domainProps.exists()) {
foundFiles.add(domainProps);
}
} // TODO should this invalid --dc be an error regardless of whether serverConfigOpt points to a valid dir?
}

return !foundFiles.isEmpty();
@@ -243,7 +243,6 @@ private File buildFilePath(final String serverConfigUserDirPropertyName, final S

File file = new File(dirPath, fileName);
validatePermissions(dirPath, file);

return file;

}
@@ -281,26 +280,29 @@ private File buildDirPath(final String serverConfigUserDirPropertyName, final St
}

/**
* This method performs a series of permissions checks given a directory and properties file path.
* This method performs a series of permissions checks given a directory and properties file path, if they exist.
*
* 1 - Check whether the parent directory dirPath has proper execute and read permissions
* 2 - Check whether properties file path is readable and writable
*
* If either of the permissions checks fail, update validFilePermissions and filePermissionsProblemPath
* appropriately.
*
* Permission checks are not performed if the dir or file do not exist, as the caller is expected to handle
* non-existing files separately, not as a permission problem.
*
*/
private void validatePermissions(final File dirPath, final File file) {

// Check execute and read permissions for parent dirPath
if( !dirPath.canExecute() || !dirPath.canRead() ) {
if( dirPath.exists() && (!dirPath.canExecute() || !dirPath.canRead()) ) {
validFilePermissions = false;
filePermissionsProblemPath = dirPath.getAbsolutePath();
return;
}

// Check read and write permissions for properties file
if( !file.canRead() || !file.canWrite() ) {
if( file.exists() && (!file.canRead() || !file.canWrite()) ) {
validFilePermissions = false;
filePermissionsProblemPath = dirPath.getAbsolutePath();
}
@@ -54,8 +54,8 @@ void persist(final String key, final String value, final boolean enableDisableMo
* Implement the persistence handler for storing the user properties.
*/
void persist(final String key, final String value, final boolean enableDisableMode, final boolean disable, final File file, final String realm) throws IOException, StartException {
final PropertiesFileLoader propertiesHandler = realm == null ? new PropertiesFileLoader(file.getAbsolutePath(), null) :
new UserPropertiesFileLoader(file.getAbsolutePath(), null);
final PropertiesFileLoader propertiesHandler = realm == null ? new PropertiesFileLoader(file.getAbsolutePath()) :
new UserPropertiesFileLoader(file.getAbsolutePath());
try {
propertiesHandler.start(null);
if (realm != null) {
@@ -23,7 +23,7 @@

package org.jboss.as.domain.management.security;


import java.util.function.Supplier;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;

@@ -60,12 +60,12 @@
*/
@Test
public void testForHostWithProto() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/EXAMPLE", RIGHT_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "EXAMPLE", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/EXAMPLE", RIGHT_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "EXAMPLE", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "EXAMPLE");
@@ -79,12 +79,12 @@ public void testForHostWithProto() throws StartException {
*/
@Test
public void testForHostWithoutProto() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "EXAMPLE", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "EXAMPLE", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "EXAMPLE");
@@ -98,12 +98,12 @@ public void testForHostWithoutProto() throws StartException {
*/
@Test
public void testPrincipalWithProto() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "EXAMPLE");
@@ -117,12 +117,12 @@ public void testPrincipalWithProto() throws StartException {
*/
@Test
public void testPrincipalWithoutProto() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/EXAMPLE@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", WRONG_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "EXAMPLE");
@@ -136,12 +136,12 @@ public void testPrincipalWithoutProto() throws StartException {
*/
@Test
public void testDefault() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", RIGHT_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "HTTP/SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "*", RIGHT_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "EXAMPLE");
@@ -155,10 +155,10 @@ public void testDefault() throws StartException {
*/
@Test
public void testHostNameCaseInSensitive() throws StartException {
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService();
service.getKeytabInjector().inject(createKeytabService("HTTP/localhost@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.getKeytabInjector().inject(createKeytabService("PROTO/ANYVALUE@SOMETHING.COM", "localhost", WRONG_SUBJECT_IDENTITY));
KeytabIdentityFactoryService service = new KeytabIdentityFactoryService(null);
service.addKeytabSupplier(createKeytabService("HTTP/localhost@SOMETHING.COM", "SOMEHOST", RIGHT_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("HTTP/ANYVALUE@SOMETHING.COM", "SOMEHOST", WRONG_SUBJECT_IDENTITY));
service.addKeytabSupplier(createKeytabService("PROTO/ANYVALUE@SOMETHING.COM", "localhost", WRONG_SUBJECT_IDENTITY));
service.start(null);

SubjectIdentity subjectIdentity = service.getSubjectIdentity("HTTP", "LocalHost");
@@ -171,11 +171,16 @@ public void testHostNameCaseInSensitive() throws StartException {
/**
* Creates mocked KeytabService
*/
private KeytabService createKeytabService(String principal, String forHost, final SubjectIdentity subjectIdentity) {
return new KeytabService(principal, null, null, new String[]{forHost}, true) {
private Supplier<KeytabService> createKeytabService(String principal, String forHost, final SubjectIdentity subjectIdentity) {
return new Supplier<KeytabService>() {
@Override
public SubjectIdentity createSubjectIdentity(boolean isClient) throws LoginException {
return subjectIdentity;
public KeytabService get() {
return new KeytabService(null, null, principal, null, null, new String[]{forHost}, true) {
@Override
public SubjectIdentity createSubjectIdentity(boolean isClient) throws LoginException {
return subjectIdentity;
}
};
}
};
}
@@ -44,6 +44,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;


/**
@@ -146,18 +147,34 @@ public void testModes() throws StartException, IOException, NamingException, Int
return startService(mode,cacheFailures, size, 30);
}

private LdapSearcherCache<LdapEntry, String> startService(CacheMode mode, boolean cacheFailures, int size, int evictionTimeout) throws StartException {
private LdapSearcherCache<LdapEntry, String> startService(CacheMode mode, boolean cacheFailures, int size, int evictionTimeout) {
LdapCacheService<LdapEntry, String> service;
switch (mode) {
case BY_ACCESS:
service = LdapCacheService.createByAccessCacheService(userSearcher, evictionTimeout, cacheFailures, size);
service = LdapCacheService.createByAccessCacheService(NullConsumer.INSTANCE, userSearcher, evictionTimeout, cacheFailures, size);
break;
default:
service = LdapCacheService.createBySearchCacheService(userSearcher, evictionTimeout, cacheFailures, size);
service = LdapCacheService.createBySearchCacheService(NullConsumer.INSTANCE, userSearcher, evictionTimeout, cacheFailures, size);
break;
}
service.start(null);
return service.getValue();
return service.cacheImplementation;
}

private static final class NullConsumer implements Consumer<LdapSearcherCache<LdapEntry, String>> {

private static final NullConsumer INSTANCE = new NullConsumer();

@Override
public Consumer<LdapSearcherCache<LdapEntry, String>> andThen(final Consumer<? super LdapSearcherCache<LdapEntry, String>> after) {
// ignored
return null;
}

@Override
public void accept(final LdapSearcherCache<LdapEntry, String> o) {
// ignored
}
}

private void testCacheFailures(CacheMode mode, boolean cacheFailure, int calls) throws StartException, IOException, NamingException {
@@ -97,7 +97,7 @@ public void testLoad() throws Exception {
tmpFile = createTempFile();
writeTestDataToFile(tmpFile);

PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader.start(null);
Properties props = loader.getProperties();
verifyProperties(props, props.size());
@@ -114,15 +114,15 @@ public void testAdd() throws Exception {
tmpFile = createTempFile();
writeTestDataToFile(tmpFile);

PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader.start(null);
Properties props = loader.getProperties();
props.put("NEW", "VALUE");
loader.persistProperties();
loader.stop(null);

// reload the file and make sure everything is there
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader2.start(null);
Properties props2 = loader2.getProperties();
verifyProperties(props2, props2.size()-1, "NEW", "VALUE");
@@ -139,7 +139,7 @@ public void testDoublePersistWithEmptyValue() throws Exception {
tmpFile = createTempFile();
writeTestDataToFile(tmpFile);

PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader.start(null);
Properties props = loader.getProperties();
props.put("EMPTY", "");
@@ -151,7 +151,7 @@ public void testDoublePersistWithEmptyValue() throws Exception {
loader.stop(null);

//verify all properties are present
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader2.start(null);
Properties props2 = loader2.getProperties();
verifyProperties(props2, props2.size()-2, "NEW", "VALUE");
@@ -173,15 +173,15 @@ public void testRemove() throws Exception {
tmpFile = createTempFile();
writeTestDataToFile(tmpFile);

PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader.start(null);
Properties props = loader.getProperties();
props.remove("ABC");
loader.persistProperties();
loader.stop(null);

// reload the file and make sure the removed item has been removed
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader2.start(null);
Properties props2 = loader2.getProperties();
verifyProperties(props2, props2.size()+1, "ABC", null);
@@ -199,15 +199,15 @@ public void testChangeValue() throws Exception {
tmpFile = createTempFile();
writeTestDataToFile(tmpFile);

PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader.start(null);
Properties props = loader.getProperties();
props.put("ABC", "321");
loader.persistProperties();
loader.stop(null);

// reload the file and make sure the removed item has been removed
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath(), null);
PropertiesFileLoader loader2 = new PropertiesFileLoader(tmpFile.getAbsolutePath());
loader2.start(null);
Properties props2 = loader2.getProperties();
verifyProperties(props2, props2.size(), "ABC", "321");
@@ -22,7 +22,10 @@
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.msc.service.Service;
@@ -100,14 +103,14 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
public void testDifferentServiceBuilderTypes() {
ServiceTarget serviceTarget = container.subTarget();

final SecurityRealmService securityRealmService = new SecurityRealmService(TESTNAME, false);
securityRealmService.getTmpDirPathInjector().inject(tmpDir.toAbsolutePath().toString());
final Supplier<String> tmpDirSupplier = () -> tmpDir.toAbsolutePath().toString();

final ServiceName realmServiceName = SecurityRealm.ServiceUtil.createServiceName(TESTNAME);
ServiceController<?> realmController = serviceTarget
.addService(realmServiceName, securityRealmService)
.addAliases(SecurityRealm.ServiceUtil.createLegacyServiceName(TESTNAME))
.install();
final ServiceBuilder<?> realmBuilder = serviceTarget.addService(realmServiceName);
final Consumer<SecurityRealm> securityRealmConsumer = realmBuilder.provides(realmServiceName, SecurityRealm.ServiceUtil.createLegacyServiceName(TESTNAME));
final SecurityRealmService securityRealmService = new SecurityRealmService(securityRealmConsumer, null, null, null, null, tmpDirSupplier, new HashSet(), TESTNAME, false);
realmBuilder.setInstance(securityRealmService);
final ServiceController<?> realmController = realmBuilder.install();

TestService legacy = new TestService();
ServiceBuilder legacyBuilder = serviceTarget.addService(ServiceName.of("LEGACY"), legacy);
@@ -23,6 +23,7 @@
package org.jboss.as.domain.management.security.adduser;

import org.jboss.as.domain.management.security.adduser.AddUser.FileMode;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

@@ -49,6 +50,16 @@
public void setup() throws IOException {
values.setFileMode(FileMode.MANAGEMENT);
values.getOptions().setJBossHome(getProperty("java.io.tmpdir")+File.separator+"permmissions");
cleanFiles("domain");
cleanFiles("standalone");
cleanProperties();
}

@After
public void teardown() {
cleanFiles("domain");
cleanFiles("standalone");
cleanProperties();
}

private File createPropertyFile(String filename, String mode) throws IOException {
@@ -73,6 +84,23 @@ private File createPropertyFile(String filename, String mode) throws IOException
return propertyUserFile;
}

/**
* Restore permissions for a dir structure (so we can clean it out) and then clean it.
* Allows clean rerunning of the test in an env where java.io.tmpdir doesn't change between runs.
*/
private void cleanFiles(String mode) {
File dir = new File(getProperty("java.io.tmpdir")+File.separator+"permmissions"+File.separator+mode);
dir.setExecutable(true);
File[] children = dir.listFiles();
if (children != null) {
for (File child : children) {
child.setReadable(true);
child.setWritable(true);
}
}
cleanFiles(dir);
}

@Test
public void testPropertyFileFinderFilePermissions() throws IOException {

@@ -101,7 +129,7 @@ public void testPropertyFileFinderFilePermissions() throws IOException {
// Test parent dir without execute
if(standaloneDir.setExecutable(false)) {
State nextState = propertyFileFinder.execute();
assertTrue(nextState instanceof ErrorState);
assertTrue(nextState.toString(), nextState instanceof ErrorState);
standaloneDir.setExecutable(true);
}

@@ -110,7 +138,7 @@ public void testPropertyFileFinderFilePermissions() throws IOException {
// Test file without read
if(standaloneMgmtUserFile.setReadable(false)) {
State nextState = propertyFileFinder.execute();
assertTrue(nextState instanceof ErrorState);
assertTrue(nextState.toString(), nextState instanceof ErrorState);
standaloneMgmtUserFile.setReadable(true);
}

@@ -119,7 +147,7 @@ public void testPropertyFileFinderFilePermissions() throws IOException {
// Test file without write
if(standaloneMgmtUserFile.setWritable(false)) {
State nextState = propertyFileFinder.execute();
assertTrue(nextState instanceof ErrorState);
assertTrue(nextState.toString(), nextState instanceof ErrorState);
standaloneMgmtUserFile.setWritable(true);
}

@@ -24,6 +24,7 @@

import org.jboss.as.domain.management.security.adduser.AddUser.FileMode;
import org.jboss.msc.service.StartException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

@@ -36,6 +37,7 @@
import java.util.Properties;

import static java.lang.System.getProperty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
@@ -50,6 +52,16 @@
public void setup() throws IOException {
values.setFileMode(FileMode.MANAGEMENT);
values.getOptions().setJBossHome(getProperty("java.io.tmpdir"));
cleanFiles("domain");
cleanFiles("standalone");
cleanProperties();
}

@After
public void teardown() {
cleanFiles("domain");
cleanFiles("standalone");
cleanProperties();
}

private File createPropertyFile(String filename, String mode) throws IOException {
@@ -71,6 +83,11 @@ private File createPropertyFile(String filename, String mode) throws IOException
return propertyUserFile;
}

private void cleanFiles(String mode) {
File dir = new File(getProperty("java.io.tmpdir")+File.separator+mode);
cleanFiles(dir);
}

@Test
public void overridePropertyfileLocationRead() throws IOException {
File domainMgmtUserFile = createPropertyFile("mgmt-users.properties", "domain");
@@ -125,5 +142,37 @@ public void overridePropertyfileLocationWrite() throws IOException, StartExcepti
consoleBuilder.validate();
}

@Test
public void noDomainDir() throws IOException {
File standaloneMgmtUserFile = createPropertyFile("mgmt-users.properties", "standalone");
File standaloneMgmtGroupFile = createPropertyFile("mgmt-groups.properties", "standalone");
File domainDir = standaloneMgmtGroupFile.getParentFile().getParentFile().toPath().resolve("domain").toFile();
assertFalse(domainDir.exists());

System.setProperty("jboss.server.config.dir", standaloneMgmtGroupFile.getParent());
System.setProperty("jboss.domain.config.dir", domainDir.getAbsolutePath());
State propertyFileFinder = new PropertyFileFinder(consoleMock, values);
State nextState = propertyFileFinder.execute();
assertTrue(nextState.toString(), nextState instanceof PromptRealmState);
assertTrue("Expected to find the "+USER_NAME+" in the list of known enabled users",values.getEnabledKnownUsers().contains(USER_NAME));
assertTrue("Expected the values.getPropertiesFiles() contained the "+standaloneMgmtUserFile,values.getUserFiles().contains(standaloneMgmtUserFile));
}

@Test
public void noStandaloneDir() throws IOException {
File domainMgmtUserFile = createPropertyFile("mgmt-users.properties", "domain");
File domainMgmtGroupFile = createPropertyFile("mgmt-groups.properties", "domain");
File standaloneDir = domainMgmtGroupFile.getParentFile().getParentFile().toPath().resolve("standalone").toFile();
assertFalse(standaloneDir.exists());

System.setProperty("jboss.server.config.dir", standaloneDir.getAbsolutePath());
System.setProperty("jboss.domain.config.dir", domainMgmtGroupFile.getParent());
State propertyFileFinder = new PropertyFileFinder(consoleMock, values);
State nextState = propertyFileFinder.execute();
assertTrue(nextState.toString(), nextState instanceof PromptRealmState);
assertTrue("Expected to find the "+USER_NAME+" in the list of known enabled users",values.getEnabledKnownUsers().contains(USER_NAME));
assertTrue("Expected the values.getPropertiesFiles() contained the "+domainMgmtUserFile,values.getUserFiles().contains(domainMgmtUserFile));
}


}