001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.activemq.security;
018    
019    import java.text.MessageFormat;
020    import java.util.HashSet;
021    import java.util.Hashtable;
022    import java.util.Iterator;
023    import java.util.Map;
024    import java.util.Set;
025    
026    import javax.naming.Context;
027    import javax.naming.NamingEnumeration;
028    import javax.naming.NamingException;
029    import javax.naming.directory.Attribute;
030    import javax.naming.directory.Attributes;
031    import javax.naming.directory.DirContext;
032    import javax.naming.directory.InitialDirContext;
033    import javax.naming.directory.SearchControls;
034    import javax.naming.directory.SearchResult;
035    
036    import org.apache.activemq.command.ActiveMQDestination;
037    import org.apache.activemq.jaas.GroupPrincipal;
038    import org.apache.activemq.jaas.LDAPLoginModule;
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    
042    /**
043     * An {@link AuthorizationMap} which uses LDAP
044     * 
045     * @org.apache.xbean.XBean
046     * @author ngcutura
047     */
048    public class LDAPAuthorizationMap implements AuthorizationMap {
049    
050        public static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory";
051        public static final String CONNECTION_URL = "connectionURL";
052        public static final String CONNECTION_USERNAME = "connectionUsername";
053        public static final String CONNECTION_PASSWORD = "connectionPassword";
054        public static final String CONNECTION_PROTOCOL = "connectionProtocol";
055        public static final String AUTHENTICATION = "authentication";
056    
057        public static final String TOPIC_SEARCH_MATCHING = "topicSearchMatching";
058        public static final String TOPIC_SEARCH_SUBTREE = "topicSearchSubtree";
059        public static final String QUEUE_SEARCH_MATCHING = "queueSearchMatching";
060        public static final String QUEUE_SEARCH_SUBTREE = "queueSearchSubtree";
061    
062        public static final String ADMIN_BASE = "adminBase";
063        public static final String ADMIN_ATTRIBUTE = "adminAttribute";
064        public static final String READ_BASE = "readBase";
065        public static final String READ_ATTRIBUTE = "readAttribute";
066        public static final String WRITE_BASE = "writeBAse";
067        public static final String WRITE_ATTRIBUTE = "writeAttribute";
068    
069        private static final Log LOG = LogFactory.getLog(LDAPLoginModule.class);
070    
071        private String initialContextFactory;
072        private String connectionURL;
073        private String connectionUsername;
074        private String connectionPassword;
075        private String connectionProtocol;
076        private String authentication;
077    
078        private DirContext context;
079    
080        private MessageFormat topicSearchMatchingFormat;
081        private MessageFormat queueSearchMatchingFormat;
082    
083        private boolean topicSearchSubtreeBool = true;
084        private boolean queueSearchSubtreeBool = true;
085    
086        private String adminBase;
087        private String adminAttribute;
088        private String readBase;
089        private String readAttribute;
090        private String writeBase;
091        private String writeAttribute;
092    
093        public LDAPAuthorizationMap() {
094            // lets setup some sensible defaults
095            initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
096            connectionURL = "ldap://localhost:10389";
097            connectionUsername = "uid=admin,ou=system";
098            connectionPassword = "secret";
099            connectionProtocol = "s";
100            authentication = "simple";
101    
102            topicSearchMatchingFormat = new MessageFormat("uid={0},ou=topics,ou=destinations,o=ActiveMQ,dc=example,dc=com");
103            queueSearchMatchingFormat = new MessageFormat("uid={0},ou=queues,ou=destinations,o=ActiveMQ,dc=example,dc=com");
104    
105            adminBase = "(cn=admin)";
106            adminAttribute = "uniqueMember";
107            readBase = "(cn=read)";
108            readAttribute = "uniqueMember";
109            writeBase = "(cn=write)";
110            writeAttribute = "uniqueMember";
111        }
112    
113        public LDAPAuthorizationMap(Map options) {
114            initialContextFactory = (String)options.get(INITIAL_CONTEXT_FACTORY);
115            connectionURL = (String)options.get(CONNECTION_URL);
116            connectionUsername = (String)options.get(CONNECTION_USERNAME);
117            connectionPassword = (String)options.get(CONNECTION_PASSWORD);
118            connectionProtocol = (String)options.get(CONNECTION_PROTOCOL);
119            authentication = (String)options.get(AUTHENTICATION);
120    
121            adminBase = (String)options.get(ADMIN_BASE);
122            adminAttribute = (String)options.get(ADMIN_ATTRIBUTE);
123            readBase = (String)options.get(READ_BASE);
124            readAttribute = (String)options.get(READ_ATTRIBUTE);
125            writeBase = (String)options.get(WRITE_BASE);
126            writeAttribute = (String)options.get(WRITE_ATTRIBUTE);
127    
128            String topicSearchMatching = (String)options.get(TOPIC_SEARCH_MATCHING);
129            String topicSearchSubtree = (String)options.get(TOPIC_SEARCH_SUBTREE);
130            String queueSearchMatching = (String)options.get(QUEUE_SEARCH_MATCHING);
131            String queueSearchSubtree = (String)options.get(QUEUE_SEARCH_SUBTREE);
132            topicSearchMatchingFormat = new MessageFormat(topicSearchMatching);
133            queueSearchMatchingFormat = new MessageFormat(queueSearchMatching);
134            topicSearchSubtreeBool = Boolean.valueOf(topicSearchSubtree).booleanValue();
135            queueSearchSubtreeBool = Boolean.valueOf(queueSearchSubtree).booleanValue();
136        }
137    
138        public Set<GroupPrincipal> getTempDestinationAdminACLs() {
139            // TODO insert implementation
140            return null;
141        }
142    
143        public Set<GroupPrincipal> getTempDestinationReadACLs() {
144            // TODO insert implementation
145            return null;
146        }
147    
148        public Set<GroupPrincipal> getTempDestinationWriteACLs() {
149            // TODO insert implementation
150            return null;
151        }
152    
153        public Set<GroupPrincipal> getAdminACLs(ActiveMQDestination destination) {
154            return getACLs(destination, adminBase, adminAttribute);
155        }
156    
157        public Set<GroupPrincipal> getReadACLs(ActiveMQDestination destination) {
158            return getACLs(destination, readBase, readAttribute);
159        }
160    
161        public Set<GroupPrincipal> getWriteACLs(ActiveMQDestination destination) {
162            return getACLs(destination, writeBase, writeAttribute);
163        }
164    
165        // Properties
166        // -------------------------------------------------------------------------
167    
168        public String getAdminAttribute() {
169            return adminAttribute;
170        }
171    
172        public void setAdminAttribute(String adminAttribute) {
173            this.adminAttribute = adminAttribute;
174        }
175    
176        public String getAdminBase() {
177            return adminBase;
178        }
179    
180        public void setAdminBase(String adminBase) {
181            this.adminBase = adminBase;
182        }
183    
184        public String getAuthentication() {
185            return authentication;
186        }
187    
188        public void setAuthentication(String authentication) {
189            this.authentication = authentication;
190        }
191    
192        public String getConnectionPassword() {
193            return connectionPassword;
194        }
195    
196        public void setConnectionPassword(String connectionPassword) {
197            this.connectionPassword = connectionPassword;
198        }
199    
200        public String getConnectionProtocol() {
201            return connectionProtocol;
202        }
203    
204        public void setConnectionProtocol(String connectionProtocol) {
205            this.connectionProtocol = connectionProtocol;
206        }
207    
208        public String getConnectionURL() {
209            return connectionURL;
210        }
211    
212        public void setConnectionURL(String connectionURL) {
213            this.connectionURL = connectionURL;
214        }
215    
216        public String getConnectionUsername() {
217            return connectionUsername;
218        }
219    
220        public void setConnectionUsername(String connectionUsername) {
221            this.connectionUsername = connectionUsername;
222        }
223    
224        public DirContext getContext() {
225            return context;
226        }
227    
228        public void setContext(DirContext context) {
229            this.context = context;
230        }
231    
232        public String getInitialContextFactory() {
233            return initialContextFactory;
234        }
235    
236        public void setInitialContextFactory(String initialContextFactory) {
237            this.initialContextFactory = initialContextFactory;
238        }
239    
240        public MessageFormat getQueueSearchMatchingFormat() {
241            return queueSearchMatchingFormat;
242        }
243    
244        public void setQueueSearchMatchingFormat(MessageFormat queueSearchMatchingFormat) {
245            this.queueSearchMatchingFormat = queueSearchMatchingFormat;
246        }
247    
248        public boolean isQueueSearchSubtreeBool() {
249            return queueSearchSubtreeBool;
250        }
251    
252        public void setQueueSearchSubtreeBool(boolean queueSearchSubtreeBool) {
253            this.queueSearchSubtreeBool = queueSearchSubtreeBool;
254        }
255    
256        public String getReadAttribute() {
257            return readAttribute;
258        }
259    
260        public void setReadAttribute(String readAttribute) {
261            this.readAttribute = readAttribute;
262        }
263    
264        public String getReadBase() {
265            return readBase;
266        }
267    
268        public void setReadBase(String readBase) {
269            this.readBase = readBase;
270        }
271    
272        public MessageFormat getTopicSearchMatchingFormat() {
273            return topicSearchMatchingFormat;
274        }
275    
276        public void setTopicSearchMatchingFormat(MessageFormat topicSearchMatchingFormat) {
277            this.topicSearchMatchingFormat = topicSearchMatchingFormat;
278        }
279    
280        public boolean isTopicSearchSubtreeBool() {
281            return topicSearchSubtreeBool;
282        }
283    
284        public void setTopicSearchSubtreeBool(boolean topicSearchSubtreeBool) {
285            this.topicSearchSubtreeBool = topicSearchSubtreeBool;
286        }
287    
288        public String getWriteAttribute() {
289            return writeAttribute;
290        }
291    
292        public void setWriteAttribute(String writeAttribute) {
293            this.writeAttribute = writeAttribute;
294        }
295    
296        public String getWriteBase() {
297            return writeBase;
298        }
299    
300        public void setWriteBase(String writeBase) {
301            this.writeBase = writeBase;
302        }
303    
304        // Implementation methods
305        // -------------------------------------------------------------------------
306        protected Set<GroupPrincipal> getACLs(ActiveMQDestination destination, String roleBase, String roleAttribute) {
307            try {
308                context = open();
309            } catch (NamingException e) {
310                LOG.error(e);
311                return new HashSet<GroupPrincipal>();
312            }
313    
314            // if ((destination.getDestinationType() &
315            // (ActiveMQDestination.QUEUE_TYPE | ActiveMQDestination.TOPIC_TYPE)) !=
316            // 0)
317            // return new HashSet();
318    
319            String destinationBase = "";
320            SearchControls constraints = new SearchControls();
321    
322            if ((destination.getDestinationType() & ActiveMQDestination.QUEUE_TYPE) == ActiveMQDestination.QUEUE_TYPE) {
323                destinationBase = queueSearchMatchingFormat.format(new String[] {destination.getPhysicalName()});
324                if (queueSearchSubtreeBool) {
325                    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
326                } else {
327                    constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
328                }
329            }
330            if ((destination.getDestinationType() & ActiveMQDestination.TOPIC_TYPE) == ActiveMQDestination.TOPIC_TYPE) {
331                destinationBase = topicSearchMatchingFormat.format(new String[] {destination.getPhysicalName()});
332                if (topicSearchSubtreeBool) {
333                    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
334                } else {
335                    constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
336                }
337            }
338    
339            constraints.setReturningAttributes(new String[] {roleAttribute});
340    
341            try {
342                Set<GroupPrincipal> roles = new HashSet<GroupPrincipal>();
343                Set<String> acls = new HashSet<String>();
344                NamingEnumeration results = context.search(destinationBase, roleBase, constraints);
345                while (results.hasMore()) {
346                    SearchResult result = (SearchResult)results.next();
347                    Attributes attrs = result.getAttributes();
348                    if (attrs == null) {
349                        continue;
350                    }
351                    acls = addAttributeValues(roleAttribute, attrs, acls);
352                }
353                for (Iterator<String> iter = acls.iterator(); iter.hasNext();) {
354                    String roleName = iter.next();
355                    roles.add(new GroupPrincipal(roleName));
356                }
357                return roles;
358            } catch (NamingException e) {
359                LOG.error(e);
360                return new HashSet<GroupPrincipal>();
361            }
362        }
363    
364        protected Set<String> addAttributeValues(String attrId, Attributes attrs, Set<String> values) throws NamingException {
365            if (attrId == null || attrs == null) {
366                return values;
367            }
368            if (values == null) {
369                values = new HashSet<String>();
370            }
371            Attribute attr = attrs.get(attrId);
372            if (attr == null) {
373                return values;
374            }
375            NamingEnumeration e = attr.getAll();
376            while (e.hasMore()) {
377                String value = (String)e.next();
378                values.add(value);
379            }
380            return values;
381        }
382    
383        protected DirContext open() throws NamingException {
384            if (context != null) {
385                return context;
386            }
387    
388            try {
389                Hashtable<String, String> env = new Hashtable<String, String>();
390                env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
391                if (connectionUsername != null || !"".equals(connectionUsername)) {
392                    env.put(Context.SECURITY_PRINCIPAL, connectionUsername);
393                }
394                if (connectionPassword != null || !"".equals(connectionPassword)) {
395                    env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
396                }
397                env.put(Context.SECURITY_PROTOCOL, connectionProtocol);
398                env.put(Context.PROVIDER_URL, connectionURL);
399                env.put(Context.SECURITY_AUTHENTICATION, authentication);
400                context = new InitialDirContext(env);
401    
402            } catch (NamingException e) {
403                LOG.error(e);
404                throw e;
405            }
406            return context;
407        }
408    
409    }