ACLManager.java
001 /*
002  *  Licensed to the Apache Software Foundation (ASF) under one
003  *  or more contributor license agreements.  See the NOTICE file
004  *  distributed with this work for additional information
005  *  regarding copyright ownership.  The ASF licenses this file
006  *  to you under the Apache License, Version 2.0 (the
007  *  "License"); you may not use this file except in compliance
008  *  with the License.  You may obtain a copy of the License at
009  *
010  *    http://www.apache.org/licenses/LICENSE-2.0
011  *
012  *  Unless required by applicable law or agreed to in writing,
013  *  software distributed under the License is distributed on an
014  *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015  *  KIND, either express or implied.  See the License for the
016  *  specific language governing permissions and limitations
017  *  under the License.    
018  *
019  
020  */
021 package org.apache.qpid.server.security.access;
022 
023 import java.util.Collection;
024 import java.util.HashMap;
025 import java.util.HashSet;
026 import java.util.Iterator;
027 import java.util.Map;
028 import java.util.Map.Entry;
029 
030 import org.apache.commons.configuration.Configuration;
031 import org.apache.log4j.Logger;
032 import org.apache.qpid.framing.AMQShortString;
033 import org.apache.qpid.server.configuration.SecurityConfiguration;
034 import org.apache.qpid.server.configuration.ServerConfiguration;
035 import org.apache.qpid.server.configuration.VirtualHostConfiguration;
036 import org.apache.qpid.server.exchange.Exchange;
037 import org.apache.qpid.server.plugins.PluginManager;
038 import org.apache.qpid.server.protocol.AMQProtocolSession;
039 import org.apache.qpid.server.queue.AMQQueue;
040 import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult;
041 import org.apache.qpid.server.security.access.plugins.SimpleXML;
042 import org.apache.qpid.server.virtualhost.VirtualHost;
043 
044 public class ACLManager
045 {
046     private static final Logger _logger = Logger.getLogger(ACLManager.class);
047     private PluginManager _pluginManager;
048     private Map<String, ACLPluginFactory> _allSecurityPlugins = new HashMap<String, ACLPluginFactory>();
049     private Map<String, ACLPlugin> _globalPlugins = new HashMap<String, ACLPlugin>();
050     private Map<String, ACLPlugin> _hostPlugins = new HashMap<String, ACLPlugin>();
051 
052     public ACLManager(SecurityConfiguration configuration, PluginManager manager)
053     {
054         this(configuration, manager, null);
055     }
056 
057     public ACLManager(SecurityConfiguration configuration, PluginManager manager, ACLPluginFactory securityPlugin)
058     {
059         _pluginManager = manager;
060 
061         if (manager == null// No plugin manager, no plugins
062         {
063             return;
064         }
065 
066         _allSecurityPlugins = _pluginManager.getSecurityPlugins();
067         if (securityPlugin != null)
068         {
069             _allSecurityPlugins.put(securityPlugin.getClass().getName(), securityPlugin);
070         }
071 
072         _globalPlugins = configurePlugins(configuration);
073     }
074 
075 
076     public void configureHostPlugins(SecurityConfiguration hostConfig)
077     {
078         _hostPlugins = configurePlugins(hostConfig);
079     }
080     
081     public Map<String, ACLPlugin> configurePlugins(SecurityConfiguration hostConfig)
082     {
083         Configuration securityConfig = hostConfig.getConfiguration();
084         Map<String, ACLPlugin> plugins = new HashMap<String, ACLPlugin>();
085         Iterator keys = securityConfig.getKeys();
086         Collection<String> handledTags = new HashSet();
087         while (keys.hasNext())
088         {
089             // Splitting the string is necessary here because of the way that getKeys() returns only
090             // bottom level children
091             String tag = ((Stringkeys.next()).split("\\."2)[0];
092             if (!handledTags.contains(tag))
093             {
094                 for (ACLPluginFactory plugin : _allSecurityPlugins.values())
095                 {
096                     if (plugin.supportsTag(tag))
097                     {
098                         _logger.warn("Plugin handling security section "+tag+" is "+plugin.getClass().getSimpleName());
099                         handledTags.add(tag);
100                         plugins.put(plugin.getClass().getName(), plugin.newInstance(securityConfig));
101                     }
102                 }
103             }
104             if (!handledTags.contains(tag))
105             {
106                 _logger.warn("No plugin handled security section "+tag);
107             }
108         }
109         return plugins;
110     }    
111 
112     public static Logger getLogger()
113     {
114         return _logger;
115     }
116 
117     private abstract class AccessCheck
118     {
119         abstract AuthzResult allowed(ACLPlugin plugin);
120     }
121 
122     private boolean checkAllPlugins(AccessCheck checker)
123     {
124         AuthzResult result = AuthzResult.ABSTAIN;
125         HashMap<String, ACLPlugin> remainingPlugins = new HashMap<String, ACLPlugin>();
126         remainingPlugins.putAll(_globalPlugins);
127         for (Entry<String, ACLPlugin> plugin : _hostPlugins.entrySet())
128         {
129             result = checker.allowed(plugin.getValue());
130             if (result == AuthzResult.DENIED)
131             {
132                 // Something vetoed the access, we're done
133                 return false
134             }
135             else if (result == AuthzResult.ALLOWED)
136             {
137                 // Remove plugin from global check list since 
138                 // host allow overrides global allow
139                 remainingPlugins.remove(plugin.getKey());
140             }
141         }
142         
143         for (ACLPlugin plugin : remainingPlugins.values())
144         {   
145             result = checker.allowed(plugin);
146             if (result == AuthzResult.DENIED)
147             {
148                 return false;
149             }
150         }
151         return true;
152     }
153 
154     public boolean authoriseBind(final AMQProtocolSession session, final Exchange exch, final AMQQueue queue,
155             final AMQShortString routingKey)
156     {
157         return checkAllPlugins(new AccessCheck()
158         {
159 
160             @Override
161             AuthzResult allowed(ACLPlugin plugin)
162             {
163                 return plugin.authoriseBind(session, exch, queue, routingKey);
164             }
165 
166         });
167     }
168 
169     public boolean authoriseConnect(final AMQProtocolSession session, final VirtualHost virtualHost)
170     {
171         return checkAllPlugins(new AccessCheck()
172         {
173 
174             @Override
175             AuthzResult allowed(ACLPlugin plugin)
176             {
177                 return plugin.authoriseConnect(session, virtualHost);
178             }
179 
180         });
181     }
182 
183     public boolean authoriseConsume(final AMQProtocolSession session, final boolean noAck, final AMQQueue queue)
184     {
185         return checkAllPlugins(new AccessCheck()
186         {
187 
188             @Override
189             AuthzResult allowed(ACLPlugin plugin)
190             {
191                 return plugin.authoriseConsume(session, noAck, queue);
192             }
193 
194         });
195     }
196 
197     public boolean authoriseConsume(final AMQProtocolSession session, final boolean exclusive, final boolean noAck,
198             final boolean noLocal, final boolean nowait, final AMQQueue queue)
199     {
200         return checkAllPlugins(new AccessCheck()
201         {
202 
203             @Override
204             AuthzResult allowed(ACLPlugin plugin)
205             {
206                 return plugin.authoriseConsume(session, exclusive, noAck, noLocal, nowait, queue);
207             }
208 
209         });
210     }
211 
212     public boolean authoriseCreateExchange(final AMQProtocolSession session, final boolean autoDelete,
213             final boolean durable, final AMQShortString exchangeName, final boolean internal, final boolean nowait,
214             final boolean passive, final AMQShortString exchangeType)
215     {
216         return checkAllPlugins(new AccessCheck()
217         {
218 
219             @Override
220             AuthzResult allowed(ACLPlugin plugin)
221             {
222                 return plugin.authoriseCreateExchange(session, autoDelete, durable, exchangeName, internal, nowait,
223                         passive, exchangeType);
224             }
225 
226         });
227     }
228 
229     public boolean authoriseCreateQueue(final AMQProtocolSession session, final boolean autoDelete,
230             final boolean durable, final boolean exclusive, final boolean nowait, final boolean passive,
231             final AMQShortString queue)
232     {
233         return checkAllPlugins(new AccessCheck()
234         {
235 
236             @Override
237             AuthzResult allowed(ACLPlugin plugin)
238             {
239                 return plugin.authoriseCreateQueue(session, autoDelete, durable, exclusive, nowait, passive, queue);
240             }
241 
242         });
243     }
244 
245     public boolean authoriseDelete(final AMQProtocolSession session, final AMQQueue queue)
246     {
247         return checkAllPlugins(new AccessCheck()
248         {
249 
250             @Override
251             AuthzResult allowed(ACLPlugin plugin)
252             {
253                 return plugin.authoriseDelete(session, queue);
254             }
255 
256         });
257     }
258 
259     public boolean authoriseDelete(final AMQProtocolSession session, final Exchange exchange)
260     {
261         return checkAllPlugins(new AccessCheck()
262         {
263 
264             @Override
265             AuthzResult allowed(ACLPlugin plugin)
266             {
267                 return plugin.authoriseDelete(session, exchange);
268             }
269 
270         });
271     }
272     
273     public boolean authorisePublish(final AMQProtocolSession session, final boolean immediate, final boolean mandatory,
274             final AMQShortString routingKey, final Exchange e)
275     {
276         return checkAllPlugins(new AccessCheck()
277         {
278 
279             @Override
280             AuthzResult allowed(ACLPlugin plugin)
281             {
282                 return plugin.authorisePublish(session, immediate, mandatory, routingKey, e);
283             }
284 
285         });
286     }
287 
288     public boolean authorisePurge(final AMQProtocolSession session, final AMQQueue queue)
289     {
290         return checkAllPlugins(new AccessCheck()
291         {
292 
293             @Override
294             AuthzResult allowed(ACLPlugin plugin)
295             {
296                 return plugin.authorisePurge(session, queue);
297             }
298 
299         });
300     }
301 
302     public boolean authoriseUnbind(final AMQProtocolSession session, final Exchange exch,
303             final AMQShortString routingKey, final AMQQueue queue)
304     {
305         return checkAllPlugins(new AccessCheck()
306         {
307 
308             @Override
309             AuthzResult allowed(ACLPlugin plugin)
310             {
311                 return plugin.authoriseUnbind(session, exch, routingKey, queue);
312             }
313 
314         });
315     }
316 
317     public void addHostPlugin(ACLPlugin aclPlugin)
318     {
319         _hostPlugins.put(aclPlugin.getClass().getName(), aclPlugin);
320     }
321 }