From 09a196b0c65d2bc7b439a51b487d2a5e282888e4 Mon Sep 17 00:00:00 2001 From: Eckart Hertzler Date: Thu, 22 Oct 2009 21:38:42 +0200 Subject: [PATCH 1/5] added a sample webapp for the security actors including examples for all authentication actors --- .../src/main/resources/akka.conf | 35 +++++++ .../src/main/scala/SimpleService.scala | 96 +++++++++++++++---- .../src/main/webapp/WEB-INF/web.xml | 21 ++++ 3 files changed, 135 insertions(+), 17 deletions(-) create mode 100644 akka-samples-security/src/main/resources/akka.conf create mode 100644 akka-samples-security/src/main/webapp/WEB-INF/web.xml diff --git a/akka-samples-security/src/main/resources/akka.conf b/akka-samples-security/src/main/resources/akka.conf new file mode 100644 index 0000000000..e2a93561f0 --- /dev/null +++ b/akka-samples-security/src/main/resources/akka.conf @@ -0,0 +1,35 @@ +#################### +# Akka Config File # +#################### + +# This file has all the default settings, so all these could be remove with no visible effect. +# Modify as needed. + + + version = "0.6" + + boot = ["se.scalablesolutions.akka.security.samples.Boot"] # FQN to the class doing initial active object/actor + # supervisor bootstrap, should be defined in default constructor + + + filters = "se.scalablesolutions.akka.security.AkkaSecurityFilterFactory" + + # only one authenticator can be enabled for the security filter factory + authenticator = "se.scalablesolutions.akka.security.samples.BasicAuthenticationService" +# authenticator = "se.scalablesolutions.akka.security.samples.DigestAuthenticationService" +# authenticator = "se.scalablesolutions.akka.security.samples.SpnegoAuthenticationService" + +# +# +# servicePrincipal = "HTTP/localhost@EXAMPLE.COM" +# keyTabLocation = "URL to keytab" +# kerberosDebug = "true" +# realm = "EXAMPLE.COM" +# + + # service = on + # hostname = "localhost" + # port = 9998 + + + diff --git a/akka-samples-security/src/main/scala/SimpleService.scala b/akka-samples-security/src/main/scala/SimpleService.scala index 2f38c23ef9..db328cc6ad 100644 --- a/akka-samples-security/src/main/scala/SimpleService.scala +++ b/akka-samples-security/src/main/scala/SimpleService.scala @@ -2,7 +2,7 @@ * Copyright (C) 2009 Scalable Solutions. */ -package sample.secure +package se.scalablesolutions.akka.security.samples import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor} import se.scalablesolutions.akka.config.ScalaConfig._ @@ -10,21 +10,31 @@ import se.scalablesolutions.akka.util.Logging import se.scalablesolutions.akka.security.{DigestAuthenticationActor, UserInfo} import se.scalablesolutions.akka.state.TransactionalState -import javax.annotation.security.RolesAllowed -import javax.ws.rs.{GET, Path, Produces} - class Boot { + object factory extends SupervisorFactory { + override def getSupervisorConfig: SupervisorConfig = { SupervisorConfig( RestartStrategy(OneForOne, 3, 100), + // Dummy implementations of all authentication actors + // see akka.conf to enable one of these for the AkkaSecurityFilterFactory Supervise( - new SimpleAuthenticationService, + new BasicAuthenticationService, + LifeCycle(Permanent, 100)) :: + /** + Supervise( + new DigestAuthenticationService, LifeCycle(Permanent, 100)) :: Supervise( - new SecureService, + new SpnegoAuthenticationService, + LifeCycle(Permanent, 100)) :: + **/ + Supervise( + new SecureTickActor, LifeCycle(Permanent, 100)):: Nil) } + } val supervisor = factory.newSupervisor @@ -34,7 +44,7 @@ class Boot { /* * 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 { +class DigestAuthenticationService 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 // @@ -43,6 +53,7 @@ class SimpleAuthenticationService extends DigestAuthenticationActor { //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" @@ -50,38 +61,89 @@ class SimpleAuthenticationService extends DigestAuthenticationActor { override def userInfo(username : String) : Option[UserInfo] = Some(UserInfo(username,"bar","ninja" :: "chef" :: Nil)) } +class BasicAuthenticationService extends BasicAuthenticationActor { + + //Change this to whatever you want + override def realm = "test" + + //Dummy method that allows you to log on with whatever username + def verify(odc : Option[BasicCredentials]) : Option[UserInfo] = odc match { + case Some(dc) => userInfo(dc.username) + case _ => None + } + + //Dummy method that allows you to log on with whatever username with the password "bar" + def userInfo(username : String) : Option[UserInfo] = Some(UserInfo(username,"bar","ninja" :: "chef" :: Nil)) + +} + +class SpnegoAuthenticationService extends SpnegoAuthenticationActor { + + def rolesFor(user: String) = "ninja" :: "chef" :: Nil + +} + /** - * This is merely a secured version of the scala-sample + * a REST Actor with class level paranoia settings to deny all access * * The interesting part is * @RolesAllowed * @PermitAll * @DenyAll */ +import java.lang.Integer +import javax.annotation.security.{RolesAllowed, DenyAll, PermitAll} +import javax.ws.rs.{GET, Path, Produces} +@Path("/secureticker") +@DenyAll +class SecureTickActor extends Actor with Logging { -@Path("/securecount") -class SecureService extends Actor with Logging { makeTransactionRequired case object Tick private val KEY = "COUNTER"; private var hasStartedTicking = false; - private val storage = TransactionalState.newMap[String, Integer] + private val storage = TransactionalState.newMap[String, Integer] + /** + * allow access for any user to the resource "/secureticker/public" + */ @GET - @Produces(Array("text/html")) + @Produces(Array("text/xml")) + @Path("/public") + @PermitAll + def publicTick = tick + + /** + * restrict access to resource "/secureticker/chef" users with "chef" role + */ + @GET + @Path("/chef") + @Produces(Array("text/xml")) @RolesAllowed(Array("chef")) - def count = (this !! Tick).getOrElse(Error in counter) + def chefTick = tick + + /** + * access denied because of the class level annotation for any user + */ + @GET + @Produces(Array("text/xml")) + def paranoiaTick = tick + + def tick = (this !! Tick) match { + case(Some(counter)) => (Tick: {counter}) + case _ => (Error in counter) + } override def receive: PartialFunction[Any, Unit] = { case Tick => if (hasStartedTicking) { val counter = storage.get(KEY).get.intValue - storage.put(KEY, new Integer(counter + 1)) - reply(Tick:{counter + 1}) + storage.put(KEY, counter + 1) + reply(new Integer(counter + 1)) } else { - storage.put(KEY, new Integer(0)) + storage.put(KEY, 0) hasStartedTicking = true - reply(Tick: 0) + reply(new Integer(0)) } } } \ No newline at end of file diff --git a/akka-samples-security/src/main/webapp/WEB-INF/web.xml b/akka-samples-security/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..a04d912c1d --- /dev/null +++ b/akka-samples-security/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + + akka-security-samples + + + AkkaServlet + se.scalablesolutions.akka.rest.AkkaServlet + + + + AkkaServlet + /* + + + + + From fcd5c677e2d67f55656a40246fbd5d150781571c Mon Sep 17 00:00:00 2001 From: Eckart Hertzler Date: Fri, 23 Oct 2009 17:22:23 +0200 Subject: [PATCH 2/5] add missing @DenyAll annotation --- akka-samples-security/src/main/scala/SimpleService.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-samples-security/src/main/scala/SimpleService.scala b/akka-samples-security/src/main/scala/SimpleService.scala index db328cc6ad..e8c58ae403 100644 --- a/akka-samples-security/src/main/scala/SimpleService.scala +++ b/akka-samples-security/src/main/scala/SimpleService.scala @@ -95,7 +95,6 @@ import java.lang.Integer import javax.annotation.security.{RolesAllowed, DenyAll, PermitAll} import javax.ws.rs.{GET, Path, Produces} @Path("/secureticker") -@DenyAll class SecureTickActor extends Actor with Logging { makeTransactionRequired @@ -106,7 +105,7 @@ class SecureTickActor extends Actor with Logging { private val storage = TransactionalState.newMap[String, Integer] /** - * allow access for any user to the resource "/secureticker/public" + * allow access for any user to "/secureticker/public" */ @GET @Produces(Array("text/xml")) @@ -115,7 +114,7 @@ class SecureTickActor extends Actor with Logging { def publicTick = tick /** - * restrict access to resource "/secureticker/chef" users with "chef" role + * restrict access to "/secureticker/chef" users with "chef" role */ @GET @Path("/chef") @@ -124,10 +123,11 @@ class SecureTickActor extends Actor with Logging { def chefTick = tick /** - * access denied because of the class level annotation for any user + * access denied for any user to default Path "/secureticker/" */ @GET @Produces(Array("text/xml")) + @DenyAll def paranoiaTick = tick def tick = (this !! Tick) match { From 433abb581c28b8dcaf29bbcd79d72d90ada09672 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Fri, 23 Oct 2009 18:15:44 +0200 Subject: [PATCH 3/5] Updated FQN of Security module --- config/akka-reference.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 281853c106..06ed6056f5 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -19,7 +19,7 @@ # FQN to the class doing initial active object/actor # supervisor bootstrap, should be defined in default constructor - boot = ["sample.java.Boot", "sample.scala.Boot", "sample.secure.Boot"] + boot = ["sample.java.Boot", "sample.scala.Boot", "se.scalablesolutions.akka.security.samples.Boot"] timeout = 5000 # default timeout for future based invocations From 90d712337abf141b15a03ddf954bb329a9e1331b Mon Sep 17 00:00:00 2001 From: Eckart Hertzler Date: Fri, 23 Oct 2009 20:54:01 +0200 Subject: [PATCH 4/5] updated FQN of sample basic authentication service --- config/akka-reference.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/akka-reference.conf b/config/akka-reference.conf index 06ed6056f5..8aa9dfd6d7 100644 --- a/config/akka-reference.conf +++ b/config/akka-reference.conf @@ -54,7 +54,7 @@ hostname = "localhost" port = 9998 filters = "se.scalablesolutions.akka.security.AkkaSecurityFilterFactory" - authenticator = "sample.secure.SimpleAuthenticationService" + authenticator = "se.scalablesolutions.akka.security.samples.BasicAuthenticationService" From e270cd9996a169eb3c5b8cf867c825c18217910d Mon Sep 17 00:00:00 2001 From: "ross.mcdonald" Date: Fri, 23 Oct 2009 22:37:28 +0100 Subject: [PATCH 5/5] remove old commas --- akka-samples-lift/src/main/scala/akka/SimpleService.scala | 8 ++++---- akka-samples-scala/src/main/scala/SimpleService.scala | 8 ++++---- akka-samples-security/src/main/scala/SimpleService.scala | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/akka-samples-lift/src/main/scala/akka/SimpleService.scala b/akka-samples-lift/src/main/scala/akka/SimpleService.scala index af8e313e31..8f7537f5bf 100644 --- a/akka-samples-lift/src/main/scala/akka/SimpleService.scala +++ b/akka-samples-lift/src/main/scala/akka/SimpleService.scala @@ -21,8 +21,8 @@ class SimpleService extends Actor { makeTransactionRequired case object Tick - private val KEY = "COUNTER"; - private var hasStartedTicking = false; + private val KEY = "COUNTER" + private var hasStartedTicking = false private val storage = TransactionalState.newMap[String, Integer] @GET @@ -54,8 +54,8 @@ class PersistentSimpleService extends Actor { makeTransactionRequired case object Tick - private val KEY = "COUNTER"; - private var hasStartedTicking = false; + private val KEY = "COUNTER" + private var hasStartedTicking = false private val storage = PersistentState.newMap(CassandraStorageConfig()) @GET diff --git a/akka-samples-scala/src/main/scala/SimpleService.scala b/akka-samples-scala/src/main/scala/SimpleService.scala index 0011e4e041..9ca6ac03ec 100644 --- a/akka-samples-scala/src/main/scala/SimpleService.scala +++ b/akka-samples-scala/src/main/scala/SimpleService.scala @@ -50,8 +50,8 @@ class SimpleService extends Actor { makeTransactionRequired case object Tick - private val KEY = "COUNTER"; - private var hasStartedTicking = false; + private val KEY = "COUNTER" + private var hasStartedTicking = false private val storage = TransactionalState.newMap[String, Integer] @GET @@ -83,8 +83,8 @@ class PersistentSimpleService extends Actor { makeTransactionRequired case object Tick - private val KEY = "COUNTER"; - private var hasStartedTicking = false; + private val KEY = "COUNTER" + private var hasStartedTicking = false private val storage = PersistentState.newMap(CassandraStorageConfig()) @GET diff --git a/akka-samples-security/src/main/scala/SimpleService.scala b/akka-samples-security/src/main/scala/SimpleService.scala index e8c58ae403..827f3ad8c1 100644 --- a/akka-samples-security/src/main/scala/SimpleService.scala +++ b/akka-samples-security/src/main/scala/SimpleService.scala @@ -100,8 +100,8 @@ class SecureTickActor extends Actor with Logging { makeTransactionRequired case object Tick - private val KEY = "COUNTER"; - private var hasStartedTicking = false; + private val KEY = "COUNTER" + private var hasStartedTicking = false private val storage = TransactionalState.newMap[String, Integer] /**