github
Advanced Search
  • Home
  • Pricing and Signup
  • Explore GitHub
  • Blog
  • Login

noomii / walruz

  • Admin
  • Watch Unwatch
  • Fork
  • Your Fork
  • Pull Request
  • Download Source
    • 9
    • 3
  • Source
  • Commits
  • Network (3)
  • Issues (0)
  • Downloads (4)
  • Wiki (1)
  • Graphs
  • Branch: master

click here to add a description

click here to add a homepage

  • Branches (1)
    • master ✓
  • Tags (4)
    • v0.0.9
    • v0.0.8
    • v0.0.7
    • v0.0.5
Sending Request…
Enable Donations

Pledgie Donations

Once activated, we'll place the following badge in your repository's detail box:
Pledgie_example
This service is courtesy of Pledgie.

Simple yet powerful authorization framework made in Ruby — Read more

  cancel

http://github.com/noomii/walruz

  cancel
  • Private
  • Read-Only
  • HTTP Read-Only

This URL has Read+Write access

Adding the config to the autoload list on the walruz.rb file 
noomii (author)
Mon Feb 01 20:47:45 -0800 2010
commit  8b48eaf0e84f990f283e3c64e46f9eb60e025f7d
tree    655c1386b220ab0a73f883db6d9b2244c044b8d7
parent  a3e6852204b7449aad12f4e24f47a803f4487f61
walruz /
name age
history
message
file .document Wed Jun 03 18:45:10 -0700 2009 Initial commit to walruz. [Roman Gonzalez]
file .gitignore Fri Aug 21 10:11:34 -0700 2009 Adding the vim files to the .gitignore [roman]
file CHANGELOG Mon Jan 25 19:53:45 -0800 2010 Adding new info to the CHANGELOG [noomii]
file LICENSE Thu Jun 04 16:47:13 -0700 2009 Updating the README and the LICENSE file [Roman Gonzalez]
file README.rdoc Wed Jul 29 15:08:53 -0700 2009 Fixing some content styling in the README and i... [roman]
file Rakefile Mon Jan 25 20:14:54 -0800 2010 Adding a description to the Gem [noomii]
file VERSION.yml Mon Jan 25 19:54:24 -0800 2010 Version bump to 0.0.10 [noomii]
directory lib/ Mon Feb 01 20:47:45 -0800 2010 Adding the config to the autoload list on the w... [noomii]
directory spec/ Wed Aug 26 10:49:09 -0700 2009 Fixing a bug related to the execution of author... [roman]
file walruz.gemspec Mon Jan 25 20:14:54 -0800 2010 Adding a description to the Gem [noomii]
README.rdoc

Walruz: Simple but Powerful Authorization Framework

Basic and Terminology

Walruz facilitates the separation between the authorization process on the business logic and the actions executed after the validation of the authorizations. To understand how it works, we will follow the following terminology:

Subject
Object that is going to be managed (Profile, Posts).
Actor
Entity that wants to perform an action on a subject (User, Admin).
Policy
A set of rules that tells if the actor can perform the desired action on the subject.

Walruz Architecture

Walruz provides modules and classes that help on the implementation of the concepts given previously, this are:

Walruz::Subject
Module that provides the interface to associate policies to an action in the subject.
Walruz::Actor
Module that provides the interface to perform queries to validate if an action can be done between the actor and the subject.
Walruz::Policy
Class that provides the interface to implement authorization logic.

Subjects specify which policies are related to which actions

Subject classes may specify a set of actions that can be performed to them using the check_authorization method

  class User
    include Walruz::Subject

    check_authorization :read => UserReadPolicy,
                        :update => UserUpdatePolicy
  end

If there is just one policy to every possible action performed to the subject, you may specify the :default action, or just specify the Policy class.

Example:

  class User
    include Walruz::Subject

    check_authorization UserPolicy
  end

or

  class User
    include Walruz::Subject

    check_authorization :default => UserPolicy
  end

You can also specify other flags with the default flag.

  class User
    include Walruz::Subject

    check_authorization :read => UserReadPolicy,
                        :update => UserUpdatePolicy,
                        :default => UserPolicy
  end

Actors verify if they are able to perform an action on a subject

Actor classes can use several methods to check if the actor instance can perform the given action on a subject. This are:

can?(action, subject)
Returns boolean that says if the actor can execute or not the action on the subject.
authorize(action, subject)
In case the actor can execute the action on the subject, it returns the parameters hash from the policy, otherwise it will raise a Walruz::NotAuthorized.
satisfies?(policy_label, subject)
It behaves just like the can? method, but instead of giving an action to be executed to the subject, it receives a policy label.

In case the given action is not assigned to any policy, a default Policy will be executed (if given), if no default policy is given then a Walruz::ActionNotFound exception will be raised.

Examples:

  current_user.can?(:read, friends_profile)      #=> true
  current_user.satisfies?(:actor_is_admin, nil)  #=> false
  current_user.satisfies(:actor_is_admin, nil)  #=> nil
  current_user.authorize(:read, friends_profile) #=> Hash
  current_user.authorize!(:read, other_person_profile) # => raises Walruz::NotAuthorized

Implementing Policies

To implement a policy, it is necessary to inherit from the Walruz::Policy class. This class provides a method called authorized? that return either a Boolean, or an Array of two items, the first one being a Boolean, and the second being a Hash of parameters returned from the Policy.

Every Policy Class also has a label associated to it, by default the label will be the name of the class in underscore case; if you want to have a custom label for a Policy Class, you can invoke the set_policy_label method on the class context and specify the label that you want for it. This label is used on the satisfies? method.

Examples:

  class ActorIsAdmin < Walruz::Policy
    set_policy_label :is_admin

    def authorized?(actor, _)
      actor.is_admin?
    end

  end

  class UserIsFriend < Walruz::Policy

    def authorized?(current_user, friend)
      friendship = Friendship.first(:conditions => { :friend_id => current_user.id, :owner_id => friend.id})
      if !friendship.nil?
        [true, {
          :friendship => friendship
        }]
      else
        false
      end
    end

  end

  # Examples using this policies with the satisfies method

  current_user.satisfies?(:is_admin, nil)

  # By default, the policy label is the name of the class in underscore case.
  current_user.satisfies?(:user_is_friend, other_user)

Composing basic policies to create complex ones

Sometimes policies can turn really messy, specially when you have a complex business model. The good news is that normally this complex policies are a composition of more simple policies (e.g. ActorCanSeeUserPictures). Instead of creating this new classes that replicates the same logic of basic policies, we could merge them together in the following way:

  ActorCanSeeUserPictures = Walruz::Utils.all(UserIsFriend, UserAllowsDisclosureOfPictures)

There is also the utility methods any and not, to create combinations of policies.

If your policy returns a parameters hash, and you are using the all method, the parameters of each policy will be merged together, if you are using the any method, the parameters of the first policy that returns true will be returned.

One other thing that the utility methods does for you is that it leaves its track on the returned policy parameters, when you invoke a composite policy, every policy will leave in the parameters hash the policy_label with a question mark at the end, that way you can know which policies were successful or not.

Example:

  class ActorIsAdmin < Walruz::Policy
    set_policy_label :is_admin

    def authorized?(actor, _)
      actor.is_admin?
    end

  end

  class ActorIsSubject < Walruz::Policy
    def authorized?(actor, subject); actor == subject; end
  end

  UserReadPolicy = any(ActorIsSubject, ActorIsAdmin)

  class User < AbstractORM
    include Walruz::Subject

    check_authorizations :read => UserReadPolicy
  end

  class UsersController < Framework::Controller
    def show
      policy_params = current_user.authorize(:read, other_user)
      if policy_params[:actor_is_subject?]
        # do logic of the user interacting with herself
      elsif policy_params[:is_admin?]
        # do logic of the admin user interacting with other user
      else
        # do other logic here...
      end
    end
  end

Dependencies between Policies

Sometimes you would like to have a Policy that strictly depends in other policies, on the previous example UserAllowsDisclosureOfPictures could have a dependency that says that only the User allows the disclosure of pictures if and only if there is a friend relationship, so we could re-implement this policy as:

Example:

  class UserAllowsDisclosureOfPictures < Walruz::Policy
    depends_on UserIsFriend
    # ...
  end

Suppose you need the parameters returned by the previous Policy, you can have them with the params method.

Example:

  class UserAllowsDisclosureOfPictures < Walruz::Policy
    depends_on UserIsFriend

    def authorized?(_, _)
      params[:friendship].allows_disclosure_of_images?
    end

  end

Policy combinators

Sometimes you would like to execute policies that are not directly related to a subject, but to the association of a subject. Given the example above of the friendship relationship and the disclosure of pictures, sometimes you would like to check if a user can see a picture directly on the picture model.

Suppose we have the following model in our system:

  class Picture < AbstractORM
    belongs_to :owner
  end

and we would like to check if the current_user can see (read) the picture using:

  current_user.can?(:read, picture_instance)

If you may recall, we already implemented the logic that checks that authorization in UserAllowsDisclosureOfPictures, but that policy only works when the subject is of class User; given that you have a subject of class Picture you can not re-use this policy.

You could solve this issue doing the following:

  class PictureReadPolicy < Walruz::Policy

    def authorized?(user, image)
      user.satisfies?(UserAllowsDisclosureOfPictures, image.owner)
    end

  end

But as you may see, we are just creating new policies to handle old ones, we are not combining the policies effectively. To avoid this caveat, you can use the PolicyClass.for_subject method:

  PictureReadPolicy = UserAllowsDisclosureOfPictures.for_subject(:owner)

  class Picture < AbstractORM
    include Walruz::Subject
    belongs_to :owner

    check_authorizations :read => PictureReadPolicy
  end

The parameter of but_for is the name of the subject’s method that will return a new subject, this new subject is then passed through the policy. Pretty neat eh?

Returning custom errors

Suppose you want to add an error to the authorization failure that is a more descriptive, you can do so on the authorized? method passing a hash with a :error_message key on the false return. If you use the can! method on the actor model, this will become the Walruz::NotAuthorized error message.

Example:

  class SomePolicy < Walruz::Policy

    def authorized?(actor, subject)
      # some complex logic here
      return [false, {
        :error_message => 'More descriptive error message'
      }]
    end
  end

Conventions

You’ll notice that once you start implementing policies for your system, you’ll be lost soon enough asking yourself which type of subject a Policy receives; to avoid such confusions, we suggest that you apply the following rules of thumb:

  • The first name of the policy should be the Subject class (e.g. UserIsFriend)
  • If the policy only applies to the actor, the policy class name should start with the Actor word (e.g. ActorIsAdmin)
  • You should always have the compositions of policies in just one place in your library folder (e.g. in policies.rb file).
  • The result of policy compositions should finish with the word Policy (e.g UserDeletePolicy = any(ActorIsSubject, ActorIsAdmin))
  • Use PolicyClass.but_for when you are combining the policy class with other policies, if you are not doing this, consider checking authorizations on parents of the subject instead of the subject (e.g. current_user.can?(:see_pictures_of, picture.owner))

If you follow this rules, it will be much easier for you to merge policies together in an efficient way.

Rails Integration

See walruz-rails gem.

More examples

You may check the project in the examples/ directory for more info; on the rails project, take a look on the spec/models/beatle_spec.rb file, it’s really illustrating.

Copyright

Copyright © 2009 Roman Gonzalez <romanandreg@gmail.com>.

Copyright © 2009 Noomii inc. <www.noomii.com>.

All rights reserved.

Blog | Support | Training | Contact | API | Status | Twitter | Help | Security
© 2010 GitHub Inc. All rights reserved. | Terms of Service | Privacy Policy
Powered by the Dedicated Servers and
Cloud Computing of Rackspace Hosting®
Dedicated Server