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.slf4j.Logger;
040 import org.slf4j.LoggerFactory;
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 Logger LOG = LoggerFactory.getLogger(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.toString());
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.toString());
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.toString());
404 throw e;
405 }
406 return context;
407 }
408
409 }