Created
November 2, 2015 11:51
-
-
Save halfninja/0ca39e1afb95e10d82ce to your computer and use it in GitHub Desktop.
Prototype compile-time security checking. Checks are done outside of the service, but the compiler will prevent you from calling the methods unless you have acquired authorization from the auth service.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.annotation.implicitNotFound | |
@implicitNotFound("Required authorization not found.") | |
case class Authorization[+A <: Role]() | |
object Authorization { | |
def of[T <: Role]: Authorization[T] = new Authorization[T]() | |
} | |
// Whatever set of roles you like | |
trait Role | |
trait Admin extends Role | |
trait Sysadmin extends Role | |
trait President extends Role | |
trait Saxophonist extends Role | |
// A service that you need permission to use | |
class SecureService { | |
def broadcast(msg: String)(implicit auth: Authorization[Sysadmin]): Unit = | |
println(s"*** ${msg} ***") | |
// An action that requires sysadmin AND president | |
def deleteEverything()(implicit auth: Authorization[President]): Unit = | |
println("Goodbye, everything!") | |
} | |
/** Some security service that checks if this user has the given role */ | |
class AuthService { | |
/** | |
* Returns a type if you have all the given roles. | |
*/ | |
def checkRoles[T <: Role](user: String): Option[Authorization[_ <: T]] = | |
Some(Authorization.of[T]) | |
} | |
// Example use | |
val service = new SecureService() | |
val auth = new AuthService() | |
auth.checkRoles[President with Sysadmin]("robonixon").map { implicit s => | |
service.broadcast("About to delete everything") | |
service.deleteEverything() | |
}.getOrElse { | |
println("Stairway denied") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment