Revocation

This is an example of revocation in ocaps.

Revocation involves two steps – first, the original capability is thunked so that access is delayed, and then a proxy encapsulating that thunk is exposed, along with a Revoker which controls the thunked access, through a Revocable (aka caretaker).

Because revocation provides access mediated over time, it is sometimes described as “temporal attenuation.”

You can read Managing Capabilities in the guide for more information.

object Revocation {

  final class Foo(name: String) {
    private def privateDoTheThing(): Unit = {
      println(s"$name.doTheThing()")
    }

    private object capabilities {
      import Foo._
      val doer: Doer = new Doer {
        override def doTheThing(): Unit = Foo.this.privateDoTheThing()
      }
    }
  }

  object Foo {
    trait Doer {
      def doTheThing(): Unit
    }

    object Doer {
      def revocable(doer: Doer): Revocable[Doer] = {
        Revocable(doer) { thunk =>
          new Doer {
            override def doTheThing(): Unit = thunk().doTheThing()
          }
        }
      }
    }

    class Access {
      def doer(foo: Foo): Doer = foo.capabilities.doer
    }
  }

  import java.util.concurrent.TimeUnit._
  import java.util.concurrent._

  private val scheduler = Executors.newScheduledThreadPool(1)

  // Guest has delegated authority to do the thing without having access to foo
  class Guest(doer: Foo.Doer) {
    def start(): Unit = {
      // Keep doing the thing forever, every second.
      val doTheThing: Runnable = new Runnable {
        override def run(): Unit = {
          try {
            doer.doTheThing()
          } catch {
            case NonFatal(e) => println("Cannot do the thing!")
          }
        }
      }
      scheduler.scheduleAtFixedRate(doTheThing, 0, 1L, SECONDS)
    }
  }

  // Revoker doesn't know about capability, only has kill switch
  class ScheduledRevoker(revoker: Revoker) {
    def start(): Unit = {
      // After three seconds, the admin decides to stop you doing the thing.
      val adminRevoke: Runnable = new Runnable {
        override def run(): Unit =  revoker.revoke()
      }
      scheduler.schedule(adminRevoke, 3L, SECONDS)
    }
  }

  def main(args: Array[String]): Unit = {
    import Foo._
    val foo = new Foo("foo")

    val access = new Access
    val doer = access.doer(foo)

    // macro generates code equivalent to the `revocable` code above
    //val Revocable(revocableDoer, revoker) = Foo.Doer.revocable(doer)
    val Revocable(revocableDoer, revoker) = macros.revocable[Doer](doer)

    new Guest(revocableDoer).start()
    new ScheduledRevoker(revoker).start()

    // After five seconds, exit program.
    val shutdown: Runnable = new Runnable {
      override def run(): Unit = scheduler.shutdown()
    }
    scheduler.schedule(shutdown, 5L, SECONDS)
  }

}
Full source at GitHub