Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.event.EventDirContext;
import javax.naming.event.NamespaceChangeListener;
import javax.naming.event.NamingEvent;
import javax.naming.event.NamingExceptionEvent;
import javax.naming.event.ObjectChangeListener;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.apache.activemq.filter.DestinationMapEntry;
import org.apache.activemq.jaas.UserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleCachedLDAPAuthorizationMap implements AuthorizationMap {
private static final Logger LOG = LoggerFactory.getLogger(SimpleCachedLDAPAuthorizationMap.class);
// Configuration Options
private final String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private String connectionURL = "ldap://localhost:1024";
private String connectionUsername = "uid=admin,ou=system";
private String connectionPassword;
private String connectionProtocol = "s";
private String authentication = "simple";
private int queuePrefixLength = 4;
private int topicPrefixLength = 4;
private int tempPrefixLength = 4;
private String queueSearchBase = "ou=Queue,ou=Destination,ou=ActiveMQ,ou=system";
private String topicSearchBase = "ou=Topic,ou=Destination,ou=ActiveMQ,ou=system";
private String tempSearchBase = "ou=Temp,ou=Destination,ou=ActiveMQ,ou=system";
private String permissionGroupMemberAttribute = "member";
private String adminPermissionGroupSearchFilter = "(cn=Admin)";
private String readPermissionGroupSearchFilter = "(cn=Read)";
private String writePermissionGroupSearchFilter = "(cn=Write)";
private boolean legacyGroupMapping = true;
private String groupObjectClass = "groupOfNames";
private String userObjectClass = "person";
private String groupNameAttribute = "cn";
private String userNameAttribute = "uid";
private int refreshInterval = -1;
private boolean refreshDisabled = false;
protected String groupClass = DefaultAuthorizationMap.DEFAULT_GROUP_CLASS;
// Internal State
private long lastUpdated = -1;
private static String ANY_DESCENDANT = "\\$";
protected DirContext context;
private EventDirContext eventContext;
private final AtomicReference map =
new AtomicReference(new DefaultAuthorizationMap());
private final ThreadPoolExecutor updaterService;
protected Map entries =
new ConcurrentHashMap();
public SimpleCachedLDAPAuthorizationMap() {
// Allow for only a couple outstanding update request, they can be slow so we
// don't want a bunch to pile up for no reason.
updaterService = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue(2),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "SimpleCachedLDAPAuthorizationMap update thread");
}
});
updaterService.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
}
protected DirContext createContext() throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
if (connectionUsername != null && !"".equals(connectionUsername)) {
env.put(Context.SECURITY_PRINCIPAL, connectionUsername);
} else {
throw new NamingException("Empty username is not allowed");
}
if (connectionPassword != null && !"".equals(connectionPassword)) {
env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
} else {
throw new NamingException("Empty password is not allowed");
}
env.put(Context.SECURITY_PROTOCOL, connectionProtocol);
env.put(Context.PROVIDER_URL, connectionURL);
env.put(Context.SECURITY_AUTHENTICATION, authentication);
return new InitialDirContext(env);
}
protected boolean isContextAlive() {
boolean alive = false;
if (context != null) {
try {
context.getAttributes("");
alive = true;
} catch (Exception e) {
}
}
return alive;
}
/**
* Returns the existing open context or creates a new one and registers listeners for push notifications if such an
* update style is enabled. This implementation should not be invoked concurrently.
*
* @return the current context
*
* @throws NamingException
* if there is an error setting things up
*/
protected DirContext open() throws NamingException {
if (isContextAlive()) {
return context;
}
try {
context = createContext();
if (refreshInterval == -1 && !refreshDisabled) {
eventContext = ((EventDirContext) context.lookup(""));
final SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
// Listeners for Queue policy //
// Listeners for each type of permission
for (PermissionType permissionType : PermissionType.values()) {
eventContext.addNamingListener(queueSearchBase, getFilterForPermissionType(permissionType), constraints,
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.QUEUE, permissionType));
}
// Listener for changes to the destination pattern entry itself and not a permission entry.
eventContext.addNamingListener(queueSearchBase, "cn=*", new SearchControls(), this.new CachedLDAPAuthorizationMapNamespaceChangeListener(
DestinationType.QUEUE, null));
// Listeners for Topic policy //
// Listeners for each type of permission
for (PermissionType permissionType : PermissionType.values()) {
eventContext.addNamingListener(topicSearchBase, getFilterForPermissionType(permissionType), constraints,
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TOPIC, permissionType));
}
// Listener for changes to the destination pattern entry itself and not a permission entry.
eventContext.addNamingListener(topicSearchBase, "cn=*", new SearchControls(), this.new CachedLDAPAuthorizationMapNamespaceChangeListener(
DestinationType.TOPIC, null));
// Listeners for Temp policy //
// Listeners for each type of permission
for (PermissionType permissionType : PermissionType.values()) {
eventContext.addNamingListener(tempSearchBase, getFilterForPermissionType(permissionType), constraints,
this.new CachedLDAPAuthorizationMapNamespaceChangeListener(DestinationType.TEMP, permissionType));
}
}
} catch (NamingException e) {
context = null;
throw e;
}
return context;
}
/**
* Queries the directory and initializes the policy based on the data in the directory. This implementation should
* not be invoked concurrently.
*
* @throws Exception
* if there is an unrecoverable error processing the directory contents
*/
@SuppressWarnings("rawtypes")
protected synchronized void query() throws Exception {
DirContext currentContext = open();
entries.clear();
final SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
DefaultAuthorizationMap newMap = new DefaultAuthorizationMap();
for (PermissionType permissionType : PermissionType.values()) {
try {
processQueryResults(newMap,
currentContext.search(queueSearchBase, getFilterForPermissionType(permissionType),
constraints), DestinationType.QUEUE, permissionType);
} catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '{}' with filter '{}'", queueSearchBase, getFilterForPermissionType(permissionType), e);
}
}
for (PermissionType permissionType : PermissionType.values()) {
try {
processQueryResults(newMap,
currentContext.search(topicSearchBase, getFilterForPermissionType(permissionType),
constraints), DestinationType.TOPIC, permissionType);
} catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '{}' with filter '{}'", topicSearchBase, getFilterForPermissionType(permissionType), e);
}
}
for (PermissionType permissionType : PermissionType.values()) {
try {
processQueryResults(newMap,
currentContext.search(tempSearchBase, getFilterForPermissionType(permissionType),
constraints), DestinationType.TEMP, permissionType);
} catch (Exception e) {
LOG.error("Policy not applied!. Error processing policy under '{}' with filter '{}'", tempSearchBase, getFilterForPermissionType(permissionType), e);
}
}
// Create and swap in the new instance with updated LDAP data.
newMap.setAuthorizationEntries(new ArrayList(entries.values()));
newMap.setGroupClass(groupClass);
this.map.set(newMap);
updated();
}
/**
* Processes results from a directory query in the context of a given destination type and permission type. This
* implementation should not be invoked concurrently.
*
* @param results
* the results to process
* @param destinationType
* the type of the destination for which the directory results apply
* @param permissionType
* the type of the permission for which the directory results apply
*
* @throws Exception
* if there is an error processing the results
*/
protected void processQueryResults(DefaultAuthorizationMap map, NamingEnumeration results, DestinationType destinationType, PermissionType permissionType)
throws Exception {
while (results.hasMore()) {
SearchResult result = results.next();
AuthorizationEntry entry = null;
try {
entry = getEntry(map, new LdapName(result.getNameInNamespace()), destinationType);
} catch (Exception e) {
LOG.error("Policy not applied! Error parsing authorization policy entry under {}", result.getNameInNamespace(), e);
continue;
}
applyACL(entry, result, permissionType);
}
}
/**
* Marks the time at which the authorization state was last refreshed. Relevant for synchronous
* policy updates. This implementation should not be invoked concurrently.
*/
protected void updated() {
lastUpdated = System.currentTimeMillis();
}
/**
* Retrieves or creates the {@link AuthorizationEntry} that corresponds to the DN in {@code dn}. This implementation
* should not be invoked concurrently.
*
* @param map
* the DefaultAuthorizationMap to operate on.
* @param dn
* the DN representing the policy entry in the directory
* @param destinationType
* the type of the destination to get/create the entry for
*
* @return the corresponding authorization entry for the DN
*
* @throws IllegalArgumentException
* if destination type is not one of {@link DestinationType#QUEUE}, {@link DestinationType#TOPIC},
* {@link DestinationType#TEMP} or if the policy entry DN is malformed
*/
protected AuthorizationEntry getEntry(DefaultAuthorizationMap map, LdapName dn, DestinationType destinationType) {
AuthorizationEntry entry = null;
switch (destinationType) {
case TEMP:
// handle temp entry
if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 1) {
// handle unknown entry
throw new IllegalArgumentException("Malformed policy structure for a temporary destination "
+ "policy entry. The permission group entries should be immediately below the " + "temporary policy base DN.");
}
entry = map.getTempDestinationAuthorizationEntry();
if (entry == null) {
entry = new TempDestinationAuthorizationEntry();
map.setTempDestinationAuthorizationEntry((TempDestinationAuthorizationEntry) entry);
}
break;
case QUEUE:
case TOPIC:
// handle regular destinations
if (dn.size() != getPrefixLengthForDestinationType(destinationType) + 2) {
throw new IllegalArgumentException("Malformed policy structure for a queue or topic destination "
+ "policy entry. The destination pattern and permission group entries should be " + "nested below the queue or topic policy base DN.");
}
ActiveMQDestination dest = formatDestination(dn, destinationType);
if (dest != null) {
entry = entries.get(dest);
if (entry == null) {
entry = new AuthorizationEntry();
entry.setDestination(dest);
entries.put(dest, entry);
}
}
break;
default:
// handle unknown entry
throw new IllegalArgumentException("Unknown destination type " + destinationType);
}
return entry;
}
/**
* Applies the policy from the directory to the given entry within the context of the provided permission type.
*
* @param entry
* the policy entry to apply the policy to
* @param result
* the results from the directory to apply to the policy entry
* @param permissionType
* the permission type of the data in the directory
*
* @throws NamingException
* if there is an error applying the ACL
*/
protected void applyACL(AuthorizationEntry entry, SearchResult result, PermissionType permissionType) throws NamingException {
// Find members
Attribute memberAttribute = result.getAttributes().get(permissionGroupMemberAttribute);
NamingEnumeration> memberAttributeEnum = memberAttribute.getAll();
HashSet