001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq.security;
023:
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.Iterator;
027: import java.util.Set;
028: import javax.jms.Destination;
029: import javax.jms.JMSException;
030: import javax.jms.JMSSecurityException;
031: import javax.jms.InvalidDestinationException;
032: import javax.jms.TemporaryQueue;
033: import javax.jms.TemporaryTopic;
034: import org.jboss.mq.ConnectionToken;
035: import org.jboss.mq.SpyMessage;
036: import org.jboss.mq.SpyDestination;
037: import org.jboss.mq.SpyTopic;
038: import org.jboss.mq.DurableSubscriptionID;
039: import org.jboss.mq.Subscription;
040: import org.jboss.mq.TransactionRequest;
041: import org.jboss.mq.server.JMSServerInterceptorSupport;
042:
043: /**
044: * ServerSecurityInvoker.java
045: *
046: * @author <a href="mailto:pra@tim.se">Peter Antman</a>
047: * @version $Revision: 57198 $
048: */
049: public class ServerSecurityInterceptor extends
050: JMSServerInterceptorSupport {
051: SecurityManager manager;
052:
053: /**
054: * The temporary destinations for a connection
055: */
056: private HashMap tempDests = new HashMap();
057:
058: public ServerSecurityInterceptor(SecurityManager manager) {
059: super ();
060: this .manager = manager;
061: }
062:
063: public String authenticate(String name, String password)
064: throws JMSException {
065: log.trace("Authenticating user " + name);
066: return manager.authenticate(name, password);
067: }
068:
069: /**
070: * Close connection. Logout user after connection is closed.
071: *
072: * @param dc Description of Parameter
073: * @exception JMSException Description of Exception
074: */
075: public void connectionClosing(ConnectionToken dc)
076: throws JMSException {
077: super .connectionClosing(dc);
078: manager.logout(dc);
079: removeTemporaryDestinations(dc);
080: }
081:
082: /*
083: Here is a number of methods that I do no know if we should check access on.
084:
085: createQueue()
086: createTopic()
087:
088: unsubscribe() - probably not.
089: */
090: //
091: // Read methods, to check access on
092: //
093: public SpyMessage[] browse(ConnectionToken dc, Destination dest,
094: String selector) throws JMSException {
095: if (log.isTraceEnabled())
096: log.trace("Checking browse authorize on " + dc + " dest="
097: + dest);
098: if (!authorizeRead(dc, ((SpyDestination) dest).getName()))
099: throw new JMSSecurityException(
100: "Connection not authorized to browse to destination: "
101: + dest);
102: return super .browse(dc, dest, selector);
103: }
104:
105: // FIXME This might actually be unneeded since a subscribe is
106: // allways done first.
107: // fuck.
108: // If we remove it, remember to remove the getSubscription method
109: public SpyMessage receive(ConnectionToken dc, int subscriberId,
110: long wait) throws JMSException {
111: if (log.isTraceEnabled())
112: log.trace("Checking receive authorize on " + dc + " subId="
113: + subscriberId);
114: // Another nightmare, how the fck do we get the dest name.
115: Subscription sub = super .getSubscription(dc, subscriberId);
116: String destName = sub.destination.getName();
117: if (!authorizeRead(dc, destName))
118: throw new JMSSecurityException(
119: "Connection not authorized to receive from destination: "
120: + destName);
121: return super .receive(dc, subscriberId, wait);
122: }
123:
124: // The price we pay for adding this to an implementation not done
125: // for acl's. The method to create a durable susbcriber is this, but
126: // with a durableSubscriptionID in the destination of the subscription.
127: // For all but, durable subscriptions this is a read access thingy.
128: // Even more: if this is a create of a durable sub, or a change of one
129: // or just usage of an existing one, that does basically only JMSTopic (it's new or has changed) and StateManager (its allowed) know of.
130: // The logic has to be this, to not get into trouble: for ALL usage
131: // of a durable sub create access is demanded!
132: //
133: public void subscribe(org.jboss.mq.ConnectionToken dc,
134: org.jboss.mq.Subscription sub) throws JMSException {
135: if (log.isTraceEnabled())
136: log.trace("Checking subscribe authorize on " + dc + " sub="
137: + sub);
138: // Do some sanity checks
139: if (sub == null)
140: throw new JMSException(
141: "The subscription is not allowed to be null");
142: else if (sub.destination == null)
143: throw new InvalidDestinationException(
144: "Destination is not allowed to be null");
145: // Check if its a durable sub/ this might actually not be true create
146: // only access to an old one. But we only allow read from durable
147: // if you actually have the rights to create one, or...does this make
148: // preconfigured clientID meaningless.
149: SpyDestination dest = sub.destination;
150: String destName = dest.getName();
151: if (dest instanceof SpyTopic) {
152: // Check durable sub
153: DurableSubscriptionID id = ((SpyTopic) dest)
154: .getDurableSubscriptionID();
155: if (id != null) {
156: // Durable sub, check create access.
157: if (!authorizeCreate(dc, destName))
158: throw new JMSSecurityException(
159: "Connection not authorized to do durable subscription on topic: "
160: + destName);
161: }
162: }
163: // We ALLWAYS check read access, even for durables
164: if (!authorizeRead(dc, destName))
165: throw new JMSSecurityException(
166: "Connection not authorized to subscribe to destination: "
167: + destName);
168: super .subscribe(dc, sub);
169: }
170:
171: //
172: // Write methods, to check access on
173: //
174: public void addMessage(ConnectionToken dc, SpyMessage message)
175: throws JMSException {
176: String dest = ((SpyDestination) message.getJMSDestination())
177: .getName();
178: if (!authorizeWrite(dc, dest))
179: throw new JMSSecurityException(
180: "Connection not authorized to addMessages to destination: "
181: + dest);
182: super .addMessage(dc, message);
183: }
184:
185: // Check that a transaction request is authorized to send the messages
186: public void transact(ConnectionToken dc, TransactionRequest t)
187: throws JMSException {
188: if (t.messages != null) {
189: // Optimization for common case
190: if (t.messages.length == 1) {
191: String dest = ((SpyDestination) t.messages[0]
192: .getJMSDestination()).getName();
193: if (authorizeWrite(dc, dest) == false)
194: throw new JMSSecurityException(
195: "Connection not authorized to addMessages to destination: "
196: + dest);
197: } else if (t.messages.length > 0) {
198: HashSet destinations = new HashSet();
199: for (int i = 0; i < t.messages.length; ++i)
200: destinations.add(((SpyDestination) t.messages[i]
201: .getJMSDestination()).getName());
202:
203: for (Iterator i = destinations.iterator(); i.hasNext();) {
204: String destinationName = (String) i.next();
205: if (authorizeWrite(dc, destinationName) == false)
206: throw new JMSSecurityException(
207: "Connection not authorized to addMessages to destination: "
208: + destinationName);
209: }
210: }
211: }
212: super .transact(dc, t);
213: }
214:
215: //
216: // Create methods, to check access on
217: //
218: public void destroySubscription(ConnectionToken dc,
219: DurableSubscriptionID id) throws JMSException {
220: // Oh, this fucker is a nightmare. How do we get wich topic the
221: // connection is trying to unsubscribe from
222: SpyTopic t = super .getDurableTopic(id);
223: if (t == null)
224: throw new InvalidDestinationException(
225: "No durable topic found for subscription "
226: + id.getSubscriptionName());
227: if (!authorizeCreate(dc, t.getName()))
228: throw new JMSSecurityException(
229: "Connection not authorized to unsubscribe from subscription: "
230: + t.getName());
231: super .destroySubscription(dc, id);
232: }
233:
234: public TemporaryTopic getTemporaryTopic(ConnectionToken dc)
235: throws JMSException {
236: TemporaryTopic result = super .getTemporaryTopic(dc);
237: addTemporaryDestination(dc, result);
238: return result;
239: }
240:
241: public TemporaryQueue getTemporaryQueue(ConnectionToken dc)
242: throws JMSException {
243: TemporaryQueue result = super .getTemporaryQueue(dc);
244: addTemporaryDestination(dc, result);
245: return result;
246: }
247:
248: public void deleteTemporaryDestination(ConnectionToken dc,
249: SpyDestination destination) throws JMSException {
250: removeTemporaryDestination(dc, destination);
251: super .deleteTemporaryDestination(dc, destination);
252: }
253:
254: //
255: // Security helper methods
256: //
257: public boolean authorizeRead(ConnectionToken dc, String destination)
258: throws JMSException {
259: // First we must get access to the destinations security meta data
260: SecurityMetadata m = manager.getSecurityMetadata(destination);
261: if (m == null) {
262: log.warn("No security configuration avaliable for "
263: + destination);
264: return false;//FIXME, is this OK?
265: }
266: Set readPrincipals = m.getReadPrincipals();
267: if (manager.authorize(dc, readPrincipals))
268: return true;
269: else
270: return false;
271: }
272:
273: public boolean authorizeWrite(ConnectionToken dc, String destination)
274: throws JMSException {
275: // First we must get access to the destinations security meta data
276: SecurityMetadata m = manager.getSecurityMetadata(destination);
277: if (m == null) {
278: log.warn("No security configuration avaliable for "
279: + destination);
280: return false;//FIXME, is this OK?
281: }
282: Set writePrincipals = m.getWritePrincipals();
283: if (manager.authorize(dc, writePrincipals))
284: return true;
285: else
286: return false;
287: }
288:
289: public boolean authorizeCreate(ConnectionToken dc,
290: String destination) throws JMSException {
291: // First we must get access to the destinations security meta data
292: SecurityMetadata m = manager.getSecurityMetadata(destination);
293: if (m == null) {
294: log.warn("No security configuration avaliable for "
295: + destination);
296: return false;//FIXME, is this OK?
297: }
298: Set createPrincipals = m.getCreatePrincipals();
299: if (manager.authorize(dc, createPrincipals))
300: return true;
301: else
302: return false;
303: }
304:
305: /**
306: * Remember the temporary destinations for a connection
307: */
308: public void addTemporaryDestination(ConnectionToken dc,
309: Destination destination) {
310: synchronized (tempDests) {
311: HashSet set = (HashSet) tempDests.get(dc);
312: if (set == null) {
313: set = new HashSet();
314: tempDests.put(dc, set);
315: }
316: set.add(destination);
317: }
318: }
319:
320: /**
321: * Remove a temporary destination
322: */
323: public void removeTemporaryDestination(ConnectionToken dc,
324: SpyDestination destination) {
325: synchronized (tempDests) {
326: HashSet set = (HashSet) tempDests.get(dc);
327: if (set == null)
328: return;
329: set.remove(destination);
330: }
331: try {
332: manager.removeDestination(destination.getName());
333: } catch (Exception e) {
334: log.warn("Unable to remove temporary destination "
335: + destination, e);
336: }
337: }
338:
339: /**
340: * Remove all temporary destination for a connection
341: */
342: public void removeTemporaryDestinations(ConnectionToken dc) {
343: synchronized (tempDests) {
344: HashSet set = (HashSet) tempDests.remove(dc);
345: if (set == null)
346: return;
347: for (Iterator iterator = set.iterator(); iterator.hasNext();) {
348: SpyDestination destination = (SpyDestination) iterator
349: .next();
350: try {
351: manager.removeDestination(destination.getName());
352: } catch (Exception e) {
353: log.warn("Unable to remove temporary destination "
354: + destination, e);
355: }
356: }
357: }
358: }
359: } // ServerSecurityInvoker
|