added a sample webapp for the security actors including examples for all authentication actors

This commit is contained in:
Eckart Hertzler 2009-10-22 21:38:42 +02:00
parent 58a0ec2b33
commit 09a196b0c6
3 changed files with 135 additions and 17 deletions

View file

@ -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.
<akka>
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
<rest>
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"
#
# <kerberos>
# servicePrincipal = "HTTP/localhost@EXAMPLE.COM"
# keyTabLocation = "URL to keytab"
# kerberosDebug = "true"
# realm = "EXAMPLE.COM"
# </kerberos>
# service = on
# hostname = "localhost"
# port = 9998
</rest>
</akka>

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009 Scalable Solutions. * 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.actor.{SupervisorFactory, Actor}
import se.scalablesolutions.akka.config.ScalaConfig._ 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.security.{DigestAuthenticationActor, UserInfo}
import se.scalablesolutions.akka.state.TransactionalState import se.scalablesolutions.akka.state.TransactionalState
import javax.annotation.security.RolesAllowed
import javax.ws.rs.{GET, Path, Produces}
class Boot { class Boot {
object factory extends SupervisorFactory { object factory extends SupervisorFactory {
override def getSupervisorConfig: SupervisorConfig = { override def getSupervisorConfig: SupervisorConfig = {
SupervisorConfig( SupervisorConfig(
RestartStrategy(OneForOne, 3, 100), RestartStrategy(OneForOne, 3, 100),
// Dummy implementations of all authentication actors
// see akka.conf to enable one of these for the AkkaSecurityFilterFactory
Supervise( Supervise(
new SimpleAuthenticationService, new BasicAuthenticationService,
LifeCycle(Permanent, 100)) ::
/**
Supervise(
new DigestAuthenticationService,
LifeCycle(Permanent, 100)) :: LifeCycle(Permanent, 100)) ::
Supervise( Supervise(
new SecureService, new SpnegoAuthenticationService,
LifeCycle(Permanent, 100)) ::
**/
Supervise(
new SecureTickActor,
LifeCycle(Permanent, 100)):: Nil) LifeCycle(Permanent, 100)):: Nil)
} }
} }
val supervisor = factory.newSupervisor 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 * 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, //If you want to have a distributed nonce-map, you can use something like below,
//don't forget to configure your standalone Cassandra instance //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 //Use an in-memory nonce-map as default
override def mkNonceMap = new scala.collection.mutable.HashMap[String,Long] override def mkNonceMap = new scala.collection.mutable.HashMap[String,Long]
//Change this to whatever you want //Change this to whatever you want
override def realm = "test" 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)) 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 * The interesting part is
* @RolesAllowed * @RolesAllowed
* @PermitAll * @PermitAll
* @DenyAll * @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 makeTransactionRequired
case object Tick case object Tick
private val KEY = "COUNTER"; private val KEY = "COUNTER";
private var hasStartedTicking = false; 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 @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")) @RolesAllowed(Array("chef"))
def count = (this !! Tick).getOrElse(<error>Error in counter</error>) 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)) => (<success>Tick: {counter}</success>)
case _ => (<error>Error in counter</error>)
}
override def receive: PartialFunction[Any, Unit] = { override def receive: PartialFunction[Any, Unit] = {
case Tick => if (hasStartedTicking) { case Tick => if (hasStartedTicking) {
val counter = storage.get(KEY).get.intValue val counter = storage.get(KEY).get.intValue
storage.put(KEY, new Integer(counter + 1)) storage.put(KEY, counter + 1)
reply(<success>Tick:{counter + 1}</success>) reply(new Integer(counter + 1))
} else { } else {
storage.put(KEY, new Integer(0)) storage.put(KEY, 0)
hasStartedTicking = true hasStartedTicking = true
reply(<success>Tick: 0</success>) reply(new Integer(0))
} }
} }
} }

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>akka-security-samples</display-name>
<servlet>
<servlet-name>AkkaServlet</servlet-name>
<servlet-class>se.scalablesolutions.akka.rest.AkkaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AkkaServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>