Package org.jboss.jms.server.container

Source Code of org.jboss.jms.server.container.SecurityAspect

/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt in the distribution for a
  * full listing of individual contributors.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
package org.jboss.jms.server.container;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.jms.Destination;
import javax.jms.JMSSecurityException;
import javax.jms.Message;

import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.jms.destination.JBossDestination;
import org.jboss.jms.message.JBossMessage;
import org.jboss.jms.server.SecurityStore;
import org.jboss.jms.server.endpoint.ServerConnectionEndpoint;
import org.jboss.jms.server.endpoint.ServerConsumerEndpoint;
import org.jboss.jms.server.endpoint.ServerSessionEndpoint;
import org.jboss.jms.server.endpoint.advised.ConnectionAdvised;
import org.jboss.jms.server.endpoint.advised.ConsumerAdvised;
import org.jboss.jms.server.endpoint.advised.SessionAdvised;
import org.jboss.jms.server.security.CheckType;
import org.jboss.jms.server.security.SecurityMetadata;
import org.jboss.jms.tx.ClientTransaction;
import org.jboss.jms.tx.TransactionRequest;
import org.jboss.logging.Logger;

/**
* This aspect enforces the JBossMessaging JMS security policy.
*
* This aspect is PER_INSTANCE
*
* For performance reasons we cache access rights in the interceptor for a maximum of
* INVALIDATION_INTERVAL milliseconds.
* This is because we don't want to do a full authentication and authorization on every send,
* for example, since this will drastically reduce performance.
* This means any changes to security data won't be reflected until INVALIDATION_INTERVAL
* milliseconds later.
*
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
* @author <a href="mailto:ovidiu@feodorov.com">Ovidiu Feodorov</a>
* @version <tt>$Revision 1.1 $</tt>
*
* $Id: SecurityAspect.java 7934 2010-02-02 14:27:40Z gaohoward $
*/
public class SecurityAspect
{
   // Constants -----------------------------------------------------

   private static final Logger log = Logger.getLogger(SecurityAspect.class);

   // Static --------------------------------------------------------
  
   // Attributes ----------------------------------------------------
  
   private boolean trace = log.isTraceEnabled();
  
   private Set readCache;
  
   private Set writeCache;
  
   private Set createCache;
  
   //TODO Make this configurable
   private static final long INVALIDATION_INTERVAL = 15000;
  
   private long lastCheck;
     
   // Constructors --------------------------------------------------
  
   // Public --------------------------------------------------------
   public SecurityAspect()
   {
      readCache = new HashSet();
     
      writeCache = new HashSet();
     
      createCache = new HashSet();
   }
  
   public Object handleCreateConsumerDelegate(Invocation invocation) throws Throwable
   {
      MethodInvocation mi = (MethodInvocation)invocation;
     
      // read permission required on the destination
      Destination dest = (Destination)mi.getArguments()[0];
     
      SessionAdvised del = (SessionAdvised)invocation.getTargetObject();
      ServerSessionEndpoint sess = (ServerSessionEndpoint)del.getEndpoint();
     
      check(dest, CheckType.READ, sess.getConnectionEndpoint());
     
      // if creating a durable subscription then need create permission
     
      String subscriptionName = (String)mi.getArguments()[3];
      if (subscriptionName != null)
      {
         // durable
         check(dest, CheckType.CREATE, sess.getConnectionEndpoint());
      }
     
      return invocation.invokeNext();
   }  
  
   public Object handleCreateBrowserDelegate(Invocation invocation) throws Throwable
   {
      // read permission required on the destination
     
      MethodInvocation mi = (MethodInvocation)invocation;
     
      Destination dest = (Destination)mi.getArguments()[0];
     
      SessionAdvised del = (SessionAdvised)invocation.getTargetObject();
      ServerSessionEndpoint sess = (ServerSessionEndpoint)del.getEndpoint();
                 
      check(dest, CheckType.READ, sess.getConnectionEndpoint());
     
      return invocation.invokeNext();
   }
  
   public Object handleSend(Invocation invocation) throws Throwable
   {
      // anonymous producer - if destination is not null then write permissions required
     
      MethodInvocation mi = (MethodInvocation)invocation;
     
      Message m = (Message)mi.getArguments()[0];
      Destination dest = m.getJMSDestination();

      SessionAdvised del = (SessionAdvised)invocation.getTargetObject();
      ServerSessionEndpoint se = (ServerSessionEndpoint)del.getEndpoint();
      ServerConnectionEndpoint ce = se.getConnectionEndpoint();
                       
      check(dest, CheckType.WRITE, ce);
           
      return invocation.invokeNext();
   }


   // An aspect over ConnectionAdvised
   public Object handleSendTransaction(Invocation invocation) throws Throwable
   {
      ConnectionAdvised del = (ConnectionAdvised)invocation.getTargetObject();
      ServerConnectionEndpoint ce = (ServerConnectionEndpoint)del.getEndpoint();

      MethodInvocation mi = (MethodInvocation)invocation;

      TransactionRequest t = (TransactionRequest)mi.getArguments()[0];

      ClientTransaction txState = t.getState();

      if (txState != null)
      {
         // distinct list of destinations...
         HashSet destinations = new HashSet();

         for (Iterator i = txState.getSessionStates().iterator(); i.hasNext(); )
         {
            ClientTransaction.SessionTxState sessionState = (ClientTransaction.SessionTxState)i.next();
            for (Iterator j = sessionState.getMsgs().iterator(); j.hasNext(); )
            {
               JBossMessage message = (JBossMessage)j.next();
               destinations.add(message.getJMSDestination());
            }
         }
         for (Iterator iterDestinations = destinations.iterator();iterDestinations.hasNext();)
         {
            Destination destination = (Destination) iterDestinations.next();
            check(destination, CheckType.WRITE, ce);
         }

      }

      return invocation.invokeNext();
   }


  
   protected void checkConsumerAccess(Invocation invocation) throws Throwable
   {
      ConsumerAdvised del = (ConsumerAdvised)invocation.getTargetObject();
      ServerConsumerEndpoint cons = (ServerConsumerEndpoint)del.getEndpoint();
      ServerConnectionEndpoint conn = cons.getSessionEndpoint().getConnectionEndpoint();
      JBossDestination dest = cons.getDestination();
     
      check(dest, CheckType.READ, conn);
   }
  
   // Package protected ---------------------------------------------
  
   // Protected -----------------------------------------------------
  
   // Private -------------------------------------------------------
        
   private boolean checkCached(Destination dest, CheckType checkType)
   {
      long now = System.currentTimeMillis();
     
      boolean granted = false;
     
      if (now - lastCheck > INVALIDATION_INTERVAL)
      {
         readCache.clear();
        
         writeCache.clear();
        
         createCache.clear();        
      }
      else
      {        
         switch (checkType.type)
         {
            case CheckType.TYPE_READ:
            {
               granted = readCache.contains(dest);
               break;
            }
            case CheckType.TYPE_WRITE:
            {
               granted = writeCache.contains(dest);
               break;
            }
            case CheckType.TYPE_CREATE:
            {
               granted = createCache.contains(dest);
               break;
            }
            default:
            {
               throw new IllegalArgumentException("Invalid checkType:" + checkType);
            }
         }
      }
     
      lastCheck = now;
     
      return granted;
   }
  
   private void check(Destination dest, CheckType checkType, ServerConnectionEndpoint conn)
      throws JMSSecurityException
   {
      JBossDestination jbd = (JBossDestination)dest;

      if (jbd.isTemporary())
      {
         if (trace) { log.trace("skipping permission check on temporary destination " + dest); }
         return;
      }

      if (trace) { log.trace("checking access permissions to " + dest); }
     
      if (checkCached(dest, checkType))
      {
         // OK
         return;
      }

      boolean isQueue = jbd.isQueue();
      String name = jbd.getName();

      SecurityStore sm = conn.getSecurityManager();
      SecurityMetadata securityMetadata = sm.getSecurityMetadata(isQueue, name);

      if (securityMetadata == null)
      {
         throw new JMSSecurityException("No security configuration avaliable for " + name);
      }

      // Authenticate. Successful autentication will place a new SubjectContext on thread local,
      // which will be used in the authorization process. However, we need to make sure we clean up
      // thread local immediately after we used the information, otherwise some other people
      // security my be screwed up, on account of thread local security stack being corrupted.
     
      sm.authenticate(conn.getUsername(), conn.getPassword());

      // Authorize
      Set principals = checkType == CheckType.READ ? securityMetadata.getReadPrincipals() :
                       checkType == CheckType.WRITE ? securityMetadata.getWritePrincipals() :
                       securityMetadata.getCreatePrincipals();
      try
      {
         if (!sm.authorize(conn.getUsername(), principals, checkType))
         {
            String msg = "User: " + conn.getUsername() +
               " is not authorized to " +
               (checkType == CheckType.READ ? "read from" :
                  checkType == CheckType.WRITE ? "write to" : "create durable sub on") +
               " destination " + name;

            throw new JMSSecurityException(msg);
         }
      }
      finally
      {
         // pop the Messaging SecurityContext, it did its job
         SecurityActions.popSubjectContext();
      }

      // if we get here we're granted, add to the cache
     
      switch (checkType.type)
      {
         case CheckType.TYPE_READ:
         {
            readCache.add(dest);
            break;
         }
         case CheckType.TYPE_WRITE:
         {
            writeCache.add(dest);
            break;
         }
         case CheckType.TYPE_CREATE:
         {
            createCache.add(dest);
            break;
         }
         default:
         {
            throw new IllegalArgumentException("Invalid checkType:" + checkType);
         }
      }     
   }
  
   // Inner classes -------------------------------------------------
 
}



TOP

Related Classes of org.jboss.jms.server.container.SecurityAspect

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.