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.
sourceobject 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)
}
}
The source code for this page can be found here.