diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorRefResolverSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorRefResolverSpec.scala new file mode 100644 index 0000000000..9d88a88574 --- /dev/null +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorRefResolverSpec.scala @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor.typed + +import akka.actor.ActorPath +import akka.actor.ActorRefProvider +import akka.actor.ActorSystemImpl +import akka.actor.MinimalActorRef +import akka.actor.RootActorPath +import akka.actor.typed.scaladsl.Behaviors +import org.scalatest.Matchers +import org.scalatest.WordSpec +import org.scalatest.concurrent.ScalaFutures + +class ActorRefResolverSpec extends WordSpec with ScalaFutures with Matchers { + "ActorRefResolver" should { + "not allow serialization of ref originating from other system" in { + val system1 = ActorSystem(Behaviors.empty[String], "sys1") + val system2 = ActorSystem(Behaviors.empty[String], "sys2") + try { + val ref1 = system1.systemActorOf(Behaviors.empty, "ref1") + val serialized = ActorRefResolver(system1).toSerializationFormat(ref1) + serialized should startWith("akka://sys1/") + + intercept[IllegalArgumentException] { + // wrong system + ActorRefResolver(system2).toSerializationFormat(ref1) + } + + // we can't detect that for MinimalActorRef + import akka.actor.typed.scaladsl.adapter._ + + val minRef1: akka.actor.ActorRef = new MinimalActorRef { + override def provider: ActorRefProvider = system1.toClassic.asInstanceOf[ActorSystemImpl].provider + override def path: ActorPath = RootActorPath(provider.getDefaultAddress) / "minRef1" + } + + val minRef1Serialized = ActorRefResolver(system2).toSerializationFormat(minRef1) + minRef1Serialized should startWith("akka://sys2/") + + } finally { + system1.terminate() + system2.terminate() + } + } + + } + +} diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/ActorRefResolver.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/ActorRefResolver.scala index 2df0b953db..f90187027f 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/ActorRefResolver.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/ActorRefResolver.scala @@ -4,6 +4,7 @@ package akka.actor.typed +import akka.actor.ActorRefWithCell import akka.actor.ExtendedActorSystem import akka.annotation.DoNotInherit import akka.annotation.InternalApi @@ -35,7 +36,7 @@ abstract class ActorRefResolver extends Extension { def toSerializationFormat[T](ref: ActorRef[T]): String /** - * Deserialize an `ActorRef` in the [[#toSerializationFormat]]. + * Deserialize an `ActorRef` in the [[ActorRefResolver#toSerializationFormat]]. */ def resolveActorRef[T](serializedActorRef: String): ActorRef[T] @@ -49,8 +50,30 @@ abstract class ActorRefResolver extends Extension { private val classicSystem = system.toClassic.asInstanceOf[ExtendedActorSystem] - override def toSerializationFormat[T](ref: ActorRef[T]): String = - ref.path.toSerializationFormatWithAddress(classicSystem.provider.getDefaultAddress) + override def toSerializationFormat[T](ref: ActorRef[T]): String = { + + def toSerializationFormatWithAddress = + ref.path.toSerializationFormatWithAddress(classicSystem.provider.getDefaultAddress) + + ref.toClassic match { + case a: ActorRefWithCell => + val originSystem = a.underlying.system.asInstanceOf[ExtendedActorSystem] + if (originSystem eq classicSystem) + toSerializationFormatWithAddress + else + throw new IllegalArgumentException( + s"ActorRefResolver for ActorSystem [${classicSystem.provider.getDefaultAddress}] shouldn't be used for " + + "serialization of ActorRef that originates from another ActorSystem " + + s"[${originSystem.provider.getDefaultAddress}]. Use the ActorRefResolver for that system instead.") + + case _ => + // no origin system information for RemoteActorRef or MinimalActorRef, so just use the + // one for this extension. That is correct for RemoteActorRef, but MinimalActorRef + // could be wrong. However, since we don't allow usage of "wrong" ActorSystem for + // ordinary ActorRef the users will learn not to make that mistake. + toSerializationFormatWithAddress + } + } override def resolveActorRef[T](serializedActorRef: String): ActorRef[T] = classicSystem.provider.resolveActorRef(serializedActorRef)