diff --git a/README.textile b/README.textile index b241c1605d..c729b0901c 100755 --- a/README.textile +++ b/README.textile @@ -1,68 +1,70 @@ -h1. Akka: RESTful Distributed Persistent Transactional Actors - -h3. "http://akkasource.org":http://akkasource.org - -Akka implements a unique hybrid of: -* The Actor model (Actors and Active Objects), which gives you: -** Concurrency (high-level and simple) -** Asynchronous, non-blocking and highly performant components. -** Supervision with "let-it-crash" semantics. Components are loosely coupled and restarted upon failure. -* Software Transactional Memory (STM). -* BASE and ACID persistence - Pluggable Eventually Consistent or ACID distributed scalable persistent storage. -* Remoting - Distributed services with supervision and error management -* REST (JAX-RS) and Comet bindings. -* Monitoring and Management - -Akka can be used in two different ways: -* As a library: used by a web app, to be put into ‘WEB-INF/lib’ -* As a kernel: stand-alone kernel, embedding the servlet container - -See the "Use-case and Deployment Scenarios":http://wiki.github.com/jboner/akka/use-case-and-deployment-scenarios for details. - -h1. What's Akka all about? Why should I care? - -If you are new to Akka then I suggest you start with either the: - -* "High Level View":http://wiki.github.com/jboner/akka/modules-the-high-level-view; which is outlining the different modules in Akka. -* "Use-case and Deployment Scenarios":http://wiki.github.com/jboner/akka/use-case-and-deployment-scenarios; outlining how and in which use-case and deployment scenarios can I use Akka? -* "Examples":http://wiki.github.com/jboner/akka/examples; showing how to build a RESTful, transactional, persistent Active Object and Actor. - -After that you can dive into the "Reference Manual":http://wiki.github.com/jboner/akka/akka-reference-manual. - -h1. Documentation - -Akka has pretty thorough "reference documentation":https://github.com/jboner/akka/wikis. Covering examples, APIs and configuration. - -h1. Distribution - -The latest distribution can be found in the "downloads section":https://github.com/jboner/akka/downloads - -h1. Mailing List - -If you have questions and/or feedback: please sign up to the Akka User mailing list: -"http://groups.google.com/group/akka-user":http://groups.google.com/group/akka-user - -h1. Professional Support - -Scalable Solutions AB is providing a variety of professional support packages for Akka, please visit their website for details: -"http://scalablesolutions.se":http://scalablesolutions.se - -h1. License - -
-This software is licensed under the Apache 2 license, quoted below.
-
-Copyright 2009 Scalable Solutions AB 
-
-Licensed 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.
-
+h1. Akka Transactors + +h2. RESTful Distributed Persistent Transactional Actors + +h3. "http://akkasource.org":http://akkasource.org + +Akka implements a unique hybrid of: +* The Actor model (Actors and Active Objects), which gives you: +** Concurrency (high-level and simple) +** Asynchronous, non-blocking and highly performant components. +** Supervision with "let-it-crash" semantics. Components are loosely coupled and restarted upon failure. +* Software Transactional Memory (STM). +* BASE and ACID persistence - Pluggable Eventually Consistent or ACID distributed scalable persistent storage. +* Remoting - Distributed services with supervision and error management +* REST (JAX-RS) and Comet bindings. +* Monitoring and Management + +Akka can be used in two different ways: +* As a library: used by a web app, to be put into ‘WEB-INF/lib’ +* As a kernel: stand-alone kernel, embedding the servlet container + +See the "Use-case and Deployment Scenarios":http://wiki.github.com/jboner/akka/use-case-and-deployment-scenarios for details. + +h1. What's Akka all about? Why should I care? + +If you are new to Akka then I suggest you start with either the: + +* "High Level View":http://wiki.github.com/jboner/akka/modules-the-high-level-view; which is outlining the different modules in Akka. +* "Use-case and Deployment Scenarios":http://wiki.github.com/jboner/akka/use-case-and-deployment-scenarios; outlining how and in which use-case and deployment scenarios can I use Akka? +* "Examples":http://wiki.github.com/jboner/akka/examples; showing how to build a RESTful, transactional, persistent Active Object and Actor. + +After that you can dive into the "Reference Manual":http://wiki.github.com/jboner/akka/akka-reference-manual. + +h1. Documentation + +Akka has pretty thorough "reference documentation":https://github.com/jboner/akka/wikis. Covering examples, APIs and configuration. + +h1. Distribution + +The latest distribution can be found in the "downloads section":https://github.com/jboner/akka/downloads + +h1. Mailing List + +If you have questions and/or feedback: please sign up to the Akka User mailing list: +"http://groups.google.com/group/akka-user":http://groups.google.com/group/akka-user + +h1. Professional Support + +Scalable Solutions AB is providing a variety of professional support packages for Akka, please visit their website for details: +"http://scalablesolutions.se":http://scalablesolutions.se + +h1. License + +
+This software is licensed under the Apache 2 license, quoted below.
+
+Copyright 2009 Scalable Solutions AB 
+
+Licensed 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.
+
diff --git a/akka-actors/src/main/scala/actor/ActorRegistry.scala b/akka-actors/src/main/scala/actor/ActorRegistry.scala index 2a534918d1..6247d4d93e 100755 --- a/akka-actors/src/main/scala/actor/ActorRegistry.scala +++ b/akka-actors/src/main/scala/actor/ActorRegistry.scala @@ -16,18 +16,20 @@ import scala.collection.jcl.HashMap object ActorRegistry extends Logging { private val actors = new HashMap[String, List[Actor]] - def actorsFor(clazz: Class[_]): List[Actor] = synchronized { - actors.get(clazz.getName) match { + def actorsFor(fqn : String): List[Actor] = synchronized { + actors.get(fqn) match { case None => Nil case Some(instances) => instances } } - + + def actorsFor(clazz: Class[_]) : List[Actor] = actorsFor(clazz.getName) + def register(actor: Actor) = synchronized { val name = actor.getClass.getName actors.get(name) match { case Some(instances) => actors + (name -> (actor :: instances)) - case None => actors + (name -> (actor :: Nil)) + case None => actors + (name -> (actor :: Nil)) } } } diff --git a/akka-actors/src/main/scala/actor/Scheduler.scala b/akka-actors/src/main/scala/actor/Scheduler.scala index 42e4b7485a..03404eda9d 100644 --- a/akka-actors/src/main/scala/actor/Scheduler.scala +++ b/akka-actors/src/main/scala/actor/Scheduler.scala @@ -11,11 +11,12 @@ * limitations under the License. */ -package se.scalablesolutions.akka.util +package se.scalablesolutions.akka.actor import java.util.concurrent._ -import actor.{OneForOneStrategy, Actor} import config.ScalaConfig._ +import _root_.se.scalablesolutions.akka.util.{Logging} + import org.scala_tools.javautils.Imports._ diff --git a/akka-actors/src/main/scala/stm/TransactionalState.scala b/akka-actors/src/main/scala/stm/TransactionalState.scala index 769b97b948..cf626fc5a9 100644 --- a/akka-actors/src/main/scala/stm/TransactionalState.scala +++ b/akka-actors/src/main/scala/stm/TransactionalState.scala @@ -4,7 +4,6 @@ package se.scalablesolutions.akka.state -//import org.multiverse.datastructures.refs.manual.Ref import stm.{TransactionManagement, Ref} import org.multiverse.templates.AtomicTemplate import org.multiverse.api.Transaction; @@ -12,8 +11,6 @@ import akka.collection._ import org.codehaus.aspectwerkz.proxy.Uuid -import scala.collection.mutable.{ArrayBuffer, HashMap} - /** * Example Scala usage: *
@@ -68,7 +65,7 @@ object TransactionalRef {
  *
  * @author Jonas Bonér
  */
-class TransactionalRef[+T] extends Transactional {
+class TransactionalRef[T] extends Transactional {
   private[this] val ref = new Ref[T]
 
   def swap(elem: T) = ref.set(elem)
@@ -93,7 +90,7 @@ class TransactionalRef[+T] extends Transactional {
 
   def flatMap[B](f: T => Option[B]): Option[B] = if (isEmpty) None else f(ref.get)
 
-  def filter(p: T => Boolean): Option[T] = if (isEmpty || p(ref.get)) this else None
+  def filter(p: T => Boolean): Option[T] = if (isEmpty || p(ref.get)) Some(ref.get) else None
 
   def foreach(f: T => Unit) { if (!isEmpty) f(ref.get) }
 
@@ -104,8 +101,6 @@ class TransactionalRef[+T] extends Transactional {
   def toRight[X](left: => X) = if (isEmpty) Left(left) else Right(ref.get)
 
   def toLeft[X](right: => X) = if (isEmpty) Right(right) else Left(ref.get)
-
-  def orElse[B >: T](alternative: => TransactionalRef[B]): TransactionalRef[B] = if (isEmpty) alternative else this
 }
 
 object TransactionalMap {
@@ -160,7 +155,7 @@ class TransactionalMap[K, V] extends Transactional with scala.collection.mutable
 }
 
 object TransactionalVector {
-  def apply[T]() = new TransactionalVector
+  def apply[T]() = new TransactionalVector[T]
 }
 
 /**
diff --git a/akka-actors/src/test/scala/AllTest.scala b/akka-actors/src/test/scala/AllTest.scala
index 0132daea26..6e782232e7 100644
--- a/akka-actors/src/test/scala/AllTest.scala
+++ b/akka-actors/src/test/scala/AllTest.scala
@@ -4,9 +4,8 @@ import junit.framework.Test
 import junit.framework.TestCase
 import junit.framework.TestSuite
 
-import actor.{ActorSpec, RemoteActorSpec, InMemoryActorSpec, SupervisorSpec, RemoteSupervisorSpec}
+import actor.{ActorSpec, RemoteActorSpec, InMemoryActorSpec, SupervisorSpec, RemoteSupervisorSpec,SchedulerSpec}
 import reactor.{EventBasedSingleThreadDispatcherTest, EventBasedThreadPoolDispatcherTest}
-import util.SchedulerSpec
 
 object AllTest extends TestCase {
   def suite(): Test = {
diff --git a/akka-actors/src/test/scala/SchedulerSpec.scala b/akka-actors/src/test/scala/SchedulerSpec.scala
index c1ee474ecf..b7b801bce6 100644
--- a/akka-actors/src/test/scala/SchedulerSpec.scala
+++ b/akka-actors/src/test/scala/SchedulerSpec.scala
@@ -1,6 +1,4 @@
-package se.scalablesolutions.akka.util
-
-import se.scalablesolutions.akka.actor.Actor
+package se.scalablesolutions.akka.actor
 
 import java.util.concurrent.TimeUnit
 
diff --git a/akka-kernel/pom.xml b/akka-kernel/pom.xml
index 66242c456d..2e57bb67a6 100755
--- a/akka-kernel/pom.xml
+++ b/akka-kernel/pom.xml
@@ -42,6 +42,11 @@
       se.scalablesolutions.akka
       0.6
     
+	
+      akka-security
+      se.scalablesolutions.akka
+      0.6
+    
 
     
     
diff --git a/akka-kernel/src/main/scala/AkkaServlet.scala b/akka-kernel/src/main/scala/AkkaServlet.scala
index 15862ff43e..dfb70c3445 100755
--- a/akka-kernel/src/main/scala/AkkaServlet.scala
+++ b/akka-kernel/src/main/scala/AkkaServlet.scala
@@ -35,10 +35,7 @@ class AkkaServlet extends ServletContainer with AtmosphereServletProcessor with
     val configurators = ConfiguratorRepository.getConfigurators
 
     rc.getClasses.addAll(configurators.flatMap(_.getComponentInterfaces))
-    log.info("Starting AkkaServlet with ResourceFilters: " + rc.getProperty("com.sun.jersey.spi.container.ResourceFilters"));
-    rc.getProperties.put("com.sun.jersey.spi.container.ResourceFilters", "org.atmosphere.core.AtmosphereFilter")
-    //rc.getFeatures.put("com.sun.jersey.config.feature.Redirect", true)
-    //rc.getFeatures.put("com.sun.jersey.config.feature.ImplicitViewables",true)
+    rc.getProperties.put("com.sun.jersey.spi.container.ResourceFilters", akka.Config.config.getString("akka.rest.filters").getOrElse(""))
 
     wa.initiate(rc, new ActorComponentProviderFactory(configurators))
   }
diff --git a/akka-persistence/src/main/scala/PersistentState.scala b/akka-persistence/src/main/scala/PersistentState.scala
index 332da23b33..61fa249cff 100644
--- a/akka-persistence/src/main/scala/PersistentState.scala
+++ b/akka-persistence/src/main/scala/PersistentState.scala
@@ -108,8 +108,8 @@ trait PersistentMap extends scala.collection.mutable.Map[AnyRef, AnyRef] with Tr
   override def get(key: AnyRef): Option[AnyRef] = {
     if (newAndUpdatedEntries.contains(key)) newAndUpdatedEntries.get(key)
     else try {
-        storage.getMapStorageEntryFor(uuid, key)
-      } catch { case e: Exception => None }
+      storage.getMapStorageEntryFor(uuid, key)
+    } catch { case e: Exception => None }
   }
   
   override def elements: Iterator[Tuple2[AnyRef, AnyRef]]  = {
diff --git a/akka-samples-security/pom.xml b/akka-samples-security/pom.xml
new file mode 100644
index 0000000000..d870bdac17
--- /dev/null
+++ b/akka-samples-security/pom.xml
@@ -0,0 +1,83 @@
+
+  4.0.0
+
+  akka-samples-security
+  Akka Sample Security Module
+
+  jar
+
+  
+    akka
+    se.scalablesolutions.akka
+    0.6
+    ../pom.xml
+  
+
+  
+    
+      akka-kernel
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-util-java
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-util
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-actors
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-security
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-persistence
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      javax.ws.rs
+      jsr311-api
+      1.0
+    
+    
+        javax.annotation
+        jsr250-api
+        1.0
+    
+
+  
+
+  
+    src/main/scala
+    
+      
+        maven-antrun-plugin
+        
+          
+            install
+            
+              
+                
+              
+            
+            
+              run
+            
+          
+        
+      
+    
+  
+
diff --git a/akka-samples-security/src/main/scala/SimpleService.scala b/akka-samples-security/src/main/scala/SimpleService.scala
new file mode 100644
index 0000000000..a3627586e6
--- /dev/null
+++ b/akka-samples-security/src/main/scala/SimpleService.scala
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package sample.secure
+
+import _root_.se.scalablesolutions.akka.state.{TransactionalState,PersistentState, CassandraStorageConfig}
+import _root_.se.scalablesolutions.akka.actor.{SupervisorFactory, Actor}
+import _root_.se.scalablesolutions.akka.config.ScalaConfig._
+import _root_.se.scalablesolutions.akka.util.Logging
+import _root_.se.scalablesolutions.akka.security.{DigestAuthenticationActor, UserInfo}
+import _root_.javax.annotation.security.{DenyAll,PermitAll,RolesAllowed}
+import javax.ws.rs.{GET, POST, Path, Produces, Consumes}
+
+class Boot {
+  object factory extends SupervisorFactory {
+    override def getSupervisorConfig: SupervisorConfig = {
+      SupervisorConfig(
+        RestartStrategy(OneForOne, 3, 100),
+        Supervise(
+          new SimpleAuthenticationService,
+          LifeCycle(Permanent, 100)) ::
+        Supervise(
+          new SecureService,
+          LifeCycle(Permanent, 100)):: Nil)
+    }
+  }
+
+  val supervisor = factory.newSupervisor
+  supervisor.startSupervisor
+}
+
+/*
+ * In akka.conf you can set the FQN of any AuthenticationActor of your wish, under the property name: akka.rest.authenticator
+ */
+class SimpleAuthenticationService extends DigestAuthenticationActor
+{
+    //If you want to have a distributed nonce-map, you can use something like below,
+    //don't forget to configure your standalone Cassandra instance
+    //
+    //makeTransactionRequired
+    //override def mkNonceMap = PersistentState.newMap(CassandraStorageConfig()).asInstanceOf[scala.collection.mutable.Map[String,Long]]
+
+    //Use an in-memory nonce-map as default
+    override def mkNonceMap = new scala.collection.mutable.HashMap[String,Long]
+    //Change this to whatever you want
+    override def realm = "test"
+
+    //Dummy method that allows you to log on with whatever username with the password "bar"
+    override def userInfo(username : String) : Option[UserInfo] = Some(UserInfo(username,"bar","ninja" :: "chef" :: Nil))
+}
+
+/**
+ * This is merely a secured version of the scala-sample
+ *
+ * The interesting part is
+ *  @RolesAllowed
+ *  @PermitAll
+ *  @DenyAll
+ */
+
+@Path("/securecount")
+class SecureService extends Actor with Logging {
+  makeTransactionRequired
+
+  case object Tick
+  private val KEY = "COUNTER";
+  private var hasStartedTicking = false;
+  private val storage = PersistentState.newMap(CassandraStorageConfig())
+
+  @GET
+  @Produces(Array("text/html"))
+  @RolesAllowed(Array("chef"))
+  def count = (this !! Tick).getOrElse(Error in counter)
+
+  override def receive: PartialFunction[Any, Unit] = {
+    case Tick => if (hasStartedTicking) {
+      val counter = storage.get(KEY).get.asInstanceOf[Integer].intValue
+      storage.put(KEY, new Integer(counter + 1))
+      reply(Tick:{counter + 1})
+    } else {
+      storage.put(KEY, new Integer(0))
+      hasStartedTicking = true
+      reply(Tick: 0)
+    }
+  }
+}
\ No newline at end of file
diff --git a/akka-security/pom.xml b/akka-security/pom.xml
new file mode 100644
index 0000000000..8862b10f18
--- /dev/null
+++ b/akka-security/pom.xml
@@ -0,0 +1,65 @@
+
+  4.0.0
+
+  akka-security
+  Akka Security Module
+
+  jar
+
+  
+    akka
+    se.scalablesolutions.akka
+    0.6
+    ../pom.xml
+  
+
+  
+    
+      org.scala-lang
+      scala-library
+      2.7.5
+    
+    
+    
+      akka-actors
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-persistence
+      se.scalablesolutions.akka
+      0.6
+    
+    
+      akka-util
+      se.scalablesolutions.akka
+      0.6
+    
+    
+        javax.annotation
+        jsr250-api
+        1.0
+    
+    
+      com.sun.jersey
+      jersey-server
+      1.1.1-ea
+    
+    
+      javax.ws.rs
+      jsr311-api
+      1.0
+    
+    
+        net.liftweb
+        lift-util
+        1.1-SNAPSHOT
+    
+  
+
+
diff --git a/akka-security/src/main/scala/Security.scala b/akka-security/src/main/scala/Security.scala
new file mode 100644
index 0000000000..b2f26e1bfe
--- /dev/null
+++ b/akka-security/src/main/scala/Security.scala
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2007-2008 WorldWide Conferencing, LLC
+ *
+ * Licensed 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.
+ */
+
+/*
+ * AKKA AAS (Authentication and Authorization Service)
+ * Rework of lift's (www.liftweb.com) HTTP Authentication module
+ * All cred to the Lift team (www.liftweb.com), especially David Pollak and Tim Perrett
+ */
+
+package se.scalablesolutions.akka.security
+
+import _root_.se.scalablesolutions.akka.actor.{Scheduler,Actor,ActorRegistry}
+import _root_.se.scalablesolutions.akka.state.{TransactionalState,PersistentStorageConfig}
+import _root_.se.scalablesolutions.akka.util.{Logging}
+
+import _root_.com.sun.jersey.api.model.AbstractMethod
+import _root_.com.sun.jersey.spi.container.{ResourceFilterFactory,ContainerRequest,ContainerRequestFilter,ContainerResponse,ContainerResponseFilter,ResourceFilter}
+import _root_.com.sun.jersey.core.util.Base64
+import _root_.javax.ws.rs.core.{SecurityContext,Context,Response}
+import _root_.javax.ws.rs.WebApplicationException
+import _root_.javax.annotation.security.{DenyAll,PermitAll,RolesAllowed}
+import _root_.java.security.Principal
+import _root_.java.util.concurrent.TimeUnit
+
+import _root_.net.liftweb.util.{SecurityHelpers, StringHelpers,IoHelpers}
+
+object Enc extends SecurityHelpers with StringHelpers with IoHelpers
+
+case object OK
+
+/**
+ * Authenticate represents a message to authenticate a request
+ */
+case class Authenticate(val req : ContainerRequest, val rolesAllowed : List[String])
+
+/**
+ * User info represents a sign-on with associated credentials/roles
+ */
+case class UserInfo(val username : String,val password : String,val roles : List[String])
+
+
+
+trait Credentials
+
+case class BasicCredentials(username : String, password : String) extends Credentials
+
+case class DigestCredentials(method: String,
+                             userName: String,
+                             realm: String,
+                             nonce: String,
+                             uri: String,
+                             qop: String,
+                             nc: String,
+                             cnonce: String,
+                             response: String,
+                             opaque: String) extends Credentials
+
+/**
+ * Jersey Filter for invocation intercept and authorization/authentication
+ */
+class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging {
+
+    class Filter(actor : Actor,rolesAllowed : Option[List[String]]) extends ResourceFilter with ContainerRequestFilter with Logging {
+
+        override def getRequestFilter : ContainerRequestFilter = this
+        override def getResponseFilter : ContainerResponseFilter = null
+
+        /**
+         * Here's where the magic happens. The request is authenticated by
+         * sending a request for authentication to the configured authenticator actor
+         */
+        override def filter(request : ContainerRequest) : ContainerRequest =
+            rolesAllowed match {
+                case Some(roles) => {
+
+                    val result : AnyRef = (authenticator !? Authenticate(request,roles))
+
+                    result match {
+                        case OK => request
+                        case r if r.isInstanceOf[Response] =>
+                            throw new WebApplicationException(r.asInstanceOf[Response])
+                        case x => {
+                            log.error("Authenticator replied with unexpected result: ",x);
+                            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR)
+                        }
+                    }
+                }
+                case None => throw new WebApplicationException(Response.Status.FORBIDDEN)
+            }
+    }
+
+    lazy val authenticatorFQN = akka.Config.config.getString("akka.rest.authenticator").getOrElse(throw new IllegalStateException("akka.rest.authenticator"))
+
+    /**
+     * Currently we always take the first, since there usually should be at most one authentication actor, but a round-robin
+     * strategy could be implemented in the future
+     */
+    def authenticator : Actor = ActorRegistry.actorsFor(authenticatorFQN).head
+
+    def mkFilter(roles : Option[List[String]]) : java.util.List[ResourceFilter] = java.util.Collections.singletonList(new Filter(authenticator,roles))
+
+    /**
+     * The create method is invoked for each resource, and we look for javax.annotation.security annotations
+     * and create the appropriate Filter configurations for each.
+     */
+    override def create(am : AbstractMethod) : java.util.List[ResourceFilter] = {
+
+        //DenyAll takes precedence
+        if (am.isAnnotationPresent(classOf[DenyAll]))
+            return mkFilter(None)
+
+        //Method-level RolesAllowed takes precedence
+        val ra = am.getAnnotation(classOf[RolesAllowed])
+
+        if (ra ne null)
+            return mkFilter(Some(ra.value.toList))
+
+        //PermitAll takes precedence over resource-level RolesAllowed annotation
+        if (am.isAnnotationPresent(classOf[PermitAll]))
+            return null;
+
+        //Last but not least, the resource-level RolesAllowed
+        val cra = am.getResource.getAnnotation(classOf[RolesAllowed])
+        if (cra ne null)
+            return mkFilter(Some(ra.value.toList))
+
+        return null;
+    }
+}
+
+/**
+ * AuthenticationActor is the super-trait for actors doing Http authentication
+ * It defines the common ground and the flow of execution
+ */
+trait AuthenticationActor[C <: Credentials] extends Actor with Logging
+{
+    type Req = ContainerRequest
+
+    //What realm does the authentication use?
+    def realm : String
+
+    //Creates a response to signal unauthorized
+    def unauthorized : Response
+
+    //Used to extract information from the request, returns None if no credentials found
+    def extractCredentials(r : Req) : Option[C]
+
+    //returns None is unverified
+    def verify(c : Option[C]) : Option[UserInfo]
+
+    //Contruct a new SecurityContext from the supplied parameters
+    def mkSecurityContext(r : Req, user : UserInfo) : SecurityContext
+
+    //This is the default security context factory
+    def mkDefaultSecurityContext(r : Req,u : UserInfo, scheme : String) : SecurityContext = {
+        val n = u.username
+        val p = new Principal { def getName = n }
+        
+        new SecurityContext {
+            def getAuthenticationScheme = scheme
+            def getUserPrincipal = p
+            def isSecure = r.isSecure
+            def isUserInRole(role : String) = u.roles.exists(_ == role)
+        }
+    }
+
+    /**
+     * Responsible for the execution flow of authentication
+     *
+     * Credentials are extracted and verified from the request,
+     * and a se3curity context is created for the ContainerRequest
+     * this should ensure good integration with current Jersey security
+     */
+    protected val authenticate: PartialFunction[Any,Unit] = {
+        case Authenticate(req,roles) => {
+                    verify(extractCredentials(req)) match {
+                        case Some(u : UserInfo) => {
+
+                                req.setSecurityContext(mkSecurityContext(req,u))
+
+                                if(roles.exists(req.isUserInRole(_)))
+                                    reply(OK)
+                                else
+                                    reply(Response.status(Response.Status.FORBIDDEN).build)
+                        }
+                        case _ => reply(unauthorized)
+                    }
+        }
+    }
+
+    override def receive: PartialFunction[Any, Unit] = authenticate
+
+    //returns the string value of the "Authorization"-header of the request
+    def auth(r : Req) = r.getHeaderValue("Authorization")
+
+    //Turns the aforementioned header value into an option
+    def authOption(r : Req) : Option[String] = {
+        val a = auth(r)
+        if(a != null && a.length > 0) Some(a) else None
+    }
+}
+
+/**
+ * This trait implements the logic for Http Basic authentication
+ * mix this trait into a class to create an authenticator
+ * Don't forget to set the authenticator FQN in the rest-part of the akka config
+ */
+trait BasicAuthenticationActor extends AuthenticationActor[BasicCredentials]
+{
+    override def unauthorized =
+            Response.status(401).header("WWW-Authenticate","Basic realm=\"" + realm + "\"").build
+
+    override def extractCredentials(r : Req) : Option[BasicCredentials] = {
+        val a = r.getHeaderValue("Authorization")
+        new String(Base64.decode(a.substring(6,a.length).getBytes)).split(":").toList match {
+        case userName :: password :: _ => Some(BasicCredentials(userName, password))
+        case userName :: Nil =>           Some(BasicCredentials(userName, ""))
+        case _ => None
+      }
+    }
+
+    override def mkSecurityContext(r : Req,u : UserInfo) : SecurityContext =
+        mkDefaultSecurityContext(r,u,SecurityContext.BASIC_AUTH)
+}
+
+/**
+ * This trait implements the logic for Http Digest authentication
+ * mix this trait into a class to create an authenticator
+ * Don't forget to set the authenticator FQN in the rest-part of the akka config
+ */
+trait DigestAuthenticationActor extends AuthenticationActor[DigestCredentials]
+{
+    import Enc._
+
+    private object InvalidateNonces
+
+    //Holds the generated nonces for the specified validity period
+    val nonceMap = mkNonceMap
+
+    //Discards old nonces
+    protected val invalidateNonces: PartialFunction[Any,Unit] = {
+        case InvalidateNonces =>
+        {
+            val ts = System.currentTimeMillis
+
+            nonceMap.retain((k,v) => (ts - v) < nonceValidityPeriod)
+        }
+
+        case e => log.info("Don't know what to do with: " + e)
+    }
+
+    //Schedule the invalidation of nonces
+    Scheduler.schedule(this, InvalidateNonces, noncePurgeInterval, noncePurgeInterval, TimeUnit.MILLISECONDS )
+
+    //authenticate or invalidate nonces
+    override def receive: PartialFunction[Any, Unit] = authenticate orElse invalidateNonces
+
+    override def unauthorized : Response =
+    {
+        val nonce = randomString(64);
+        nonceMap.put(nonce,System.currentTimeMillis)
+        unauthorized(nonce,"auth",randomString(64))
+    }
+
+    def unauthorized(nonce : String, qop : String, opaque : String) : Response =
+    {
+        Response.status(401).header("WWW-Authenticate",
+                                            "Digest realm=\"" + realm + "\", " +
+                                            "qop=\"" + qop + "\", " +
+                                            "nonce=\"" + nonce + "\", " +
+                                            "opaque=\"" + opaque + "\"").build
+    }
+
+    //Tests wether the specified credentials are valid
+    def validate(auth: DigestCredentials,user : UserInfo) : Boolean = {
+            def h(s : String) = hexEncode(md5(s.getBytes("UTF-8")))
+
+            val ha1 = h(auth.userName + ":" + auth.realm  + ":" + user.password)
+            val ha2 = h(auth.method   + ":" + auth.uri)
+
+            val response = h(ha1      + ":" + auth.nonce  + ":" +
+                             auth.nc  + ":" + auth.cnonce + ":" +
+                             auth.qop + ":" + ha2)
+
+            (response == auth.response) && (nonceMap.getOrElse(auth.nonce, -1) != -1)
+      }
+
+    override def verify(odc : Option[DigestCredentials]) : Option[UserInfo] = odc match {
+        case Some(dc) => {
+                userInfo(dc.userName) match {
+                    case Some(u) if validate(dc,u) =>
+                                  nonceMap.get(dc.nonce).map( t => (System.currentTimeMillis - t) < nonceValidityPeriod ).map(_ => u)
+                    case _ => None
+                }
+        }
+        case _ => None
+    }
+
+    override def extractCredentials(r : Req) : Option[DigestCredentials] =
+    {
+        authOption(r).map( s => {
+                val ? = splitNameValuePairs(s.substring(7,s.length ))
+
+                DigestCredentials(r.getMethod.toUpperCase,  ?("username"), ?("realm"),    ?("nonce"),
+                                                            ?("uri"),      ?("qop"),      ?("nc"),
+                                                            ?("cnonce"),   ?("response"), ?("opaque"))
+            })
+    }
+
+    override def mkSecurityContext(r : Req,u : UserInfo) : SecurityContext =
+        mkDefaultSecurityContext(r,u,SecurityContext.DIGEST_AUTH)
+
+    //Mandatory overrides
+    def userInfo(username : String) : Option[UserInfo]
+
+    def mkNonceMap : scala.collection.mutable.Map[String,Long]
+
+    //Optional overrides
+    def nonceValidityPeriod = 60*1000//ms
+    def noncePurgeInterval = 2*60*1000 //ms
+}
\ No newline at end of file
diff --git a/akka.ipr b/akka.ipr
index ed7695be70..7f0eb5d60c 100644
--- a/akka.ipr
+++ b/akka.ipr
@@ -261,6 +261,8 @@
       
       
       
+      
+      
       
       
     
@@ -1384,6 +1386,50 @@
         
       
     
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
+    
+      
+        
+      
+      
+        
+      
+      
+        
+      
+    
   
   
     
diff --git a/akka.iws b/akka.iws
index 650d20a2ac..284b10e21d 100644
--- a/akka.iws
+++ b/akka.iws
@@ -2,17 +2,9 @@
 
   
     
-      
-      
-      
-      
-      
-      
+      
       
       
-      
-      
-      
     
     
     
@@ -120,16 +112,34 @@
       
         
           
-            
+            
               
             
           
         
       
-      
+      
         
           
-            
+            
+              
+            
+          
+        
+      
+      
+        
+          
+            
+              
+            
+          
+        
+      
+      
+        
+          
+            
               
             
           
@@ -155,10 +165,10 @@
         
   
@@ -759,27 +769,6 @@
         
       
     
-    
-      
-        
-          
-        
-      
-    
-    
-      
-        
-          
-        
-      
-    
-    
-      
-        
-          
-        
-      
-    
     
       
         
@@ -789,7 +778,28 @@
     
     
       
-        
+        
+          
+        
+      
+    
+    
+      
+        
+          
+        
+      
+    
+    
+      
+        
+          
+        
+      
+    
+    
+      
+        
           
         
       
diff --git a/config/akka-reference.conf b/config/akka-reference.conf
index 569f9fd9c5..cabe677c11 100644
--- a/config/akka-reference.conf
+++ b/config/akka-reference.conf
@@ -17,7 +17,7 @@
 
   version = "0.6"
  
-  boot = ["sample.java.Boot", "sample.scala.Boot"] # FQN to the class doing initial active object/actor
+  boot = ["sample.java.Boot", "sample.scala.Boot", "sample.secure.Boot"] # FQN to the class doing initial active object/actor
                                                    # supervisor bootstrap, should be defined in default constructor
   
     timeout = 5000 # default timeout for future based invocations
@@ -44,6 +44,8 @@
     service = on
     hostname = "localhost"
     port = 9998
+    filters = "se.scalablesolutions.akka.security.AkkaSecurityFilterFactory;org.atmosphere.core.AtmosphereFilter"
+    authenticator = "sample.secure.SimpleAuthenticationService"
   
   
   
diff --git a/pom.xml b/pom.xml
index 0fa7b50324..688563fc0f 100755
--- a/pom.xml
+++ b/pom.xml
@@ -25,11 +25,13 @@
     akka-rest
     akka-camel
     akka-amqp
+    akka-security
     akka-kernel
     akka-fun-test-java
     akka-samples-scala
     akka-samples-lift
     akka-samples-java
+    akka-samples-security