15th
walruz: Creating Basic Authorization Policies
Last post we checked the architecture of the walruz authorization framework, now we will continue on the implementation of basic policies. Creation of Authorization Policies.
To create a policy you have to create a class that extends from the Walruz::Policy class, once that is done, you’ll need to define the authorized? method. This method will receive 2 parameters: the actor, and the subject.
class ActorIsAdmin < Walruz::Policy
def authorized?(actor, _)
actor.admin?
end
end
class ActorIsSubject < Walruz::Policy
def authorized?(actor, subject)
actor == subject
end
end
In the example above, we define the most common policies, the first one is the “current user is admin” policy, in this policy we ignore completely the subject because it won’t affect the nature of the policy.
The second one is the “actor is subject”, you may not see the utility of this one after we use it (and ooh boy… we are going to use it a lot!).
Using the Authorization Policies
So, suppose we have the “all well known” User class, and we want to check if a User can be read, modified, updated or destroyed by the current authenticated user. The User class would go as follow:
class User < ActiveRecord::Base
include Walruz::Actor
include Walruz::Subject
######################
### Authorizations ###
######################
UserReadPolicy = Walruz::Utils.any(ActorIsSubject, ActorIsAdmin)
check_authorizations :read => UserReadPolicy
end
So the code above states that a User is an Actor (an entity that wants to execute some action in other entity), and at the same time a Subject (an entity that wants to be accessed in some way). It also defines some authorization policies associated to a label (in this case the action name). In this example Walruz::Utils.any let’s us define a composition of policies, this method will join a collection of policies together by a logical or statement.
There are several ways to check if an Actor can perform some action on a Subject, but for now, we are going to use the most basic one: the can? method.
def show(user)
if current_user.can?(:read, user)
render user
else
render :template => 'public/unauthorized'
end
end
In the declaration of the UserReadPolicy we define that a User “A” can be read by another User “B” if either “A” is “B”, or “A” is an admin user. We are checking that conditions are met when we execute the current_user.can?(:read, user) statement.
Let’s extend our example a little bit, and Add a friendship relationship between users:
class User < ActiveRecord::Base
include Walruz::Actor
include Walruz::Subject
####################
### Associations ###
####################
has_many :friendships
has_many :friends, :through => :friendships, :class => 'User', :foreign_key => 'friender_id'
######################
### Authorizations ###
######################
UserReadPolicy = Walruz::Utils.any(ActorIsSubject, ActorIsAdmin)
check_authorizations :read => UserReadPolicy
########################
### Instance Methods ###
########################
def become_friend_of(another_user)
Friendship.create!(:friender => self, :friendee => another_user)
Friendship.create!(:friender => another_user, :friendee => self)
end
end
class Friendship < ActiveRecord::Base
belongs_to :friender
belongs_to :friendee
end
Let’s say we want to enable friends to see each other records, right now it’s not possible with the policies stated, but this can be easily achievable with a new walruz policy.
class UserIsFriend < Walruz::Policy
def authorized?(current_user, another_user)
another_user.friends.include?(current_user)
end
end
Once we create the new policy, we add it to the UserReadPolicy composition.
class User < ActiveRecord::Base
...
UserReadPolicy = Walruz::Utils.any(ActorIsSubject, UserIsFriend, ActorIsAdmin)
...
end
As you may already noticed, the name of this new policy is different from the previous ones (this new one starts with “User” instead of “Actor”), this is because, in this policy the class of the Subject is important for the use of it. As a rule of thumb, if the subject is an important deal on the policy class, you should always start the name of the policy with the class of the Subject; that way it will become more easier to merge different policies together; but more on that later.
That’s all for this episode now, next post we are going to do some more fancy stuff with the policies of this episode and going to extend the example a little bit more, until then.
