mirror of https://github.com/apache/activemq.git
https://issues.apache.org/jira/browse/AMQ-5876 - refactor properties loading such that it can be reused by cert and props login modules. Both loading on start and refreshing if reload=true and lastMod indicates change
This commit is contained in:
parent
6f457d2f5c
commit
59cd018979
|
@ -43,7 +43,7 @@ import org.slf4j.LoggerFactory;
|
||||||
*
|
*
|
||||||
* @author sepandm@gmail.com (Sepand)
|
* @author sepandm@gmail.com (Sepand)
|
||||||
*/
|
*/
|
||||||
public abstract class CertificateLoginModule implements LoginModule {
|
public abstract class CertificateLoginModule extends PropertiesLoader implements LoginModule {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CertificateLoginModule.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CertificateLoginModule.class);
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ public abstract class CertificateLoginModule implements LoginModule {
|
||||||
private String username;
|
private String username;
|
||||||
private Set<String> groups;
|
private Set<String> groups;
|
||||||
private Set<Principal> principals = new HashSet<Principal>();
|
private Set<Principal> principals = new HashSet<Principal>();
|
||||||
private boolean debug;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overriding to allow for proper initialization. Standard JAAS.
|
* Overriding to allow for proper initialization. Standard JAAS.
|
||||||
|
@ -63,12 +62,7 @@ public abstract class CertificateLoginModule implements LoginModule {
|
||||||
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.subject = subject;
|
||||||
this.callbackHandler = callbackHandler;
|
this.callbackHandler = callbackHandler;
|
||||||
|
init(options);
|
||||||
debug = "true".equalsIgnoreCase((String)options.get("debug"));
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
LOG.debug("Initialized debug");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,71 +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.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.slf4j.Logger;
|
|
||||||
|
|
||||||
class PrincipalProperties {
|
|
||||||
private final Properties principals;
|
|
||||||
private final long reloadTime;
|
|
||||||
|
|
||||||
PrincipalProperties(final String type, final File source, final Logger log) {
|
|
||||||
Properties props = new Properties();
|
|
||||||
long reloadTime = 0;
|
|
||||||
try {
|
|
||||||
load(source, props);
|
|
||||||
reloadTime = System.currentTimeMillis();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
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,135 @@
|
||||||
|
/**
|
||||||
|
* 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.jaas;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class PropertiesLoader {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoader.class);
|
||||||
|
static Map<FileNameKey, ReloadableProperties> staticCache = new HashMap<FileNameKey, ReloadableProperties>();
|
||||||
|
protected boolean debug;
|
||||||
|
|
||||||
|
public void init(Map options) {
|
||||||
|
debug = booleanOption("debug", options);
|
||||||
|
if (debug) {
|
||||||
|
LOG.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) {
|
||||||
|
LOG.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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,11 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.jaas;
|
package org.apache.activemq.jaas;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
@ -36,80 +36,30 @@ import javax.security.auth.spi.LoginModule;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
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 USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user";
|
||||||
private static final String GROUP_FILE = "org.apache.activemq.jaas.properties.group";
|
private static final String GROUP_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.group";
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoginModule.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoginModule.class);
|
||||||
|
|
||||||
private Subject subject;
|
private Subject subject;
|
||||||
private CallbackHandler callbackHandler;
|
private CallbackHandler callbackHandler;
|
||||||
|
|
||||||
private boolean debug;
|
private Properties users;
|
||||||
private boolean reload = false;
|
private Properties groups;
|
||||||
private static volatile PrincipalProperties users;
|
|
||||||
private static volatile PrincipalProperties groups;
|
|
||||||
private String user;
|
private String user;
|
||||||
private final Set<Principal> principals = new HashSet<Principal>();
|
private final Set<Principal> principals = new HashSet<Principal>();
|
||||||
private File baseDir;
|
|
||||||
private boolean loginSucceeded;
|
private boolean loginSucceeded;
|
||||||
private boolean decrypt = true;
|
|
||||||
|
|
||||||
@Override
|
@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.subject = subject;
|
||||||
this.callbackHandler = callbackHandler;
|
this.callbackHandler = callbackHandler;
|
||||||
loginSucceeded = false;
|
loginSucceeded = false;
|
||||||
|
init(options);
|
||||||
debug = "true".equalsIgnoreCase((String) options.get("debug"));
|
users = load(USER_FILE_PROP_NAME, "user", options).getProps();
|
||||||
if (options.get("reload") != null) {
|
groups = load(GROUP_FILE_PROP_NAME, "group", options).getProps();
|
||||||
reload = "true".equalsIgnoreCase((String) options.get("reload"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.get("baseDir") != null) {
|
|
||||||
baseDir = new File((String) options.get("baseDir"));
|
|
||||||
}
|
|
||||||
|
|
||||||
setBaseDir();
|
|
||||||
String usersFile = (String) 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) {
|
|
||||||
LOG.debug("Reloading users from " + uf.getAbsolutePath());
|
|
||||||
}
|
|
||||||
users = new PrincipalProperties("user", uf, LOG);
|
|
||||||
if( decrypt ) {
|
|
||||||
try {
|
|
||||||
EncryptionSupport.decrypt(users.getPrincipals());
|
|
||||||
} catch(NoClassDefFoundError e) {
|
|
||||||
// this Happens whe jasypt is not on the classpath..
|
|
||||||
decrypt = false;
|
|
||||||
LOG.info("jasypt is not on the classpath: password decryption disabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String groupsFile = (String) options.get(GROUP_FILE) + "";
|
|
||||||
File gf = baseDir != null ? new File(baseDir, groupsFile) : new File(groupsFile);
|
|
||||||
if (reload || groups == null || gf.lastModified() > groups.getReloadTime()) {
|
|
||||||
if (debug) {
|
|
||||||
LOG.debug("Reloading groups from " + gf.getAbsolutePath());
|
|
||||||
}
|
|
||||||
groups = new PrincipalProperties("group", gf, LOG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
LOG.debug("Using basedir=" + baseDir.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -155,9 +105,9 @@ public class PropertiesLoginModule implements LoginModule {
|
||||||
if (result) {
|
if (result) {
|
||||||
principals.add(new UserPrincipal(user));
|
principals.add(new UserPrincipal(user));
|
||||||
|
|
||||||
for (Map.Entry<String, String> entry : groups.entries()) {
|
for (Map.Entry<Object, Object> entry : groups.entrySet()) {
|
||||||
String name = entry.getKey();
|
String name = (String) entry.getKey();
|
||||||
String[] userList = entry.getValue().split(",");
|
String[] userList = ((String)entry.getValue()).split(",");
|
||||||
for (int i = 0; i < userList.length; i++) {
|
for (int i = 0; i < userList.length; i++) {
|
||||||
if (user.equals(userList[i])) {
|
if (user.equals(userList[i])) {
|
||||||
principals.add(new GroupPrincipal(name));
|
principals.add(new GroupPrincipal(name));
|
||||||
|
@ -204,11 +154,4 @@ public class PropertiesLoginModule implements LoginModule {
|
||||||
loginSucceeded = false;
|
loginSucceeded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For test-usage only.
|
|
||||||
*/
|
|
||||||
static void resetUsersAndGroupsCache() {
|
|
||||||
users = null;
|
|
||||||
groups = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* 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.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.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class ReloadableProperties {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ReloadableProperties.class);
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
LOG.debug("Load of: " + key);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to load: " + key + ", reason:" + e.getLocalizedMessage());
|
||||||
|
if (key.isDebug()) {
|
||||||
|
LOG.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);
|
||||||
|
LOG.info("jasypt is not on the classpath: password decryption disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasModificationAfter(long reloadTime) {
|
||||||
|
return key.file.lastModified() > reloadTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package org.apache.activemq.jaas;
|
package org.apache.activemq.jaas;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -45,12 +43,11 @@ import javax.security.auth.login.LoginException;
|
||||||
*/
|
*/
|
||||||
public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
||||||
|
|
||||||
private static final String USER_FILE = "org.apache.activemq.jaas.textfiledn.user";
|
private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.user";
|
||||||
private static final String GROUP_FILE = "org.apache.activemq.jaas.textfiledn.group";
|
private static final String GROUP_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.group";
|
||||||
|
|
||||||
private File baseDir;
|
private Properties groups;
|
||||||
private String usersFilePathname;
|
private Map<String, String> usersByDn;
|
||||||
private String groupsFilePathname;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs initialization of file paths. A standard JAAS override.
|
* Performs initialization of file paths. A standard JAAS override.
|
||||||
|
@ -58,15 +55,10 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
|
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
|
||||||
super.initialize(subject, callbackHandler, sharedState, 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 = (String)options.get(USER_FILE) + "";
|
usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap();
|
||||||
groupsFilePathname = (String)options.get(GROUP_FILE) + "";
|
groups = load(GROUP_FILE_PROP_NAME, "", options).getProps();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overriding to allow DN authorization based on DNs specified in text
|
* Overriding to allow DN authorization based on DNs specified in text
|
||||||
|
@ -84,28 +76,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
||||||
throw new LoginException("Client certificates not found. Cannot authenticate.");
|
throw new LoginException("Client certificates not found. Cannot authenticate.");
|
||||||
}
|
}
|
||||||
|
|
||||||
File usersFile = new File(baseDir, usersFilePathname);
|
return usersByDn.get(getDistinguishedName(certs));
|
||||||
|
|
||||||
Properties users = new Properties();
|
|
||||||
|
|
||||||
try(java.io.FileInputStream in = new java.io.FileInputStream(usersFile)) {
|
|
||||||
users.load(in);
|
|
||||||
} 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 (((String)vals.nextElement()).equals(dn)) {
|
|
||||||
return (String)keys.nextElement();
|
|
||||||
} else {
|
|
||||||
keys.nextElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,16 +89,6 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Set<String> getUserGroups(String username) throws LoginException {
|
protected Set<String> getUserGroups(String username) throws LoginException {
|
||||||
File groupsFile = new File(baseDir, groupsFilePathname);
|
|
||||||
|
|
||||||
Properties groups = new Properties();
|
|
||||||
try {
|
|
||||||
java.io.FileInputStream in = new java.io.FileInputStream(groupsFile);
|
|
||||||
groups.load(in);
|
|
||||||
in.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
throw new LoginException("Unable to load group properties file " + groupsFile);
|
|
||||||
}
|
|
||||||
Set<String> userGroups = new HashSet<String>();
|
Set<String> userGroups = new HashSet<String>();
|
||||||
for (Enumeration<Object> enumeration = groups.keys(); enumeration.hasMoreElements();) {
|
for (Enumeration<Object> enumeration = groups.keys(); enumeration.hasMoreElements();) {
|
||||||
String groupName = (String)enumeration.nextElement();
|
String groupName = (String)enumeration.nextElement();
|
||||||
|
|
|
@ -121,7 +121,7 @@ public class PropertiesLoginModuleRaceConditionTest {
|
||||||
public void after() throws InterruptedException {
|
public void after() throws InterruptedException {
|
||||||
pool.shutdown();
|
pool.shutdown();
|
||||||
assertTrue(pool.awaitTermination(500, TimeUnit.SECONDS));
|
assertTrue(pool.awaitTermination(500, TimeUnit.SECONDS));
|
||||||
PropertiesLoginModule.resetUsersAndGroupsCache();
|
PropertiesLoader.resetUsersAndGroupsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/**
|
||||||
|
* 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.security;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import javax.management.remote.JMXPrincipal;
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import javax.security.auth.login.LoginException;
|
||||||
|
import org.apache.activemq.jaas.CertificateLoginModule;
|
||||||
|
import org.apache.activemq.jaas.JaasCertificateCallbackHandler;
|
||||||
|
import org.apache.activemq.jaas.PropertiesLoader;
|
||||||
|
import org.apache.activemq.jaas.TextFileCertificateLoginModule;
|
||||||
|
import org.apache.activemq.transport.tcp.StubX509Certificate;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
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-groups.properties";
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(TextFileCertificateLoginModuleTest.class);
|
||||||
|
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.group", groupsFile);
|
||||||
|
options.put("reload", "true");
|
||||||
|
|
||||||
|
JaasCertificateCallbackHandler[] callbackHandlers = new JaasCertificateCallbackHandler[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++) {
|
||||||
|
LOG.info("subject is: " + subjects[i].getPrincipals().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info(usersFiles + ": Time taken is " + timeTaken);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private JaasCertificateCallbackHandler getJaasCertificateCallbackHandler(String user) {
|
||||||
|
JMXPrincipal principal = new JMXPrincipal(user);
|
||||||
|
X509Certificate cert = new StubX509Certificate(principal);
|
||||||
|
return new JaasCertificateCallbackHandler(new X509Certificate[]{cert});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Subject doAuthenticate(HashMap options, JaasCertificateCallbackHandler callbackHandler) throws LoginException {
|
||||||
|
Subject mySubject = new Subject();
|
||||||
|
loginModule.initialize(mySubject, callbackHandler, null, options);
|
||||||
|
loginModule.login();
|
||||||
|
loginModule.commit();
|
||||||
|
return mySubject;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,100 @@
|
||||||
|
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