Simple authorization system (like the one in Apache Shiro) works like this:
- user has a set of permissions
- permissions can imply other permissions
- different actions can require different combinations of permissions
So we need to express somehow that a method requires a set of permissions and we need to express that certain permissions imply other permissions.
After trying different language constructs (like type parameters, variance etc) and asking for help at scala-user mailing list, i came to the following solution:
object Permissions { trait UserPermissions trait AnyUserPermissions extends UserPermissions trait RegisteredUserPermissions extends AnyUserPermissions trait AdminPermissions extends RegisteredUserPermissions trait DomainAPermissions extends RegisteredUserPermissions trait DomainBPermissions extends RegisteredUserPermissions implicit def adm2doma(x: AdminPermissions): DomainAPermissions = new DomainAPermissions{} implicit def adm2domb(x: AdminPermissions): DomainBPermissions = new DomainBPermissions{} } object Test { import Permissions._ def sampleRegisteredUserFunction[T <% RegisteredUserPermissions](p: T) = println("registered user") def sampleCombinedPermissionsFunction[T <% DomainAPermissions <% DomainBPermissions](p: T) = println("combined") def main(args: Array[String]) { val anyUserPermissions = new UserPermissions{} val combinedPermissions = new DomainAPermissions with DomainBPermissions val adminPermissions: AdminPermissions = new AdminPermissions{} sampleRegisteredUserFunction(combinedPermissions) sampleCombinedPermissionsFunction(combinedPermissions) sampleRegisteredUserFunction(adminPermissions) sampleCombinedPermissionsFunction(adminPermissions) sampleRegisteredUserFunction(anyUserPermissions) // compile error - as expected } }The idea is quite obvious: permissions are traits that you can inherit and mix when implementing your own permissions. If inheritance is not enough (for example, we want AdminPermissions to imply any other permissions including those added by users of our library) we can use implicit conversions.
To require certain sets of permissions in a method we declare it using view bounded type parameters (T <% DomainAPermissions <% DomainBPermissions means that T should be implicitly convertable to both DomainAPermissions and DomainBPermissions).
This way you can mark some parts of your code to require statically checked permissions. I think it's better than explicit runtime checks or annotations with AOP.
Комментариев нет:
Отправить комментарий