You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ch...@apache.org on 2011/06/09 17:22:43 UTC

svn commit: r1133926 - in /activemq/activemq-apollo/trunk: apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/ apollo-util/src/main/scala/org/apache/activemq/apollo/util/

Author: chirino
Date: Thu Jun  9 15:22:43 2011
New Revision: 1133926

URL: http://svn.apache.org/viewvc?rev=1133926&view=rev
Log:
Fixes https://issues.apache.org/jira/browse/APLO-42 : File based JAAS login modules should cache file contents until changed.

Added:
    activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/FileCache.scala
Modified:
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/CertificateLoginModule.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileGroupLoginModule.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileUserLoginModule.scala
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/SocketAddressLoginModule.scala

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/CertificateLoginModule.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/CertificateLoginModule.scala?rev=1133926&r1=1133925&r2=1133926&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/CertificateLoginModule.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/CertificateLoginModule.scala Thu Jun  9 15:22:43 2011
@@ -27,11 +27,13 @@ import java.security.cert.X509Certificat
 import java.{util => ju}
 import java.io.{FileInputStream, File, IOException}
 import org.yaml.snakeyaml.Yaml
-import org.apache.activemq.apollo.util.{FileSupport, Log}
 import java.lang.String
 import org.apache.activemq.jaas.{UserPrincipal, CertificateCallback}
-import java.util.LinkedList
 import javax.security.auth.spi.LoginModule
+import java.util.{Properties, LinkedList}
+import org.apache.activemq.apollo.util.FileSupport._
+import org.apache.activemq.apollo.util.{FileCache, FileSupport, Log}
+import org.apache.activemq.apollo.util.Log._
 
 /**
  * <p>
@@ -43,6 +45,21 @@ object CertificateLoginModule {
   val LOGIN_CONFIG = "java.security.auth.login.config"
   val FILE_OPTION = "dn_file"
   val DEFAULT_LOG = Log(getClass)
+
+  def load_dns(file:File):Option[java.util.Map[String, AnyRef]] = {
+    try {
+      import FileSupport._
+      using( new FileInputStream(file) ) { in=>
+        Some((new Yaml().load(in)).asInstanceOf[java.util.Map[String, AnyRef]])
+      }
+    } catch {
+      case e: Throwable =>
+        DEFAULT_LOG.warn(e, "Unable to load the distinguished name file: " + file)
+        None
+    }
+  }
+
+  val file_cache = new FileCache[java.util.Map[String, AnyRef]](load_dns)
 }
 
 /**
@@ -112,16 +129,10 @@ class CertificateLoginModule extends Log
         }
 
       case Some(file)=>
-        val users = try {
-          import FileSupport._
-          using( new FileInputStream(file) ) { in=>
-            (new Yaml().load(in)).asInstanceOf[java.util.Map[String, AnyRef]]
-          }
-        } catch {
-          case e: Throwable =>
-            warn(e, "Unable to load the distinguished name file: " + file)
-            e.printStackTrace
-            throw new LoginException("Invalid login module configuration")
+
+        val users = file_cache.get(file) match {
+          case None => throw new LoginException("Invalid login module configuration")
+          case Some(x) => x
         }
 
         for (cert <- certificates) {

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileGroupLoginModule.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileGroupLoginModule.scala?rev=1133926&r1=1133925&r2=1133926&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileGroupLoginModule.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileGroupLoginModule.scala Thu Jun  9 15:22:43 2011
@@ -34,7 +34,6 @@ package org.apache.activemq.apollo.broke
  * limitations under the License.
  */
 import java.io.File
-import java.io.FileInputStream
 import java.security.Principal
 import javax.security.auth.Subject
 import javax.security.auth.callback.CallbackHandler
@@ -43,10 +42,9 @@ import javax.security.auth.spi.LoginModu
 import org.apache.activemq.jaas.GroupPrincipal
 import org.apache.activemq.jaas.UserPrincipal
 import java.{util => ju}
-import org.apache.activemq.apollo.util.{FileSupport, Log}
-import FileSupport._
 import java.util.regex.Pattern
-import java.util.{LinkedList, Properties}
+import java.util.LinkedList
+import org.apache.activemq.apollo.util.Log
 
 object FileGroupLoginModule {
   val LOGIN_CONFIG = "java.security.auth.login.config"
@@ -104,16 +102,9 @@ class FileGroupLoginModule extends Login
 
   def commit: Boolean = {
 
-    val groups = try {
-      using( new FileInputStream(file) ) { in=>
-        val groups = new Properties()
-        groups.load(in)
-        groups
-      }
-    } catch {
-      case e: Throwable =>
-        warn(e, "Unable to load group properties file " + file)
-        return false;
+    val groups = FileUserLoginModule.file_cache.get(file) match {
+      case None => return false
+      case Some(x) => x
     }
 
     import collection.JavaConversions._

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileUserLoginModule.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileUserLoginModule.scala?rev=1133926&r1=1133925&r2=1133926&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileUserLoginModule.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/FileUserLoginModule.scala Thu Jun  9 15:22:43 2011
@@ -34,13 +34,30 @@ import javax.security.auth.spi.LoginModu
 
 import org.apache.activemq.jaas.UserPrincipal
 import java.{util => ju}
-import org.apache.activemq.apollo.util.{FileSupport, Log}
+import org.apache.activemq.apollo.util.{FileCache, Log, FileSupport}
 import FileSupport._
 
 object FileUserLoginModule {
   val LOGIN_CONFIG = "java.security.auth.login.config"
   val FILE_OPTION = "file"
   val DEFAULT_LOG = Log(getClass)
+
+  def load_properties(file:File):Option[Properties] = {
+    try {
+      val rc = new Properties()
+      using( new FileInputStream(file) ) { in=>
+        rc.load(in)
+      }
+      EncryptionSupport.decrypt(rc)
+      Some(rc)
+    } catch {
+      case e: Throwable =>
+        DEFAULT_LOG.warn(e, "Unable to load properties file: " + file)
+        None
+    }
+  }
+
+  val file_cache = new FileCache[Properties](load_properties)
 }
 
 /**
@@ -80,16 +97,9 @@ class FileUserLoginModule extends LoginM
   }
 
   def login: Boolean = {
-    val users = new Properties()
-    try {
-      using( new FileInputStream(file) ) { in=>
-        users.load(in)
-      }
-      EncryptionSupport.decrypt(users)
-    } catch {
-      case e: Throwable =>
-        warn(e, "Unable to load user properties file: " + file)
-        return false
+    val users = file_cache.get(file) match {
+      case None => return false
+      case Some(x) => x
     }
 
     val callbacks = new Array[Callback](2)

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/SocketAddressLoginModule.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/SocketAddressLoginModule.scala?rev=1133926&r1=1133925&r2=1133926&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/SocketAddressLoginModule.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/security/SocketAddressLoginModule.scala Thu Jun  9 15:22:43 2011
@@ -22,11 +22,11 @@ import javax.security.auth.callback.Call
 import javax.security.auth.callback.UnsupportedCallbackException
 import javax.security.auth.login.LoginException
 import java.{util => ju}
-import java.io.{File, IOException}
-import org.apache.activemq.apollo.util.{FileSupport, Log}
 import java.lang.String
 import javax.security.auth.spi.LoginModule
-import java.net.{InetSocketAddress, SocketAddress, InetAddress}
+import java.net.{InetSocketAddress, SocketAddress}
+import java.io.{File, IOException}
+import org.apache.activemq.apollo.util.{FileCache, FileSupport, Log}
 
 /**
  * <p>
@@ -39,6 +39,26 @@ object SocketAddressLoginModule {
   val WHITE_LIST_OPTION = "white_list_file"
   val BLACK_LIST_OPTION = "black_list_file"
   val DEFAULT_LOG = Log(getClass)
+
+  def load_line_set(file:File):Option[Set[String]] = {
+    try {
+      import FileSupport._
+      val rc: Set[String] = file.read_text().split("\n").map(_.trim()).toSet
+      Some(rc.flatMap { line =>
+        if(line.isEmpty || line.startsWith("#")) {
+          None
+        } else {
+          Some(line)
+        }
+      })
+    } catch {
+      case e: Throwable =>
+        DEFAULT_LOG.warn(e, "Unable to load file: " + file)
+        None
+    }
+  }
+
+  val file_cache = new FileCache[Set[String]](load_line_set)
 }
 
 /**
@@ -112,15 +132,17 @@ class SocketAddressLoginModule extends L
   }
 
   def matches(file:File, address:SocketAddress):Boolean = {
-
     val needle = address match {
       case address:InetSocketAddress =>
         address.getAddress.getHostAddress
       case _ => return false
     }
 
-    import FileSupport._
-    file.read_text().split("\n").find( _.trim() == needle ).isDefined
+    file_cache.get(file) match {
+      case None => false
+      case Some(haystack) =>
+        haystack.contains(needle)
+    }
   }
 
   def commit: Boolean = {

Added: activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/FileCache.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/FileCache.scala?rev=1133926&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/FileCache.scala (added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/main/scala/org/apache/activemq/apollo/util/FileCache.scala Thu Jun  9 15:22:43 2011
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.apollo.util
+
+import java.io.File
+import java.lang.Long
+import org.fusesource.hawtdispatch._
+import scala.Some
+import java.util.concurrent.{TimeUnit, ConcurrentHashMap}
+
+
+/**
+ * <p>
+ * Class used to maintain a cache of loaded files which gets
+ * evicted periodically via async time stamp checks.
+ * </p>
+ *
+ * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
+ */
+class FileCache[T](mapper: (File)=>Option[T], evict_after:Long=1000*60*5) {
+
+  class Entry(val file:File, val modified:Long, @volatile var last_accessed:Long, val value:Option[T])
+
+  private val cache = new ConcurrentHashMap[File, Entry]()
+  private var eviction_ver = 0
+
+  def get(file:File):Option[T] = {
+    var rc = cache.get(file)
+    val now: Long = System.currentTimeMillis()
+    if( rc == null ) {
+      rc = if ( !file.exists() ) {
+        new Entry(file, 0, now, None)
+      } else {
+        new Entry(file, file.lastModified(), now, mapper(file))
+      }
+      this.synchronized {
+        cache.put(file, rc)
+        if( cache.size() == 1) {
+          eviction_ver += 1;
+          val ver = eviction_ver
+          globalQueue.after(1, TimeUnit.SECONDS)(eviction_check(ver))
+        }
+      }
+    }
+    rc.last_accessed = now
+    rc.value
+  }
+
+  def eviction_check(ver:Int):Unit = {
+    if (ver == eviction_ver) {
+      import collection.JavaConversions._
+      val evict_point = System.currentTimeMillis() - evict_after
+      val evictions = cache.values().flatMap { entry =>
+        if(
+          entry.value == None ||
+          !entry.file.exists() ||
+          entry.file.lastModified() != entry.modified ||
+          entry.last_accessed < evict_point
+        ) {
+          Some(entry.file)
+        } else {
+          None
+        }
+      }
+      evictions.foreach(f => cache.remove(f))
+      this.synchronized {
+        if( cache.size() == 0) {
+          eviction_ver += 1;
+        } else {
+          globalQueue.after(1, TimeUnit.SECONDS)(eviction_check(ver))
+        }
+      }
+    }
+  }
+
+}
\ No newline at end of file