changed trapExit from Boolean to "trapExit = List(classOf[..], classOf[..])" + cleaned up security code

This commit is contained in:
jboner 2009-11-17 22:26:25 +01:00
parent 495adb7898
commit d7ac449d26
7 changed files with 465 additions and 371 deletions

132
.idea/workspace.xml generated
View file

@ -2,10 +2,13 @@
<project version="4">
<component name="ChangeListManager" verified="true">
<list default="true" readonly="true" id="e5228cd0-f5f0-4bab-b96d-de1a99e71911" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/pom.xml" afterPath="$PROJECT_DIR$/pom.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka.iml" afterPath="$PROJECT_DIR$/akka.iml" />
<change type="DELETED" beforePath="$PROJECT_DIR$/akka.ipr" afterPath="" />
<change type="DELETED" beforePath="$PROJECT_DIR$/akka.iws" afterPath="" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Actor.scala" afterPath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Actor.scala" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-security/src/main/scala/Security.scala" afterPath="$PROJECT_DIR$/akka-security/src/main/scala/Security.scala" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Supervisor.scala" afterPath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Supervisor.scala" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-security/src/test/scala/SecuritySpec.scala" afterPath="$PROJECT_DIR$/akka-security/src/test/scala/SecuritySpec.scala" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-amqp/src/main/scala/AMQP.scala" afterPath="$PROJECT_DIR$/akka-amqp/src/main/scala/AMQP.scala" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Scheduler.scala" afterPath="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Scheduler.scala" />
</list>
<ignored path=".idea/workspace.xml" />
<ignored path="akka.iws" />
@ -65,10 +68,64 @@
<component name="FileColors" enabled="true" enabledForTabs="true" />
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="Actor.scala" pinned="false" current="true" current-in-tab="true">
<file leaf-file-name="Actor.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Actor.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="162" column="71" selection-start="6288" selection-end="6288" vertical-scroll-proportion="1.7435898">
<state line="75" column="6" selection-start="2861" selection-end="2861" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="Scheduler.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Scheduler.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="45" column="0" selection-start="1590" selection-end="1628" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="Config.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/config/Config.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="22" column="13" selection-start="649" selection-end="649" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="Supervisor.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Supervisor.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="106" column="60" selection-start="3768" selection-end="3768" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="AMQP.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-amqp/src/main/scala/AMQP.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="291" column="0" selection-start="10068" selection-end="10068" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="Security.scala" pinned="false" current="true" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/akka-security/src/main/scala/Security.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="38" column="3" selection-start="1677" selection-end="1677" vertical-scroll-proportion="0.02631579">
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="SecuritySpec.scala" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/akka-security/src/test/scala/SecuritySpec.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="65" column="45" selection-start="2449" selection-end="2449" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
@ -90,6 +147,18 @@
<setting name="OPEN_NEW_TAB" value="false" />
</FindUsagesManager>
</component>
<component name="IdeDocumentHistory">
<option name="changedFiles">
<list>
<option value="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Actor.scala" />
<option value="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Scheduler.scala" />
<option value="$PROJECT_DIR$/akka-actors/src/main/scala/actor/Supervisor.scala" />
<option value="$PROJECT_DIR$/akka-amqp/src/main/scala/AMQP.scala" />
<option value="$PROJECT_DIR$/akka-security/src/test/scala/SecuritySpec.scala" />
<option value="$PROJECT_DIR$/akka-security/src/main/scala/Security.scala" />
</list>
</option>
</component>
<component name="MavenImportPreferences">
<option name="importingSettings">
<MavenImportingSettings>
@ -173,6 +242,13 @@
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit">
<method>
<option name="AntTarget" enabled="false" />
<option name="BuildArtifacts" enabled="false" />
<option name="Maven.BeforeRunTask" enabled="false" />
</method>
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<module name="" />
<option name="MAIN_CLASS_NAME" />
@ -329,9 +405,51 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Scheduler.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="45" column="0" selection-start="1590" selection-end="1628" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/config/Config.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="22" column="13" selection-start="649" selection-end="649" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Supervisor.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="106" column="60" selection-start="3768" selection-end="3768" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-amqp/src/main/scala/AMQP.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="291" column="0" selection-start="10068" selection-end="10068" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-actors/src/main/scala/actor/Actor.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="162" column="71" selection-start="6288" selection-end="6288" vertical-scroll-proportion="1.7435898">
<state line="75" column="6" selection-start="2861" selection-end="2861" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-security/src/test/scala/SecuritySpec.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="65" column="45" selection-start="2449" selection-end="2449" vertical-scroll-proportion="0.0">
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/akka-security/src/main/scala/Security.scala">
<provider selected="true" editor-type-id="text-editor">
<state line="38" column="3" selection-start="1677" selection-end="1677" vertical-scroll-proportion="0.02631579">
<folding />
</state>
</provider>

View file

@ -124,10 +124,10 @@ trait Actor extends Logging with TransactionManagement {
* so it fits the specific use-case that the actor is used for.
* <p/>
* It is beneficial to have actors share the same dispatcher, easily +100 actors can share the same.
* <br/>
* <p/>
* But if you are running many many actors then it can be a good idea to have split them up in terms of
* dispatcher sharing.
* <br/>
* <p/>
* Default is that all actors that are created and spawned from within this actor is sharing the same
* dispatcher as its creator.
* <pre>
@ -151,9 +151,19 @@ trait Actor extends Logging with TransactionManagement {
/**
* User overridable callback/setting.
*
* Set trapExit to true if actor should be able to trap linked actors exit messages.
* Set trapExit to the list of exception classes that the actor should be able to trap
* from the actor it is supervising. When the supervising actor throws these exceptions
* then they will trigger a restart.
* <p/>
* <pre>
* // trap all exceptions
* trapExit = List(classOf[Throwable])
*
* // trap specific exceptions only
* trapExit = List(classOf[MyApplicationException], classOf[MyApplicationError])
* </pre>
*/
protected[this] var trapExit: Boolean = false
protected[this] var trapExit: List[Class[_ <: Throwable]] = Nil
/**
* User overridable callback/setting.
@ -392,8 +402,9 @@ trait Actor extends Logging with TransactionManagement {
* Links an other actor to this actor. Links are unidirectional and means that a the linking actor will
* receive a notification if the linked actor has crashed.
* <p/>
* If the 'trapExit' member field has been set to 'true' then it will 'trap' the failure and automatically
* restart the linked actors according to the restart strategy defined by the 'faultHandler'.
* If the 'trapExit' member field has been set to at contain at least one exception class then it will
* 'trap' these exceptions and automatically restart the linked actors according to the restart strategy
* defined by the 'faultHandler'.
* <p/>
* To be invoked from within the actor itself.
*/
@ -628,14 +639,14 @@ trait Actor extends Logging with TransactionManagement {
}
private[this] def handleTrapExit(dead: Actor, reason: Throwable): Unit = {
if (trapExit) {
if (trapExit.exists(_.isAssignableFrom(reason.getClass))) {
if (faultHandler.isDefined) {
faultHandler.get match {
// FIXME: implement support for maxNrOfRetries and withinTimeRange in RestartStrategy
case AllForOneStrategy(maxNrOfRetries, withinTimeRange) => restartLinkedActors(reason)
case OneForOneStrategy(maxNrOfRetries, withinTimeRange) => dead.restart(reason)
}
} else throw new IllegalStateException("No 'faultHandler' defined for actor with the 'trapExit' flag set to true - can't proceed " + toString)
} else throw new IllegalStateException("No 'faultHandler' defined for actor with the 'trapExit' member field set to non-empty list of exception classes - can't proceed " + toString)
} else {
if (_supervisor.isDefined) _supervisor.get ! Exit(dead, reason) // if 'trapExit' is not defined then pass the Exit on
}

View file

@ -42,7 +42,7 @@ object Scheduler extends Actor {
private var service = Executors.newSingleThreadScheduledExecutor(SchedulerThreadFactory)
private val schedulers = new ConcurrentHashMap[Actor, Actor]
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = true
trapExit = List(classOf[Throwable])
start
def schedule(receiver: Actor, message: AnyRef, initialDelay: Long, delay: Long, timeUnit: TimeUnit) = {

View file

@ -104,7 +104,7 @@ abstract class SupervisorFactory extends Logging {
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
class Supervisor private[akka] (handler: FaultHandlingStrategy) extends Actor with Logging with Configurator {
trapExit = true
trapExit = List(classOf[Throwable])
faultHandler = Some(handler)
//dispatcher = Dispatchers.newThreadBasedDispatcher(this)

View file

@ -46,7 +46,7 @@ import java.io.IOException
object AMQP extends Actor {
private val connections = new ConcurrentHashMap[FaultTolerantConnectionActor, FaultTolerantConnectionActor]
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = true
trapExit = List(classOf[Throwable])
start
sealed trait AMQPMessage
@ -288,7 +288,7 @@ object AMQP extends Actor {
extends FaultTolerantConnectionActor { self: Consumer =>
faultHandler = Some(OneForOneStrategy(5, 5000))
trapExit = true
trapExit = List(classOf[Throwable])
private val listeners = new HashMap[MessageConsumerListener, MessageConsumerListener]

View file

@ -23,8 +23,8 @@
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_.se.scalablesolutions.akka.util.Logging
import _root_.se.scalablesolutions.akka.Config
import _root_.com.sun.jersey.api.model.AbstractMethod
import _root_.com.sun.jersey.spi.container.{ResourceFilterFactory, ContainerRequest, ContainerRequestFilter, ContainerResponse, ContainerResponseFilter, ResourceFilter}
@ -51,8 +51,6 @@ case class Authenticate(val req : ContainerRequest, val rolesAllowed : List[Stri
*/
case class UserInfo(val username: String, val password: String, val roles: List[String])
trait Credentials
case class BasicCredentials(username: String, password: String) extends Credentials
@ -74,10 +72,11 @@ case class SpnegoCredentials(token : Array[Byte]) 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 {
class Filter(actor: Actor, rolesAllowed: Option[List[String]])
extends ResourceFilter with ContainerRequestFilter with Logging {
override def getRequestFilter: ContainerRequestFilter = this
override def getResponseFilter: ContainerResponseFilter = null
/**
@ -87,10 +86,7 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging {
override def filter(request: ContainerRequest): ContainerRequest =
rolesAllowed match {
case Some(roles) => {
val result : AnyRef = (authenticator !? Authenticate(request,roles))
result match {
(authenticator !? Authenticate(request, roles)).asInstanceOf[AnyRef] match {
case OK => request
case r if r.isInstanceOf[Response] =>
throw new WebApplicationException(r.asInstanceOf[Response])
@ -104,7 +100,7 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging {
}
}
lazy val authenticatorFQN = akka.Config.config.getString("akka.rest.authenticator").getOrElse(throw new IllegalStateException("akka.rest.authenticator"))
lazy val authenticatorFQN = 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
@ -112,7 +108,8 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging {
*/
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))
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
@ -147,8 +144,7 @@ class AkkaSecurityFilterFactory extends ResourceFilterFactory with Logging {
* 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
{
trait AuthenticationActor[C <: Credentials] extends Actor {
type Req = ContainerRequest
//What realm does the authentication use?
@ -190,20 +186,16 @@ trait AuthenticationActor[C <: Credentials] extends Actor with Logging
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)
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
def receive = authenticate
//returns the string value of the "Authorization"-header of the request
def auth(r: Req) = r.getHeaderValue("Authorization")
@ -220,13 +212,11 @@ trait AuthenticationActor[C <: Credentials] extends Actor with Logging
* 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]
{
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 Authorization = """(.*):(.*)""".r
authOption(r) match {
@ -241,18 +231,16 @@ trait BasicAuthenticationActor extends AuthenticationActor[BasicCredentials]
}
}
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
* 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]
{
trait DigestAuthenticationActor extends AuthenticationActor[DigestCredentials] {
import Enc._
private object InvalidateNonces
@ -263,30 +251,25 @@ trait DigestAuthenticationActor extends AuthenticationActor[DigestCredentials]
//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)
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 receive = authenticate orElse invalidateNonces
override def unauthorized : Response =
{
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 =
{
def unauthorized(nonce: String, qop: String, opaque: String): Response = {
Response.status(401).header("WWW-Authenticate",
"Digest realm=\"" + realm + "\", " +
"qop=\"" + qop + "\", " +
@ -319,12 +302,11 @@ trait DigestAuthenticationActor extends AuthenticationActor[DigestCredentials]
case _ => None
}
override def extractCredentials(r : Req) : Option[DigestCredentials] =
{
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"),
DigestCredentials(r.getMethod.toUpperCase,
?("username"), ?("realm"), ?("nonce"),
?("uri"), ?("qop"), ?("nc"),
?("cnonce"), ?("response"), ?("opaque"))
})
@ -356,8 +338,8 @@ import _root_.javax.security.auth.kerberos.KerberosPrincipal
import _root_.org.ietf.jgss.GSSContext
import _root_.org.ietf.jgss.GSSCredential
import _root_.org.ietf.jgss.GSSManager
trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
{
trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials] {
override def unauthorized =
Response.status(401).header("WWW-Authenticate", "Negotiate").build
@ -365,13 +347,11 @@ trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
// but the commons Base64 does
import _root_.org.apache.commons.codec.binary.Base64
override def extractCredentials(r: Req): Option[SpnegoCredentials] = {
val AuthHeader = """Negotiate\s(.*)""".r
authOption(r) match {
case Some(AuthHeader(token)) => {
case Some(AuthHeader(token)) =>
Some(SpnegoCredentials(Base64.decodeBase64(token.trim.getBytes)))
}
case _ => None
}
}
@ -379,12 +359,9 @@ trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
override def verify(odc: Option[SpnegoCredentials]): Option[UserInfo] = odc match {
case Some(dc) => {
try {
val principal = Subject.doAs(this.serviceSubject, new KerberosValidateAction(dc.token));
val user = stripRealmFrom(principal)
Some(UserInfo(user, null, rolesFor(user)))
} catch {
case e: PrivilegedActionException => {
@ -415,37 +392,31 @@ trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
/**
* principal name for the HTTP kerberos service, i.e HTTP/ { server } @ { realm }
*/
lazy val servicePrincipal = akka.Config.config.getString("akka.rest.kerberos.servicePrincipal").getOrElse(throw new IllegalStateException("akka.rest.kerberos.servicePrincipal"))
lazy val servicePrincipal = Config.config.getString("akka.rest.kerberos.servicePrincipal").getOrElse(throw new IllegalStateException("akka.rest.kerberos.servicePrincipal"))
/**
* keytab location with credentials for the service principal
*/
lazy val keyTabLocation = akka.Config.config.getString("akka.rest.kerberos.keyTabLocation").getOrElse(throw new IllegalStateException("akka.rest.kerberos.keyTabLocation"))
lazy val keyTabLocation = Config.config.getString("akka.rest.kerberos.keyTabLocation").getOrElse(throw new IllegalStateException("akka.rest.kerberos.keyTabLocation"))
lazy val kerberosDebug = akka.Config.config.getString("akka.rest.kerberos.kerberosDebug").getOrElse("false")
lazy val kerberosDebug = Config.config.getString("akka.rest.kerberos.kerberosDebug").getOrElse("false")
/**
* is not used by this authenticator, so accept an empty value
*/
lazy val realm = akka.Config.config.getString("akka.rest.kerberos.realm").getOrElse("")
lazy val realm = Config.config.getString("akka.rest.kerberos.realm").getOrElse("")
/**
* verify the kerberos token from a client with the server
*/
class KerberosValidateAction(kerberosTicket: Array[Byte]) extends PrivilegedExceptionAction[String] {
def run = {
val context = GSSManager.getInstance().createContext(null.asInstanceOf[GSSCredential])
context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length)
val user = context.getSrcName().toString()
context.dispose()
user
}
}
// service principal login to kerberos on startup
@ -460,16 +431,11 @@ trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
new java.net.URL(this.keyTabLocation).toExternalForm(),
this.servicePrincipal,
this.kerberosDebug)
val princ = new java.util.HashSet[Principal](1)
princ.add(new KerberosPrincipal(this.servicePrincipal))
val sub = new Subject(false, princ, new java.util.HashSet[Object], new java.util.HashSet[Object])
val lc = new LoginContext("", sub, null, loginConfig)
lc.login()
lc.getSubject()
}
@ -477,16 +443,15 @@ trait SpnegoAuthenticationActor extends AuthenticationActor[SpnegoCredentials]
* this class simulates a login-config.xml
*/
class LoginConfig(keyTabLocation: String, servicePrincipal: String, debug: String) extends Configuration {
override def getAppConfigurationEntry(name: String): Array[AppConfigurationEntry] = {
val options = new java.util.HashMap[String, String]
options.put("useKeyTab", "true");
options.put("keyTab", this.keyTabLocation);
options.put("principal", this.servicePrincipal);
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("isInitiator", "true");
options.put("debug", debug);
options.put("useKeyTab", "true")
options.put("keyTab", this.keyTabLocation)
options.put("principal", this.servicePrincipal)
options.put("storeKey", "true")
options.put("doNotPrompt", "true")
options.put("isInitiator", "true")
options.put("debug", debug)
Array(new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",

View file

@ -13,12 +13,13 @@ import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
import org.mockito.Matchers._
import org.junit.{Before, After, Test}
import _root_.javax.ws.rs.core.{SecurityContext, Context, Response}
import _root_.com.sun.jersey.spi.container.{ResourceFilterFactory, ContainerRequest, ContainerRequestFilter, ContainerResponse, ContainerResponseFilter, ResourceFilter}
import _root_.com.sun.jersey.core.util.Base64
class BasicAuthenticatorSpec extends junit.framework.TestCase with Suite with MockitoSugar with MustMatchers {
class BasicAuthenticatorSpec extends junit.framework.TestCase
with Suite with MockitoSugar with MustMatchers {
val authenticator = new BasicAuthenticator
authenticator.start
@ -36,6 +37,7 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase with Suite with Mo
val req = mock[ContainerRequest]
// fake a basic auth header -> this will authenticate the user
when(req.getHeaderValue("Authorization")).thenReturn("Basic " + new String(Base64.encode("foo:bar")))
// fake a request authorization -> this will authorize the user
when(req.isUserInRole("chef")).thenReturn(true)
@ -48,6 +50,7 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase with Suite with Mo
@Test def testUnauthorized = {
val req = mock[ContainerRequest]
// fake a basic auth header -> this will authenticate the user
when(req.getHeaderValue("Authorization")).thenReturn("Basic " + new String(Base64.encode("foo:bar")))
when(req.isUserInRole("chef")).thenReturn(false) // this will deny access
@ -55,20 +58,17 @@ class BasicAuthenticatorSpec extends junit.framework.TestCase with Suite with Mo
val result: Response = (authenticator !? Authenticate(req, List("chef")))
result.getStatus must equal(Response.Status.FORBIDDEN.getStatusCode)
// the authenticator must have set a security context
verify(req).setSecurityContext(any[SecurityContext])
}
class BasicAuthenticator extends BasicAuthenticationActor {
def verify(odc: Option[BasicCredentials]): Option[UserInfo] = odc match {
case Some(dc) => Some(UserInfo("foo", "bar", "ninja" :: "chef" :: Nil))
case _ => None
}
override def realm = "test"
}
}