pekko/akka-samples-security/src/main/scala/SimpleService.scala

149 lines
4.4 KiB
Scala
Raw Normal View History

2009-09-07 18:42:15 +02:00
/**
* Copyright (C) 2009 Scalable Solutions.
*/
package se.scalablesolutions.akka.security.samples
2009-09-07 18:42:15 +02:00
import se.scalablesolutions.akka.actor.{SupervisorFactory, Actor}
import se.scalablesolutions.akka.config.ScalaConfig._
import se.scalablesolutions.akka.util.Logging
import se.scalablesolutions.akka.security.{DigestAuthenticationActor, UserInfo}
import se.scalablesolutions.akka.state.TransactionalState
2009-09-07 18:42:15 +02:00
class Boot {
2009-09-07 18:42:15 +02:00
object factory extends SupervisorFactory {
2009-09-07 18:42:15 +02:00
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 BasicAuthenticationService,
LifeCycle(Permanent, 100)) ::
/**
Supervise(
new DigestAuthenticationService,
LifeCycle(Permanent, 100)) ::
2009-09-07 18:42:15 +02:00
Supervise(
new SpnegoAuthenticationService,
2009-09-07 18:42:15 +02:00
LifeCycle(Permanent, 100)) ::
**/
2009-09-07 18:42:15 +02:00
Supervise(
new SecureTickActor,
2009-09-07 18:42:15 +02:00
LifeCycle(Permanent, 100)):: Nil)
}
2009-09-07 18:42:15 +02:00
}
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 DigestAuthenticationService extends DigestAuthenticationActor {
2009-09-07 18:42:15 +02:00
//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]
2009-09-07 18:42:15 +02:00
//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))
}
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
}
2009-09-07 18:42:15 +02:00
/**
* a REST Actor with class level paranoia settings to deny all access
2009-09-07 18:42:15 +02:00
*
* 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 {
2009-09-07 18:42:15 +02:00
makeTransactionRequired
case object Tick
private val KEY = "COUNTER";
private var hasStartedTicking = false;
private val storage = TransactionalState.newMap[String, Integer]
/**
* allow access for any user to the resource "/secureticker/public"
*/
@GET
@Produces(Array("text/xml"))
@Path("/public")
@PermitAll
def publicTick = tick
2009-09-07 18:42:15 +02:00
/**
* restrict access to resource "/secureticker/chef" users with "chef" role
*/
2009-09-07 18:42:15 +02:00
@GET
@Path("/chef")
@Produces(Array("text/xml"))
2009-09-07 18:42:15 +02:00
@RolesAllowed(Array("chef"))
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>)
}
2009-09-07 18:42:15 +02:00
override def receive: PartialFunction[Any, Unit] = {
case Tick => if (hasStartedTicking) {
2009-10-19 10:52:18 +02:00
val counter = storage.get(KEY).get.intValue
storage.put(KEY, counter + 1)
reply(new Integer(counter + 1))
2009-09-07 18:42:15 +02:00
} else {
storage.put(KEY, 0)
2009-09-07 18:42:15 +02:00
hasStartedTicking = true
reply(new Integer(0))
2009-09-07 18:42:15 +02:00
}
}
}