CCM NG: LoginManagerTest. Note: LoginManager does not work yet, Test class and LoginManager etc still need some work
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3527 8810af33-2d31-482b-a856-94f89814c4dfpull/2/head
parent
c9b854a723
commit
5282e63ebd
|
|
@ -26,6 +26,8 @@ import com.arsdigita.util.parameter.SpecificClassParameter;
|
|||
import com.arsdigita.util.parameter.StringArrayParameter;
|
||||
import com.arsdigita.util.parameter.StringParameter;
|
||||
|
||||
import org.libreccm.core.authentication.LocalLoginModule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -79,7 +81,8 @@ public class SecurityConfig extends AbstractConfig {
|
|||
private final Parameter m_loginConfig = new StringArrayParameter(
|
||||
"waf.login_config", Parameter.REQUIRED,
|
||||
new String[]{
|
||||
"Register:com.arsdigita.kernel.security.LocalLoginModule:requisite",});
|
||||
String.format("Register:%s:requisite",
|
||||
LocalLoginModule.class.getName())});
|
||||
|
||||
private final Parameter m_adminEmail = new StringParameter(
|
||||
"waf.admin.contact_email", Parameter.OPTIONAL, null);
|
||||
|
|
@ -223,7 +226,6 @@ public class SecurityConfig extends AbstractConfig {
|
|||
// }
|
||||
// return s_systemAdministratorEmailAddress;
|
||||
// }
|
||||
|
||||
public final boolean isAutoRegistrationOn() {
|
||||
return ((Boolean) get(m_autoRegistrationOn)).booleanValue();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package com.arsdigita.runtime;
|
||||
|
||||
import com.arsdigita.util.Assert;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Subject to change.
|
||||
*
|
||||
* An error to indicate invalid configurations.
|
||||
*
|
||||
* Usage: throw new ConfigError( "message" );
|
||||
*
|
||||
* @author Justin Ross <jross@redhat.com>
|
||||
*/
|
||||
public class ConfigError extends Error {
|
||||
|
||||
private static final long serialVersionUID = 5224480607138738741L;
|
||||
|
||||
/**
|
||||
* Constructs a new configuration error with the content
|
||||
* <code>message</code>.
|
||||
*
|
||||
* @param message A <code>String</code> describing what's wrong; it cannot
|
||||
* be null
|
||||
*/
|
||||
public ConfigError(final String message) {
|
||||
super(message);
|
||||
|
||||
Assert.exists(message, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new configuration error with a default message.
|
||||
*/
|
||||
public ConfigError() {
|
||||
super("Configuration is invalid");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,197 +18,42 @@
|
|||
*/
|
||||
package org.libreccm.core.authentication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import com.arsdigita.kernel.security.SecurityConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
/**
|
||||
* Parses a string array/list of strings to create an configuration for JAAS.
|
||||
* Configuration for JAAS containing all active {@link LoginModule}
|
||||
* implementations.
|
||||
*
|
||||
* This class is based on the {@code com.arsdigita.kernel.security.LoginConfig}
|
||||
* class. The code itself has been heavily refactored using features like
|
||||
* Generics and other things added to the Java language and the Java Standard
|
||||
* Library in the last ten years since the original class was written.
|
||||
* The active modules are stored in the {@link SecurityConfig} as an array
|
||||
* of strings. The original {@code com.arsdigita.kernel.security.LoginConfig}
|
||||
* class parsed this string array in its constructor. For LibreCCM 7 the code
|
||||
* has been split up. The logic for parsing the configuration is now provided
|
||||
* by the {@link LoginConfigBuilder} class. This allowed us the greatly
|
||||
* simplify this class. Also now we don't have a constructor which throws
|
||||
* Exceptions.
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public class LoginConfig extends Configuration {
|
||||
|
||||
/**
|
||||
* Maps application names to {@code AppConfigurationEntry[]}.
|
||||
*
|
||||
* ToDo: To create a configuration this class parses strings. Of course the
|
||||
* parsing may fails. In this case the constructor throws an exception.
|
||||
* Throwing exceptions from a constructor is considered a bad practise
|
||||
* because this may leaves a party created object. Instead the better option
|
||||
* would be a factory method or a builder.
|
||||
* The configuration entries.
|
||||
*/
|
||||
private final Map<String, AppConfigurationEntry[]> appConfigs = new HashMap<>();
|
||||
private final Map<String, AppConfigurationEntry[]> appConfigs;
|
||||
|
||||
private LoginConfig() {
|
||||
//Nothing.
|
||||
this.appConfigs = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new login configuration from a list of string. {@code Request}
|
||||
* and {@code Register} are mandatory contexts, WAF refuses to start if they
|
||||
* are not configured. Each login context can span multiple modules.
|
||||
*
|
||||
* The input list comprises of strings adhering to the following format:
|
||||
*
|
||||
* <pre>
|
||||
* context:moduleName:controlFlag[:option1[:option2[:...]]]
|
||||
* </pre>
|
||||
*
|
||||
* <dl>
|
||||
*
|
||||
* <dt>context</dt>
|
||||
* <dd>String</dd>
|
||||
*
|
||||
* <dt>moduleName</dt>
|
||||
* <dd>String</dd>
|
||||
*
|
||||
* <dt>controlFlag</dt>
|
||||
* <dd>"required"</dd>
|
||||
* <dd>"requisite"</dd>
|
||||
* <dd>"sufficient"</dd>
|
||||
* <dd>"optional"</dd>
|
||||
*
|
||||
* <dt>option</dt>
|
||||
* <dd>"key=value"</dd>
|
||||
* </dl>
|
||||
*
|
||||
* <p>
|
||||
* Example:</p>
|
||||
*
|
||||
* <pre>
|
||||
* Request:com.arsdigita.kernel.security.CredentialLoginModule:requisite:debug=true
|
||||
* Register:com.arsdigita.kernel.security.LocalLoginModule:requisite
|
||||
* Register:com.arsdigita.kernel.security.UserIDLoginModule:requisite
|
||||
* Register:com.arsdigita.kernel.security.CredentialLoginModule:optional
|
||||
* </pre>
|
||||
*
|
||||
* @param config The configuration in string format.
|
||||
*
|
||||
*/
|
||||
public LoginConfig(final List<String> config) {
|
||||
final Map<String, List<String>> contextConfigs = new HashMap<>();
|
||||
|
||||
for (String tuple : config) {
|
||||
final int pos = tuple.indexOf(':');
|
||||
final String context = tuple.substring(0, pos);
|
||||
final String moduleConfig = tuple.substring(pos + 1);
|
||||
final List<String> contextConfig = retrieveContextConfig(
|
||||
context, contextConfigs);
|
||||
|
||||
contextConfig.add(moduleConfig);
|
||||
}
|
||||
|
||||
for (final Map.Entry<String, List<String>> entry : contextConfigs.
|
||||
entrySet()) {
|
||||
addAppConfig(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> retrieveContextConfig(
|
||||
final String context, final Map<String, List<String>> contextConfigs) {
|
||||
List<String> contextConfig = contextConfigs.get(context);
|
||||
|
||||
if (contextConfig == null) {
|
||||
contextConfig = new ArrayList<>();
|
||||
contextConfigs.put(context, contextConfig);
|
||||
}
|
||||
|
||||
return contextConfig;
|
||||
}
|
||||
|
||||
private void addAppConfig(final String name, final List<String> entries) {
|
||||
final AppConfigurationEntry[] configEntries
|
||||
= new AppConfigurationEntry[entries.size()];
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
final List<String> entry = Arrays.asList(entries.get(i).split(":"));
|
||||
configEntries[i] = loadAppConfigEntry(entry);
|
||||
|
||||
}
|
||||
appConfigs.put(name, configEntries);
|
||||
}
|
||||
|
||||
private AppConfigurationEntry loadAppConfigEntry(final List<String> entry) {
|
||||
if (entry.size() < 2) {
|
||||
throw new LoginConfigMalformedException("LoginConfig is malformed.");
|
||||
}
|
||||
|
||||
final String name = entry.get(0);
|
||||
final AppConfigurationEntry.LoginModuleControlFlag flag = parseFlag(
|
||||
entry.get(1));
|
||||
final Map<String, String> options = new HashMap<>();
|
||||
|
||||
if (entry.size() > 2) {
|
||||
for (int i = 2; i < entry.size(); i++) {
|
||||
addOption(entry.get(i), options);
|
||||
}
|
||||
}
|
||||
|
||||
return new AppConfigurationEntry(name, flag, options);
|
||||
}
|
||||
|
||||
private AppConfigurationEntry.LoginModuleControlFlag parseFlag(
|
||||
final String flagStr) {
|
||||
switch (flagStr) {
|
||||
case "REQUISITE":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
|
||||
|
||||
case "REQUIRED":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
|
||||
|
||||
case "SUFFICIENT":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
|
||||
|
||||
case "OPTIONAL":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
|
||||
|
||||
default:
|
||||
throw new LoginConfigMalformedException(String.format(
|
||||
"Unknown flag \"%s\". Valid flags are: REQUISITE, "
|
||||
+ "REQUIRED, SUFFICIENT, OPTIONAL",
|
||||
flagStr));
|
||||
}
|
||||
}
|
||||
|
||||
private void addOption(final String option,
|
||||
final Map<String, String> options) {
|
||||
final int index = option.indexOf('=');
|
||||
if (index == -1) {
|
||||
throw new LoginConfigMalformedException(String.format(
|
||||
"The option string \"%s\" is malformed.", option));
|
||||
}
|
||||
|
||||
final String key = option.substring(0, index);
|
||||
final String value = option.substring(index + 1);
|
||||
options.put(key, value);
|
||||
}
|
||||
|
||||
public static void foo(final LoginConfig config) {
|
||||
if (config.appConfigs.get("foo") != null) {
|
||||
config.appConfigs.remove("foo");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient constructor taking an arrays of strings containing the
|
||||
* configuration. Internally this constructor converts the array to a list
|
||||
* and calls {@link #LoginConfig(java.util.List)}.
|
||||
*
|
||||
* @param config The configuration in string form.
|
||||
*
|
||||
* @see #LoginConfig(java.util.List)
|
||||
*/
|
||||
public LoginConfig(final String[] config) {
|
||||
this(Arrays.asList(config));
|
||||
LoginConfig(final Map<String, AppConfigurationEntry[]> appConfigs) {
|
||||
this.appConfigs = appConfigs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Copyright (C) 2015 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.core.authentication;
|
||||
|
||||
import com.arsdigita.kernel.security.SecurityConfig;
|
||||
import com.arsdigita.runtime.ConfigError;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
/**
|
||||
* Creates a {@link LoginConfig} instance from an array or an list of strings.
|
||||
*
|
||||
* The process of creating a {@link LoginConfig} instance from the string values
|
||||
* like those stored in the {@link SecurityConfig} is a complex process.
|
||||
* Originally the constructor of the
|
||||
* {@code com.arsdigita.kernel.security.LoginConfig} class has done all this
|
||||
* work. Some parts were outsourced to private methods.
|
||||
*
|
||||
* The problem with this approach is that several of this method may throw an
|
||||
* exception/error. But throwing exceptions from a constructor is considered a
|
||||
* bad practise. Also the private support methods made the original
|
||||
* {@code LoginConfig} class quite big and complex. Therefore the code has been
|
||||
* split up. The construction process is now done by this class which creates
|
||||
* {@code LoginConfig} from a provided list or array of strings. The strings
|
||||
* must be in the correct format:
|
||||
*
|
||||
* <pre>
|
||||
* context:moduleName:controlFlag[:option1[:option2[:...]]]
|
||||
* </pre>
|
||||
*
|
||||
* <dl>
|
||||
* <dt>{@code context}</dt>
|
||||
* <dd>String</dd>
|
||||
*
|
||||
* <dt>{@code moduleName}</dt>
|
||||
* <dd>Fully qualified class name of a {@link LoginModule}.</dd>
|
||||
*
|
||||
* <dt>{@code controlFlag}</dt>
|
||||
* <dd>
|
||||
* One of the following flags:
|
||||
* <ul>
|
||||
* <li>{@code required}</li>
|
||||
* <li>{@code requisite}</li>
|
||||
* <li>{@code sufficient}</li>
|
||||
* <li>{@code optional}</li>
|
||||
* </ul>
|
||||
* </dd>
|
||||
*
|
||||
* <dt>option</dt>
|
||||
* <dd>Options for the module in the following format: {@code key=value}</dd>
|
||||
* </dl>
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* <pre>
|
||||
* Request:com.arsdigita.kernel.security.CredentialLoginModule:requisite:debug=true
|
||||
* Register:com.arsdigita.kernel.security.LocalLoginModule:requisite
|
||||
* Register:com.arsdigita.kernel.security.UserIDLoginModule:requisite
|
||||
* Register:com.arsdigita.kernel.security.CredentialLoginModule:optional
|
||||
* </pre>
|
||||
*
|
||||
* The build a {@link LoginConfig} first construct an instance of this class and
|
||||
* pass the string array containing the configuration. For example:
|
||||
*
|
||||
* <pre>
|
||||
* final LoginConfigBuilder loginConfigBuilder =
|
||||
* new LoginConfigBuilder(SecurityConfig.getInstance().getLoginConfig());
|
||||
* </pre>
|
||||
*
|
||||
* Then call the {@link #build()} method which does all the work:
|
||||
*
|
||||
* <pre>
|
||||
* final LoginConfig loginConfig = loginConfigBuilder.build();
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public class LoginConfigBuilder {
|
||||
|
||||
private final transient String[] config;
|
||||
|
||||
/**
|
||||
* Creates a new {@code LoginConfigBuilder} for the provided configuration.
|
||||
*
|
||||
* @param config The configuration for which an {@link LoginConfig} should
|
||||
* be created, as array of strings as provided by
|
||||
* {@link SecurityConfig#getLoginConfig()}.
|
||||
*/
|
||||
public LoginConfigBuilder(final String[] config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link LoginConfig} from {@link #config}.
|
||||
*
|
||||
* If one entry of the {@link #config} is malformed a {@link ConfigError}
|
||||
* will be thrown.
|
||||
*
|
||||
* @return A {@link LoginConfig} object.
|
||||
*/
|
||||
public LoginConfig build() {
|
||||
//Temporary storage for the data extracted from the config string array.
|
||||
final Map<String, List<String>> contextConfigs = new HashMap<>();
|
||||
|
||||
//Parse the tuples in the config string array.
|
||||
for (final String tuple : config) {
|
||||
//Find the index of the first ':'.
|
||||
final int index = tuple.indexOf(':');
|
||||
//Extract context and module config parts from the tuple.
|
||||
final String context = tuple.substring(0, index);
|
||||
final String moduleConfig = tuple.substring(index + 1);
|
||||
|
||||
//Put them in the list for the context.
|
||||
final List<String> contextConfig = retrieveContextConfig(
|
||||
context, contextConfigs);
|
||||
contextConfig.add(moduleConfig);
|
||||
|
||||
}
|
||||
|
||||
//Create the map of AppConfigurationEntry objects.
|
||||
final Map<String, AppConfigurationEntry[]> appConfigs = new HashMap<>();
|
||||
for (final Map.Entry<String, List<String>> entry : contextConfigs
|
||||
.entrySet()) {
|
||||
//Add the config entry. The helper method called creates the
|
||||
//AppConfigurationEntry object from the string value.
|
||||
addAppConfig(appConfigs, entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
//Create the LoginConfig object with the Map of AppConfigurationEntries.
|
||||
return new LoginConfig(appConfigs);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for retrieving a list for specific context from the context
|
||||
* maps. Used by {@link #build()}. If the map has no entry with the provided
|
||||
* name a new list is created a put into the list.
|
||||
*
|
||||
* @param context The name of the context, used as key in the
|
||||
* provided map.
|
||||
* @param contextConfigs The map of context configs.
|
||||
*
|
||||
* @return The context configs list for the provided context.
|
||||
*/
|
||||
private List<String> retrieveContextConfig(
|
||||
final String context, final Map<String, List<String>> contextConfigs) {
|
||||
List<String> contextConfig = contextConfigs.get(context);
|
||||
|
||||
if (contextConfig == null) {
|
||||
contextConfig = new ArrayList<>();
|
||||
contextConfigs.put(context, contextConfig);
|
||||
}
|
||||
|
||||
return contextConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for creating an {@link AppConfigurationEntry} object from a
|
||||
* string.
|
||||
*
|
||||
* @param appConfigs The map of {@link AppConfigurationEntry} objects in
|
||||
* which the created entry will be stored.
|
||||
* @param name The name of the context for which the
|
||||
* {@link AppConfigurationEntry} is created.
|
||||
* @param entries The list of configuration entries to parse.
|
||||
*/
|
||||
private void addAppConfig(
|
||||
final Map<String, AppConfigurationEntry[]> appConfigs,
|
||||
final String name,
|
||||
final List<String> entries) {
|
||||
|
||||
//Map containing the parsed entries
|
||||
final AppConfigurationEntry[] configEntries
|
||||
= new AppConfigurationEntry[entries
|
||||
.size()];
|
||||
|
||||
//Parse all entries. We use a "traditional" for loop here because we
|
||||
//need the position in the array.
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
//Load the current configuration entry.
|
||||
configEntries[i] = loadAppConfigEntry(entries.get(i));
|
||||
}
|
||||
|
||||
//Put the parsed entires into the map
|
||||
appConfigs.put(name, configEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for parsing a single configuration entry. The
|
||||
*
|
||||
* tokens entry
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private AppConfigurationEntry loadAppConfigEntry(final String entry) {
|
||||
//Split the string tokens. The tokens are limited by the ':' character.
|
||||
final String[] tokens = entry.split(":");
|
||||
|
||||
//If there less then two tokens the entry is malformed and we throw an
|
||||
//ConfigError.
|
||||
if (tokens.length < 2) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (final String str : tokens) {
|
||||
builder.append(str);
|
||||
}
|
||||
throw new ConfigError(String.format(
|
||||
"Malformed SecurityConfig entry: %s", builder.toString()));
|
||||
}
|
||||
|
||||
//Extract the name of the configured module (the first token)
|
||||
final String name = tokens[0];
|
||||
//Extract the flat (second token)
|
||||
final AppConfigurationEntry.LoginModuleControlFlag flag = parseFlag(
|
||||
tokens[1]);
|
||||
|
||||
//Extract the provided options if any
|
||||
final Map<String, String> options = new HashMap<>();
|
||||
if (tokens.length > 2) {
|
||||
for (int i = 2; i < tokens.length; i++) {
|
||||
//The the option to the map of options.
|
||||
addOption(tokens[i], options);
|
||||
}
|
||||
}
|
||||
|
||||
//Create an AppConfguration using the extracted data.
|
||||
return new AppConfigurationEntry(name, flag, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert a string to a
|
||||
* {@link AppConfigurationEntry.LoginModuleControlFlag}. If the provided
|
||||
* string is not a valid flag a {@link ConfigError} is thrown.
|
||||
*
|
||||
* @param flag The string to convert.
|
||||
*
|
||||
* @return {@link AppConfigurationEntry.LoginModuleControlFlag} instance.
|
||||
*/
|
||||
private AppConfigurationEntry.LoginModuleControlFlag parseFlag(
|
||||
final String flag) {
|
||||
switch (flag.toUpperCase()) {
|
||||
case "REQUISITE":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
|
||||
|
||||
case "REQUIRED":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
|
||||
|
||||
case "SUFFICIENT":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
|
||||
|
||||
case "OPTIONAL":
|
||||
return AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
|
||||
|
||||
default:
|
||||
throw new ConfigError(String.format(
|
||||
"Unknown flag \"%s\". Valid flags are: REQUISITE, "
|
||||
+ "REQUIRED, SUFFICIENT, OPTIONAL",
|
||||
flag));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for extracting the key and value parts from an module
|
||||
* option string. If the option string is malformed an {@link ConfigError}
|
||||
* is thrown.
|
||||
*
|
||||
* @param option The option string to parse.
|
||||
* @param options The map of options to which the parsed option we be added.
|
||||
*/
|
||||
private void addOption(final String option,
|
||||
final Map<String, String> options) {
|
||||
//Find the index of the '=' character.
|
||||
final int index = option.indexOf('=');
|
||||
//If there is no '=' in the string the option string is invalid
|
||||
if (index == -1) {
|
||||
throw new ConfigError(String.format(
|
||||
"The option string \"%s\" is malformed.", option));
|
||||
}
|
||||
|
||||
//Extract key and value an put them into the options map.
|
||||
final String key = option.substring(0, index);
|
||||
final String value = option.substring(index + 1);
|
||||
options.put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.core.authentication;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public class LoginConfigMalformedException extends RuntimeException {
|
||||
private static final long serialVersionUID = 8573796343232732323L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>NewException</code> without detail
|
||||
* message.
|
||||
*/
|
||||
public LoginConfigMalformedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>NewException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public LoginConfigMalformedException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -53,13 +53,13 @@ public class LoginManager {
|
|||
/**
|
||||
* Name of the register login context.
|
||||
*/
|
||||
private static final String REGISTER_LOGIN_CONTEXT = "RegisterLoginContext";
|
||||
private static final String REGISTER_LOGIN_CONTEXT = "Register";
|
||||
|
||||
@Inject
|
||||
private transient CcmSessionContext sessionContext;
|
||||
|
||||
@Inject
|
||||
private transient UserRepository userRepository;
|
||||
// @Inject
|
||||
// private transient UserRepository userRepository;
|
||||
|
||||
public void login(final String username, final String password)
|
||||
throws LoginException {
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ public class SecurityConfigTest {
|
|||
.getPackage())
|
||||
.addPackage(org.libreccm.tests.categories.IntegrationTest.class
|
||||
.getPackage())
|
||||
.addPackage(org.libreccm.core.authentication.LocalLoginModule.class.getPackage())
|
||||
.addAsLibraries(libs)
|
||||
.addAsResource(
|
||||
"configtests/com/arsdigita/kernel/security/SecurityConfigTest/ccm-core.config",
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ public class UserManagerTest {
|
|||
|
||||
return ShrinkWrap
|
||||
.create(WebArchive.class,
|
||||
"LibreCCM-org.libreccm.core.UserRepositoryTest.war")
|
||||
"LibreCCM-org.libreccm.core.UserManagerTest.war")
|
||||
.addPackage(User.class.getPackage())
|
||||
.addPackage(org.libreccm.web.Application.class.getPackage())
|
||||
.addPackage(org.libreccm.categorization.Category.class.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (C) 2015 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.core.authentication;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.junit.Arquillian;
|
||||
import org.jboss.arquillian.junit.InSequence;
|
||||
import org.jboss.arquillian.persistence.PersistenceTest;
|
||||
import org.jboss.arquillian.persistence.UsingDataSet;
|
||||
import org.jboss.arquillian.transaction.api.annotation.TransactionMode;
|
||||
import org.jboss.arquillian.transaction.api.annotation.Transactional;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
|
||||
import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.libreccm.core.CcmObject;
|
||||
import org.libreccm.core.CcmSessionContext;
|
||||
import org.libreccm.core.EmailAddress;
|
||||
import org.libreccm.tests.categories.IntegrationTest;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@Category(IntegrationTest.class)
|
||||
@RunWith(Arquillian.class)
|
||||
@PersistenceTest
|
||||
@Transactional(TransactionMode.COMMIT)
|
||||
public class LoginManagerTest {
|
||||
|
||||
@Inject
|
||||
private transient LoginManager loginManager;
|
||||
|
||||
@Inject
|
||||
private transient CcmSessionContext ccmSessionContext;
|
||||
|
||||
public LoginManagerTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
final String[] config = new String[]{
|
||||
String.format("Register:%s:requisite",
|
||||
LocalLoginModule.class.getName())};
|
||||
final LoginConfigBuilder loginConfigBuilder = new LoginConfigBuilder(
|
||||
config);
|
||||
Configuration.setConfiguration(loginConfigBuilder.build());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
@Deployment
|
||||
public static WebArchive createDeployment() {
|
||||
final PomEquippedResolveStage pom = Maven
|
||||
.resolver()
|
||||
.loadPomFromFile("pom.xml");
|
||||
final PomEquippedResolveStage dependencies = pom
|
||||
.importCompileAndRuntimeDependencies();
|
||||
final File[] libs = dependencies.resolve().withTransitivity().asFile();
|
||||
|
||||
for (File lib : libs) {
|
||||
System.err.printf("Adding file '%s' to test archive...%n",
|
||||
lib.getName());
|
||||
}
|
||||
|
||||
return ShrinkWrap
|
||||
.create(WebArchive.class,
|
||||
"LibreCCM-org.libreccm.core.authentication.LoginManagerTest.war")
|
||||
.addPackage(CcmObject.class.getPackage())
|
||||
.addPackage(org.libreccm.web.Application.class.getPackage())
|
||||
.addPackage(org.libreccm.categorization.Category.class.
|
||||
getPackage())
|
||||
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
|
||||
addPackage(org.libreccm.jpa.EntityManagerProducer.class
|
||||
.getPackage())
|
||||
.addPackage(org.libreccm.jpa.utils.MimeTypeConverter.class
|
||||
.getPackage())
|
||||
.addPackage(org.libreccm.testutils.EqualsVerifier.class.
|
||||
getPackage())
|
||||
.addPackage(org.libreccm.core.authentication.LoginManager.class
|
||||
.getPackage())
|
||||
.addPackage(com.arsdigita.kernel.KernelConfig.class.getPackage())
|
||||
.addPackage(com.arsdigita.runtime.AbstractConfig.class.getPackage())
|
||||
.addPackage(com.arsdigita.util.parameter.AbstractParameter.class
|
||||
.getPackage())
|
||||
.addPackage(com.arsdigita.util.UncheckedWrapperException.class
|
||||
.getPackage())
|
||||
.addPackage(org.libreccm.tests.categories.IntegrationTest.class
|
||||
.getPackage())
|
||||
.addPackage(com.arsdigita.web.CCMApplicationContextListener.class
|
||||
.getPackage())
|
||||
.addAsLibraries(libs)
|
||||
.addAsResource("test-persistence.xml",
|
||||
"META-INF/persistence.xml")
|
||||
.addAsResource(
|
||||
"configtests/com/arsdigita/kernel/KernelConfigTest/ccm-core.config",
|
||||
"ccm-core.config")
|
||||
.addAsWebInfResource(
|
||||
"configtests/com/arsdigita/kernel/KernelConfigTest/registry.properties",
|
||||
"conf/registry/registry.properties")
|
||||
.addAsWebInfResource(
|
||||
"configtests/com/arsdigita/kernel/KernelConfigTest/kernel.properties",
|
||||
"conf/registry/ccm-core/kernel.properties")
|
||||
.addAsResource(
|
||||
"com/arsdigita/kernel/KernelConfig_parameter.properties",
|
||||
"com/arsdigita/kernel/KernelConfig_parameter.properties")
|
||||
.addAsWebInfResource("test-web.xml", "WEB-INF/web.xml")
|
||||
.addAsWebInfResource(EmptyAsset.INSTANCE, "WEB-INF/beans.xml");
|
||||
}
|
||||
|
||||
@InSequence(1)
|
||||
public void isLoginManagerInjected() {
|
||||
assertThat(loginManager, is(not(nullValue())));
|
||||
}
|
||||
|
||||
@InSequence(2)
|
||||
public void isCcmSessionContextInjected() {
|
||||
assertThat(ccmSessionContext, is(not(nullValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(10)
|
||||
public void loginValidCredentials() throws LoginException {
|
||||
loginManager.login("jdoe@example.com", "correct-pw");
|
||||
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(not(nullValue())));
|
||||
final EmailAddress emailAddress = new EmailAddress();
|
||||
emailAddress.setAddress("jdoe@example.org");
|
||||
emailAddress.setBouncing(false);
|
||||
emailAddress.setVerified(true);
|
||||
assertThat(ccmSessionContext.getCurrentParty().getEmailAddresses(),
|
||||
contains(equalTo(emailAddress)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(20)
|
||||
public void loginWrongCredentials() throws LoginException {
|
||||
try {
|
||||
loginManager.login("jdoe@example.com", "wrong-pw");
|
||||
} catch (LoginException ex) {
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(nullValue()));
|
||||
}
|
||||
|
||||
fail("No login exception was thrown.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(30)
|
||||
public void loginEmptyPassword() {
|
||||
try {
|
||||
loginManager.login("jdoe@example.com", "");
|
||||
} catch (LoginException ex) {
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(nullValue()));
|
||||
}
|
||||
|
||||
fail("No login exception was thrown.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(40)
|
||||
public void loginEmptyUserName() {
|
||||
try {
|
||||
loginManager.login("", "correct-pw");
|
||||
} catch (LoginException ex) {
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(nullValue()));
|
||||
}
|
||||
|
||||
fail("No login exception was thrown.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(50)
|
||||
public void loginNullPassword() {
|
||||
try {
|
||||
loginManager.login("jdoe@example.com", null);
|
||||
} catch (LoginException ex) {
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(nullValue()));
|
||||
}
|
||||
|
||||
fail("No login exception was thrown.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@UsingDataSet(
|
||||
"datasets/org/libreccm/core/authentication/LoginManagerTest/data.json")
|
||||
@InSequence(60)
|
||||
public void loginNullUsername() {
|
||||
try {
|
||||
loginManager.login(null, "correct-pw");
|
||||
} catch (LoginException ex) {
|
||||
assertThat(ccmSessionContext.getCurrentParty(), is(nullValue()));
|
||||
}
|
||||
|
||||
fail("No login exception was thrown.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"subjects":
|
||||
[
|
||||
{
|
||||
"subject_id": -10
|
||||
}
|
||||
],
|
||||
"subject_email_addresses":
|
||||
[
|
||||
{
|
||||
"subject_id": -10,
|
||||
"email_address": "jdoe@example.com",
|
||||
"bouncing": false,
|
||||
"verified": true
|
||||
}
|
||||
],
|
||||
"ccm_users":
|
||||
[
|
||||
{
|
||||
"banned": false,
|
||||
"hash_algorithm": "SHA-512",
|
||||
"family_name": "Doe",
|
||||
"given_name": "John",
|
||||
"password": "abc8f796612f5c5b5d0c89cf86ea3b8d1c7d15f9c06851f85708013b0248b2e6d9b315b48f586168fe6cc29296e5a9090a5aab14a85b3ffd0633ca8ccc587a09",
|
||||
"salt": "fiagaifa",
|
||||
"screen_name": "jdoe",
|
||||
"subject_id": -10
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue