ARTEMIS-299 file-based login module performance
This is based on improvements made in ActiveMQ 5.x. See more at https://issues.apache.org/jira/browse/AMQ-5876.
This commit is contained in:
parent
08e5245c0c
commit
ae70d7178e
|
@ -37,7 +37,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
|||
* Allows for subclasses to define methods used to verify user certificates and
|
||||
* find user roles. Uses CertificateCallbacks to retrieve certificates.
|
||||
*/
|
||||
public abstract class CertificateLoginModule implements LoginModule {
|
||||
public abstract class CertificateLoginModule extends PropertiesLoader implements LoginModule {
|
||||
|
||||
private CallbackHandler callbackHandler;
|
||||
private Subject subject;
|
||||
|
@ -46,7 +46,6 @@ public abstract class CertificateLoginModule implements LoginModule {
|
|||
private String username;
|
||||
private Set<String> roles;
|
||||
private Set<Principal> principals = new HashSet<Principal>();
|
||||
private boolean debug;
|
||||
|
||||
/**
|
||||
* Overriding to allow for proper initialization. Standard JAAS.
|
||||
|
@ -56,11 +55,7 @@ public abstract class CertificateLoginModule implements LoginModule {
|
|||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
|
||||
debug = "true".equalsIgnoreCase((String) options.get("debug"));
|
||||
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Initialized debug");
|
||||
}
|
||||
init(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
|
||||
class PrincipalProperties {
|
||||
|
||||
private final Properties principals;
|
||||
private final long reloadTime;
|
||||
|
||||
PrincipalProperties(final String type, final File source, final ActiveMQServerLogger log) {
|
||||
Properties props = new Properties();
|
||||
long reloadTime = 0;
|
||||
try {
|
||||
load(source, props);
|
||||
reloadTime = System.currentTimeMillis();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
log.warn("Unable to load " + type + " properties file " + source);
|
||||
}
|
||||
this.reloadTime = reloadTime;
|
||||
this.principals = props;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
Set<Map.Entry<String, String>> entries() {
|
||||
return (Set) principals.entrySet();
|
||||
}
|
||||
|
||||
String getProperty(String name) {
|
||||
return principals.getProperty(name);
|
||||
}
|
||||
|
||||
long getReloadTime() {
|
||||
return reloadTime;
|
||||
}
|
||||
|
||||
private void load(final File source, Properties props) throws FileNotFoundException, IOException {
|
||||
FileInputStream in = new FileInputStream(source);
|
||||
try {
|
||||
props.load(in);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
Properties getPrincipals() {
|
||||
return principals;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
|
||||
public class PropertiesLoader {
|
||||
|
||||
static Map<FileNameKey, ReloadableProperties> staticCache = new HashMap<FileNameKey, ReloadableProperties>();
|
||||
protected boolean debug;
|
||||
|
||||
public void init(Map options) {
|
||||
debug = booleanOption("debug", options);
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Initialized debug");
|
||||
}
|
||||
}
|
||||
|
||||
public ReloadableProperties load(String nameProperty, String fallbackName, Map options) {
|
||||
ReloadableProperties result;
|
||||
FileNameKey key = new FileNameKey(nameProperty, fallbackName, options);
|
||||
key.setDebug(debug);
|
||||
|
||||
synchronized (staticCache) {
|
||||
result = staticCache.get(key);
|
||||
if (result == null) {
|
||||
result = new ReloadableProperties(key);
|
||||
staticCache.put(key, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result.obtained();
|
||||
}
|
||||
|
||||
private static boolean booleanOption(String name, Map options) {
|
||||
return Boolean.parseBoolean((String) options.get(name));
|
||||
}
|
||||
|
||||
public class FileNameKey {
|
||||
|
||||
final File file;
|
||||
final String absPath;
|
||||
final boolean reload;
|
||||
private boolean decrypt;
|
||||
private boolean debug;
|
||||
|
||||
public FileNameKey(String nameProperty, String fallbackName, Map options) {
|
||||
this.file = new File(baseDir(options), stringOption(nameProperty, fallbackName, options));
|
||||
absPath = file.getAbsolutePath();
|
||||
reload = booleanOption("reload", options);
|
||||
decrypt = booleanOption("decrypt", options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof FileNameKey && this.absPath.equals(((FileNameKey) other).absPath);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.absPath.hashCode();
|
||||
}
|
||||
|
||||
public boolean isReload() {
|
||||
return reload;
|
||||
}
|
||||
|
||||
public File file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public boolean isDecrypt() {
|
||||
return decrypt;
|
||||
}
|
||||
|
||||
public void setDecrypt(boolean decrypt) {
|
||||
this.decrypt = decrypt;
|
||||
}
|
||||
|
||||
private String stringOption(String key, String nameDefault, Map options) {
|
||||
Object result = options.get(key);
|
||||
return result != null ? result.toString() : nameDefault;
|
||||
}
|
||||
|
||||
private File baseDir(Map options) {
|
||||
File baseDir = null;
|
||||
if (options.get("baseDir") != null) {
|
||||
baseDir = new File((String) options.get("baseDir"));
|
||||
}
|
||||
else {
|
||||
if (System.getProperty("java.security.auth.login.config") != null) {
|
||||
baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Using basedir=" + baseDir.getAbsolutePath());
|
||||
}
|
||||
return baseDir;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "PropsFile=" + absPath;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For test-usage only.
|
||||
*/
|
||||
public static void resetUsersAndGroupsCache() {
|
||||
staticCache.clear();
|
||||
}
|
||||
}
|
|
@ -25,87 +25,41 @@ import javax.security.auth.callback.UnsupportedCallbackException;
|
|||
import javax.security.auth.login.FailedLoginException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
|
||||
public class PropertiesLoginModule implements LoginModule {
|
||||
public class PropertiesLoginModule extends PropertiesLoader implements LoginModule {
|
||||
|
||||
private static final String USER_FILE = "org.apache.activemq.jaas.properties.user";
|
||||
private static final String GROUP_FILE = "org.apache.activemq.jaas.properties.role";
|
||||
private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user";
|
||||
private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.role";
|
||||
|
||||
private Subject subject;
|
||||
private CallbackHandler callbackHandler;
|
||||
|
||||
private boolean debug;
|
||||
private boolean reload = false;
|
||||
private static volatile PrincipalProperties users;
|
||||
private static volatile PrincipalProperties roles;
|
||||
private Properties users;
|
||||
private Properties roles;
|
||||
private String user;
|
||||
private final Set<Principal> principals = new HashSet<Principal>();
|
||||
private File baseDir;
|
||||
private boolean loginSucceeded;
|
||||
// private boolean decrypt = true;
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
Map sharedState,
|
||||
Map options) {
|
||||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
loginSucceeded = false;
|
||||
|
||||
debug = "true".equalsIgnoreCase((String) options.get("debug"));
|
||||
if (options.get("reload") != null) {
|
||||
reload = "true".equalsIgnoreCase((String) options.get("reload"));
|
||||
}
|
||||
|
||||
if (options.get("baseDir") != null) {
|
||||
baseDir = new File((String) options.get("baseDir"));
|
||||
}
|
||||
|
||||
setBaseDir();
|
||||
String usersFile = options.get(USER_FILE) + "";
|
||||
File uf = baseDir != null ? new File(baseDir, usersFile) : new File(usersFile);
|
||||
|
||||
if (reload || users == null || uf.lastModified() > users.getReloadTime()) {
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Reloading users from " + uf.getAbsolutePath());
|
||||
}
|
||||
users = new PrincipalProperties("user", uf, ActiveMQServerLogger.LOGGER);
|
||||
// if( decrypt ) {
|
||||
// try {
|
||||
// EncryptionSupport.decrypt(users.getPrincipals());
|
||||
// } catch(NoClassDefFoundError e) {
|
||||
// // this Happens whe jasypt is not on the classpath..
|
||||
// decrypt = false;
|
||||
// ActiveMQServerLogger.LOGGER.info("jasypt is not on the classpath: password decryption disabled.");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
String groupsFile = options.get(GROUP_FILE) + "";
|
||||
File gf = baseDir != null ? new File(baseDir, groupsFile) : new File(groupsFile);
|
||||
if (reload || roles == null || gf.lastModified() > roles.getReloadTime()) {
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Reloading roles from " + gf.getAbsolutePath());
|
||||
}
|
||||
roles = new PrincipalProperties("role", gf, ActiveMQServerLogger.LOGGER);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBaseDir() {
|
||||
if (baseDir == null) {
|
||||
if (System.getProperty("java.security.auth.login.config") != null) {
|
||||
baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Using basedir=" + baseDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
init(options);
|
||||
users = load(USER_FILE_PROP_NAME, "user", options).getProps();
|
||||
roles = load(ROLE_FILE_PROP_NAME, "role", options).getProps();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,12 +107,12 @@ public class PropertiesLoginModule implements LoginModule {
|
|||
if (result) {
|
||||
principals.add(new UserPrincipal(user));
|
||||
|
||||
for (Map.Entry<String, String> entry : roles.entries()) {
|
||||
String name = entry.getKey();
|
||||
for (Map.Entry<Object, Object> entry : roles.entrySet()) {
|
||||
String name = (String) entry.getKey();
|
||||
String[] userList = ((String) entry.getValue()).split(",");
|
||||
if (debug) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Inspecting role '" + name + "' with user(s): " + entry.getValue());
|
||||
}
|
||||
String[] userList = entry.getValue().split(",");
|
||||
for (int i = 0; i < userList.length; i++) {
|
||||
if (user.equals(userList[i])) {
|
||||
principals.add(new RolePrincipal(name));
|
||||
|
@ -204,12 +158,4 @@ public class PropertiesLoginModule implements LoginModule {
|
|||
user = null;
|
||||
loginSucceeded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For test-usage only.
|
||||
*/
|
||||
public static void resetUsersAndGroupsCache() {
|
||||
users = null;
|
||||
roles = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
|
||||
public class ReloadableProperties {
|
||||
|
||||
private Properties props = new Properties();
|
||||
private Map<String, String> invertedProps;
|
||||
private long reloadTime = -1;
|
||||
private final PropertiesLoader.FileNameKey key;
|
||||
|
||||
public ReloadableProperties(PropertiesLoader.FileNameKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public synchronized Properties getProps() {
|
||||
return props;
|
||||
}
|
||||
|
||||
public synchronized ReloadableProperties obtained() {
|
||||
if (reloadTime < 0 || (key.isReload() && hasModificationAfter(reloadTime))) {
|
||||
props = new Properties();
|
||||
try {
|
||||
load(key.file(), props);
|
||||
invertedProps = null;
|
||||
if (key.isDebug()) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Load of: " + key);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
ActiveMQServerLogger.LOGGER.error("Failed to load: " + key + ", reason:" + e.getLocalizedMessage());
|
||||
if (key.isDebug()) {
|
||||
ActiveMQServerLogger.LOGGER.debug("Load of: " + key + ", failure exception" + e);
|
||||
}
|
||||
}
|
||||
reloadTime = System.currentTimeMillis();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized Map<String, String> invertedPropertiesMap() {
|
||||
if (invertedProps == null) {
|
||||
invertedProps = new HashMap<>(props.size());
|
||||
for (Map.Entry<Object, Object> val : props.entrySet()) {
|
||||
invertedProps.put((String) val.getValue(), (String) val.getKey());
|
||||
}
|
||||
}
|
||||
return invertedProps;
|
||||
}
|
||||
|
||||
private void load(final File source,
|
||||
Properties props) throws IOException {
|
||||
FileInputStream in = new FileInputStream(source);
|
||||
try {
|
||||
props.load(in);
|
||||
// if (key.isDecrypt()) {
|
||||
// try {
|
||||
// EncryptionSupport.decrypt(this.props);
|
||||
// } catch (NoClassDefFoundError e) {
|
||||
// // this Happens whe jasypt is not on the classpath..
|
||||
// key.setDecrypt(false);
|
||||
// ActiveMQServerLogger.LOGGER.info("jasypt is not on the classpath: password decryption disabled.");
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasModificationAfter(long reloadTime) {
|
||||
return key.file.lastModified() > reloadTime;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,8 +20,6 @@ import javax.security.auth.Subject;
|
|||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.cert.X509Certificate;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -37,16 +35,15 @@ import java.util.Set;
|
|||
* org.apache.activemq.jaas.textfiledn.user and
|
||||
* org.apache.activemq.jaas.textfiledn.role properties respectively. NOTE: This
|
||||
* class will re-read user and group files for every authentication (i.e it does
|
||||
* live updates of allowed groups and users).
|
||||
* live updates of allowed roles and users).
|
||||
*/
|
||||
public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
||||
|
||||
private static final String USER_FILE = "org.apache.activemq.jaas.textfiledn.user";
|
||||
private static final String ROLE_FILE = "org.apache.activemq.jaas.textfiledn.role";
|
||||
private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.user";
|
||||
private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.role";
|
||||
|
||||
private File baseDir;
|
||||
private String usersFilePathname;
|
||||
private String rolesFilePathname;
|
||||
private Properties roles;
|
||||
private Map<String, String> usersByDn;
|
||||
|
||||
/**
|
||||
* Performs initialization of file paths. A standard JAAS override.
|
||||
|
@ -54,15 +51,8 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
|||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
|
||||
super.initialize(subject, callbackHandler, sharedState, options);
|
||||
if (System.getProperty("java.security.auth.login.config") != null) {
|
||||
baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
|
||||
}
|
||||
else {
|
||||
baseDir = new File(".");
|
||||
}
|
||||
|
||||
usersFilePathname = options.get(USER_FILE) + "";
|
||||
rolesFilePathname = options.get(ROLE_FILE) + "";
|
||||
usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap();
|
||||
roles = load(ROLE_FILE_PROP_NAME, "", options).getProps();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,32 +71,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
|||
throw new LoginException("Client certificates not found. Cannot authenticate.");
|
||||
}
|
||||
|
||||
File usersFile = new File(baseDir, usersFilePathname);
|
||||
|
||||
Properties users = new Properties();
|
||||
|
||||
try {
|
||||
java.io.FileInputStream in = new java.io.FileInputStream(usersFile);
|
||||
users.load(in);
|
||||
in.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new LoginException("Unable to load user properties file " + usersFile);
|
||||
}
|
||||
|
||||
String dn = getDistinguishedName(certs);
|
||||
|
||||
Enumeration<Object> keys = users.keys();
|
||||
for (Enumeration<Object> vals = users.elements(); vals.hasMoreElements(); ) {
|
||||
if (vals.nextElement().equals(dn)) {
|
||||
return (String) keys.nextElement();
|
||||
}
|
||||
else {
|
||||
keys.nextElement();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return usersByDn.get(getDistinguishedName(certs));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,17 +84,6 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
|||
*/
|
||||
@Override
|
||||
protected Set<String> getUserRoles(String username) throws LoginException {
|
||||
File rolesFile = new File(baseDir, rolesFilePathname);
|
||||
|
||||
Properties roles = new Properties();
|
||||
try {
|
||||
java.io.FileInputStream in = new java.io.FileInputStream(rolesFile);
|
||||
roles.load(in);
|
||||
in.close();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new LoginException("Unable to load role properties file " + rolesFile);
|
||||
}
|
||||
Set<String> userRoles = new HashSet<String>();
|
||||
for (Enumeration<Object> enumeration = roles.keys(); enumeration.hasMoreElements(); ) {
|
||||
String groupName = (String) enumeration.nextElement();
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoader;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -126,7 +127,7 @@ public class PropertiesLoginModuleRaceConditionTest {
|
|||
public void after() throws InterruptedException {
|
||||
pool.shutdown();
|
||||
assertTrue(pool.awaitTermination(500, TimeUnit.SECONDS));
|
||||
PropertiesLoginModule.resetUsersAndGroupsCache();
|
||||
PropertiesLoader.resetUsersAndGroupsCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.core.security.jaas;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
import java.math.BigInteger;
|
||||
import java.security.Principal;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
public class StubX509Certificate extends X509Certificate {
|
||||
|
||||
private final Principal id;
|
||||
|
||||
public StubX509Certificate(Principal id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Principal getSubjectDN() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
// --- Stubbed Methods ---
|
||||
public void checkValidity() {
|
||||
}
|
||||
|
||||
public void checkValidity(Date arg0) {
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public BigInteger getSerialNumber() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Principal getIssuerDN() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getNotBefore() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getNotAfter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] getTBSCertificate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSigAlgName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSigAlgOID() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] getSigAlgParams() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean[] getIssuerUniqueID() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean[] getSubjectUniqueID() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean[] getKeyUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getBasicConstraints() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public byte[] getEncoded() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void verify(PublicKey arg0) {
|
||||
}
|
||||
|
||||
public void verify(PublicKey arg0, String arg1) {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasUnsupportedCriticalExtension() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Set getCriticalExtensionOIDs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Set getNonCriticalExtensionOIDs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] getExtensionValue(String arg0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.activemq.artemis.core.security.jaas;
|
||||
|
||||
import javax.management.remote.JMXPrincipal;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.cert.X509Certificate;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.CertificateLoginModule;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoader;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TextFileCertificateLoginModuleTest {
|
||||
|
||||
private static final String CERT_USERS_FILE_SMALL = "cert-users-SMALL.properties";
|
||||
private static final String CERT_USERS_FILE_LARGE = "cert-users-LARGE.properties";
|
||||
private static final String CERT_GROUPS_FILE = "cert-roles.properties";
|
||||
|
||||
private static final int NUMBER_SUBJECTS = 10;
|
||||
|
||||
static {
|
||||
String path = System.getProperty("java.security.auth.login.config");
|
||||
if (path == null) {
|
||||
URL resource = TextFileCertificateLoginModuleTest.class.getClassLoader().getResource("login.config");
|
||||
if (resource != null) {
|
||||
path = resource.getFile();
|
||||
System.setProperty("java.security.auth.login.config", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CertificateLoginModule loginModule;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
loginModule = new TextFileCertificateLoginModule();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
PropertiesLoader.resetUsersAndGroupsCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginWithSMALLUsersFile() throws Exception {
|
||||
loginTest(CERT_USERS_FILE_SMALL, CERT_GROUPS_FILE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginWithLARGEUsersFile() throws Exception {
|
||||
loginTest(CERT_USERS_FILE_LARGE, CERT_GROUPS_FILE);
|
||||
}
|
||||
|
||||
private void loginTest(String usersFiles, String groupsFile) throws LoginException {
|
||||
|
||||
HashMap options = new HashMap<String, String>();
|
||||
options.put("org.apache.activemq.jaas.textfiledn.user", usersFiles);
|
||||
options.put("org.apache.activemq.jaas.textfiledn.role", groupsFile);
|
||||
options.put("reload", "true");
|
||||
|
||||
JaasCallbackHandler[] callbackHandlers = new JaasCallbackHandler[NUMBER_SUBJECTS];
|
||||
Subject[] subjects = new Subject[NUMBER_SUBJECTS];
|
||||
|
||||
for (int i = 0; i < callbackHandlers.length; i++) {
|
||||
callbackHandlers[i] = getJaasCertificateCallbackHandler("DN=TEST_USER_" + (i + 1));
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
for (int outer = 0; outer < 500; outer++) {
|
||||
for (int i = 0; i < NUMBER_SUBJECTS; i++) {
|
||||
Subject subject = doAuthenticate(options, callbackHandlers[i]);
|
||||
subjects[i] = subject;
|
||||
}
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long timeTaken = endTime - startTime;
|
||||
|
||||
for (int i = 0; i < NUMBER_SUBJECTS; i++) {
|
||||
ActiveMQServerLogger.LOGGER.info("subject is: " + subjects[i].getPrincipals().toString());
|
||||
}
|
||||
|
||||
ActiveMQServerLogger.LOGGER.info(usersFiles + ": Time taken is " + timeTaken);
|
||||
|
||||
}
|
||||
|
||||
private JaasCallbackHandler getJaasCertificateCallbackHandler(String user) {
|
||||
JMXPrincipal principal = new JMXPrincipal(user);
|
||||
X509Certificate cert = new StubX509Certificate(principal);
|
||||
return new JaasCallbackHandler(null, null, new X509Certificate[]{cert});
|
||||
}
|
||||
|
||||
private Subject doAuthenticate(HashMap options, JaasCallbackHandler callbackHandler) throws LoginException {
|
||||
Subject mySubject = new Subject();
|
||||
loginModule.initialize(mySubject, callbackHandler, null, options);
|
||||
loginModule.login();
|
||||
loginModule.commit();
|
||||
return mySubject;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
## ---------------------------------------------------------------------------
|
||||
## Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
## contributor license agreements. See the NOTICE file distributed with
|
||||
## this work for additional information regarding copyright ownership.
|
||||
## The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
## (the "License"); you may not use this file except in compliance with
|
||||
## the License. You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
## ---------------------------------------------------------------------------
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,116 @@
|
|||
## ---------------------------------------------------------------------------
|
||||
## Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
## contributor license agreements. See the NOTICE file distributed with
|
||||
## this work for additional information regarding copyright ownership.
|
||||
## The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
## (the "License"); you may not use this file except in compliance with
|
||||
## the License. You may obtain a copy of the License at
|
||||
##
|
||||
## http://www.apache.org/licenses/LICENSE-2.0
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, software
|
||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
## See the License for the specific language governing permissions and
|
||||
## limitations under the License.
|
||||
## ---------------------------------------------------------------------------
|
||||
1CN=TEST0000001, OU=TEST, O=TEST TEST TEST1 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
2CN=TEST0000001, OU=TEST, O=TEST TEST TEST2 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
3CN=TEST0000001, OU=TEST, O=TEST TEST TEST3 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
4CN=TEST0000001, OU=TEST, O=TEST TEST TEST4 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
5CN=TEST0000001, OU=TEST, O=TEST TEST TEST5 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
6CN=TEST0000001, OU=TEST, O=TEST TEST TEST6 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
7CN=TEST0000001, OU=TEST, O=TEST TEST TEST7 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
8CN=TEST0000001, OU=TEST, O=TEST TEST TEST8 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
9CN=TEST0000001, OU=TEST, O=TEST TEST TEST9 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
10CN=TEST0000001, OU=TEST, O=TEST TEST TEST10 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
11CN=TEST0000001, OU=TEST, O=TEST TEST TEST11 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
12CN=TEST0000001, OU=TEST, O=TEST TEST TEST12 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
13CN=TEST0000001, OU=TEST, O=TEST TEST TEST13 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
14CN=TEST0000001, OU=TEST, O=TEST TEST TEST14 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
15CN=TEST0000001, OU=TEST, O=TEST TEST TEST15 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
16CN=TEST0000001, OU=TEST, O=TEST TEST TEST16 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
17CN=TEST0000001, OU=TEST, O=TEST TEST TEST17 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
18CN=TEST0000001, OU=TEST, O=TEST TEST TEST18 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
19CN=TEST0000001, OU=TEST, O=TEST TEST TEST19 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
20CN=TEST0000001, OU=TEST, O=TEST TEST TEST20 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
21CN=TEST0000001, OU=TEST, O=TEST TEST TEST21 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
22CN=TEST0000001, OU=TEST, O=TEST TEST TEST22 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
23CN=TEST0000001, OU=TEST, O=TEST TEST TEST23 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
24CN=TEST0000001, OU=TEST, O=TEST TEST TEST24 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
25CN=TEST0000001, OU=TEST, O=TEST TEST TEST25 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
26CN=TEST0000001, OU=TEST, O=TEST TEST TEST26 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
27CN=TEST0000001, OU=TEST, O=TEST TEST TEST27 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
28CN=TEST0000001, OU=TEST, O=TEST TEST TEST28 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
29CN=TEST0000001, OU=TEST, O=TEST TEST TEST29 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
30CN=TEST0000001, OU=TEST, O=TEST TEST TEST30 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
31CN=TEST0000001, OU=TEST, O=TEST TEST TEST31 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
32CN=TEST0000001, OU=TEST, O=TEST TEST TEST32 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
33CN=TEST0000001, OU=TEST, O=TEST TEST TEST33 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
34CN=TEST0000001, OU=TEST, O=TEST TEST TEST34 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
35CN=TEST0000001, OU=TEST, O=TEST TEST TEST35 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
36CN=TEST0000001, OU=TEST, O=TEST TEST TEST36 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
37CN=TEST0000001, OU=TEST, O=TEST TEST TEST37 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
38CN=TEST0000001, OU=TEST, O=TEST TEST TEST38 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
39CN=TEST0000001, OU=TEST, O=TEST TEST TEST39 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
40CN=TEST0000001, OU=TEST, O=TEST TEST TEST40 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
41CN=TEST0000001, OU=TEST, O=TEST TEST TEST41 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
42CN=TEST0000001, OU=TEST, O=TEST TEST TEST42 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
43CN=TEST0000001, OU=TEST, O=TEST TEST TEST43 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
44CN=TEST0000001, OU=TEST, O=TEST TEST TEST44 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
45CN=TEST0000001, OU=TEST, O=TEST TEST TEST45 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
46CN=TEST0000001, OU=TEST, O=TEST TEST TEST46 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
47CN=TEST0000001, OU=TEST, O=TEST TEST TEST47 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
48CN=TEST0000001, OU=TEST, O=TEST TEST TEST48 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
49CN=TEST0000001, OU=TEST, O=TEST TEST TEST49 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
50CN=TEST0000001, OU=TEST, O=TEST TEST TEST50 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
51CN=TEST0000001, OU=TEST, O=TEST TEST TEST51 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
52CN=TEST0000001, OU=TEST, O=TEST TEST TEST52 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
53CN=TEST0000001, OU=TEST, O=TEST TEST TEST53 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
54CN=TEST0000001, OU=TEST, O=TEST TEST TEST54 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
55CN=TEST0000001, OU=TEST, O=TEST TEST TEST55 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
56CN=TEST0000001, OU=TEST, O=TEST TEST TEST56 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
57CN=TEST0000001, OU=TEST, O=TEST TEST TEST57 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
58CN=TEST0000001, OU=TEST, O=TEST TEST TEST58 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
59CN=TEST0000001, OU=TEST, O=TEST TEST TEST59 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
60CN=TEST0000001, OU=TEST, O=TEST TEST TEST60 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
61CN=TEST0000001, OU=TEST, O=TEST TEST TEST61 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
62CN=TEST0000001, OU=TEST, O=TEST TEST TEST62 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
63CN=TEST0000001, OU=TEST, O=TEST TEST TEST63 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
64CN=TEST0000001, OU=TEST, O=TEST TEST TEST64 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
65CN=TEST0000001, OU=TEST, O=TEST TEST TEST65 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
66CN=TEST0000001, OU=TEST, O=TEST TEST TEST66 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
67CN=TEST0000001, OU=TEST, O=TEST TEST TEST67 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
68CN=TEST0000001, OU=TEST, O=TEST TEST TEST68 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
69CN=TEST0000001, OU=TEST, O=TEST TEST TEST69 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
70CN=TEST0000001, OU=TEST, O=TEST TEST TEST70 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
71CN=TEST0000001, OU=TEST, O=TEST TEST TEST71 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
72CN=TEST0000001, OU=TEST, O=TEST TEST TEST72 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
73CN=TEST0000001, OU=TEST, O=TEST TEST TEST73 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
74CN=TEST0000001, OU=TEST, O=TEST TEST TEST74 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
75CN=TEST0000001, OU=TEST, O=TEST TEST TEST75 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
76CN=TEST0000001, OU=TEST, O=TEST TEST TEST76 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
77CN=TEST0000001, OU=TEST, O=TEST TEST TEST77 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
78CN=TEST0000001, OU=TEST, O=TEST TEST TEST78 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
79CN=TEST0000001, OU=TEST, O=TEST TEST TEST79 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
80CN=TEST0000001, OU=TEST, O=TEST TEST TEST80 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
81CN=TEST0000001, OU=TEST, O=TEST TEST TEST81 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
82CN=TEST0000001, OU=TEST, O=TEST TEST TEST82 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
83CN=TEST0000001, OU=TEST, O=TEST TEST TEST83 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
84CN=TEST0000001, OU=TEST, O=TEST TEST TEST84 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
85CN=TEST0000001, OU=TEST, O=TEST TEST TEST85 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
86CN=TEST0000001, OU=TEST, O=TEST TEST TEST86 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
87CN=TEST0000001, OU=TEST, O=TEST TEST TEST87 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
88CN=TEST0000001, OU=TEST, O=TEST TEST TEST88 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
89CN=TEST0000001, OU=TEST, O=TEST TEST TEST89 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
90CN=TEST0000001, OU=TEST, O=TEST TEST TEST90 TEST TEST TEST TEST TEST TEST, L=TEST, ST=TEST, C=GB
|
||||
10001CN=DN=TEST_USER_1
|
||||
10002CN=DN=TEST_USER_2
|
||||
10003CN=DN=TEST_USER_3
|
||||
10004CN=DN=TEST_USER_4
|
||||
10005CN=DN=TEST_USER_5
|
||||
10006CN=DN=TEST_USER_6
|
||||
10007CN=DN=TEST_USER_7
|
||||
10008CN=DN=TEST_USER_8
|
||||
10009CN=DN=TEST_USER_9
|
||||
10010CN=DN=TEST_USER_10
|
Loading…
Reference in New Issue