AOP folks love their security checking – they can use an aspects to weave security checking in and around methods of an object. Here’s a sketch of how to do something similar in Scala, but with complete type safety. We group operations into traits, then use implicit coercions to do the transitions between the traits. While doing the coercion we perform our security check.
Note that the work method doesn’t worry about the security issues – it just calls the methods it is interested in. The Scala compiler inserts the calls to the coercion functions automatically.
package secure; /** In this example we are working towards having the following ConcreteContext class compile and run safely. This code is in the public domain. @author Ross Judson */ class ConcreteContext(implicit val session: Session) { import Console._ // bring in println import Security._ // bring in automatic security checks def work { val account: Account = null // We can access the generic account members without any check println("The account's id is " + account.id) // An implicit conversion is performed to a ReadAccount, and during // that implicit conversion the necessary security check is performed, // which might lead to an exception being thrown. println("We need read security to see the balance: " + account.balance) // Here we need write permission; the Scala compiler runs a different implicit conversion // (and a different security check) to get write permission. println("And we need write security to withdraw: " + account.withdraw(100)) } } /** A Session is able to check for a kind of permission on an account. */ trait Session { def checkRead(account: Account): Option[ReadAccount] def checkWrite(account: Account): Option[WriteAccount] } /** We have a basic Accont trait which can only return its account identifier. */ trait Account { val id: String } /** To get the balance on the account we need read security. Read operations are grouped here. */ trait ReadAccount extends Account { val balance: Int } /** To withdraw we need write permission. */ trait WriteAccount extends Account { def withdraw(amount: Int): Int } /** When we fail a security check we throw one of these. */ class SecurityException extends Exception /** Our security object has the security check methods we need. Note the use of an implicit session parameter to automatically pull in the context's session. */ object Security { implicit def account2Read(account: Account)(implicit session: Session): ReadAccount = session.checkRead(account) match { case Some(rd) => rd case None => throw new SecurityException } implicit def account2Write(account: Account)(implicit session: Session): WriteAccount = session.checkWrite(account) match { case Some(rd) => rd case None => throw new SecurityException } }