• Stars
    star
    312
  • Rank 134,133 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 14 years ago
  • Updated over 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple Ruby authorization system.

Canable

Simple Ruby authorization system.

Install

gem install canable

Cans

Whatever class you want all permissions to run through should include Canable::Cans.

class User
  include MongoMapper::Document
  include Canable::Cans
end

This means that an instance of a user automatically gets can methods for the default REST actions: can_view?(resource), can_create?(resource), can_update?(resource), can_destroy?(resource).

Ables

Each of the can methods simply calls the related "able" method (viewable, creatable, updatable, destroyable) for the action (view, create, update, delete). Canable comes with defaults for this methods that you can then override as makes sense for your permissions.

class Article
  include MongoMapper::Document
  include Canable::Ables
end

Including Canable::Ables adds the able methods to the class including it. In this instance, article now has viewable_by?(user), creatable_by?(user), updatable_by?(user) and destroyable_by?(user).

Lets say an article can be viewed and created by anyone, but only updated or destroyed by the user that created the article. To do that, you could leave viewable_by? and creatable_by? alone as they default to true and just override the other methods.

class Article
  include MongoMapper::Document
  include Canable::Ables
  userstamps! # adds creator and updater

  def updatable_by?(user)
    creator == user
  end

  def destroyable_by?(user)
    updatable_by?(user)
  end
end

Let's look at some sample code now:

john = User.create(:name => 'John')
steve = User.create(:name => 'Steve')

ruby = Article.new(:title => 'Ruby')
john.can_create?(ruby) # true
steve.can_create?(ruby) # true

ruby.creator = john
ruby.save

john.can_view?(ruby) # true
steve.can_view?(ruby) # true

john.can_update?(ruby) # true
steve.can_update?(ruby) # false

john.can_destroy?(ruby) # true
steve.can_destroy?(ruby) # false

Now we can implement our permissions for each resource and then always check whether a user can or cannot do something. This makes it all really easy to test. In one common pattern, a single permission flag controls whether or not users can perform multiple administrator-specific operations. Canable can honor that flag with:

def writable_by?(user)
  user.can_do_anything?
end
alias_method :creatable_by?, :writable_by?
alias_method :updatable_by?, :writable_by?
alias_method :destroyable_by?, :writable_by?

Next, how would you use this in the controller.

Enforcers

class ApplicationController
  include Canable::Enforcers
end

Including Canable::Enforcers adds an enforce permission method for each of the actions defined (by default view/create/update/destroy). It is the same thing as doing this for each Canable action:

class ApplicationController
  include Canable::Enforcers

  delegate :can_view?, :to => :current_user
  helper_method :can_view? # so you can use it in your views
  hide_action :can_view?

  private
    def enforce_view_permission(resource)
      raise Canable::Transgression unless can_view?(resource)
    end
end

Which means you can use it like this:

class ArticlesController < ApplicationController
  def show
    @article = Article.find!(params[:id])
    enforce_view_permission(@article)
  end
end

If the user can_view? the article, all is well. If not, a Canable::Transgression is raised which you can decide how to handle (show 404, slap them on the wrist, etc.). For example:

class ApplicationController < ActionController::Base
  rescue_from Canable::Transgression, :with => :render_403

  protected
  def render_403(e)
    # notify normal exception handler(s) here
    render :status => 403
  end

Adding Your Own Actions

You can add your own actions like this:

Canable.add(:publish, :publishable)

The first parameter is the can method (ie: can_publish?) and the second is the able method (ie: publishable_by?).

Ables can also be added as class methods. For example, to restrict access to an index action:

Canable.add(:index, :indexable)

Then enforce by passing the class instead of the instance:

class ArticlesController < ApplicationController
  def index
    @articles = Article.all
    enforce_index_permission(Article)
  end
end

Then in the article model, add the able check as a class method:

class Article
  # ...
  def self.indexable_by?(user)
    !user.nil?
  end
end

Review

So, lets review: cans go on user model, ables go on everything, you override ables in each model where you want to enforce permissions, and enforcers go after each time you find or initialize an object in a controller. Bing, bang, boom.

Contributing

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright (c) 2010 John Nunemaker. See LICENSE for details.

More Repositories

1

httparty

๐ŸŽ‰ Makes http fun again!
Ruby
5,789
star
2

flipper

๐Ÿฌ Beautiful, performant feature flags for Ruby.
Ruby
3,295
star
3

crack

Really simple JSON and XML parsing, ripped from Merb and Rails.
Ruby
539
star
4

nunes

๐Ÿ“ˆ The friendly gem that instruments everything for you, like I would if I could.
Ruby
498
star
5

fancy-zoom

[DEAD] Zoomy JavaScript based loosely on Fancy Zoom by Cabel Sasser.
JavaScript
475
star
6

happymapper

[unmaintained] XML to object mapping library
Ruby
300
star
7

twitter-app

example rails 3 application that uses oauth
Ruby
132
star
8

joint

[DEAD AND UNMAINTAINED] MongoMapper and GridFS joined in file upload love.
Ruby
129
star
9

resilient

๐Ÿ”Œ circuit breaker based on netflix/hystrix but in ruby, hopefully commands/semaphores eventually too
Ruby
103
star
10

user_stamp

A Rails plugin that adds the ability to automatically stamp each record with the currently logged in user.
Ruby
84
star
11

flipper-ui

[MOVED] ... to main flipper repo. See readme.
67
star
12

google-weather

stupid simple fetching of the weather using google's api
Ruby
54
star
13

bin

DEAD AND UNMAINTAINED. ActiveSupport MongoDB Cache store.
Ruby
54
star
14

scrobbler

[DEAD] Scrobbler is a wrapper for the audioscrobbler (last.fm) web services.
Ruby
48
star
15

hunt

really simple search for mongomapper
Ruby
38
star
16

lorem

Ever get tired of opening up lipsum.com, filling out a form and waiting for it to give you your filler text? Yeah, me too. I threw this together in an hour to generate lipsum text from the command line.
Ruby
33
star
17

scam

Really basic fake model for doing types in memory
Ruby
32
star
18

dotfiles

Some of my dotfiles, nothing fancy or worthwhile
Shell
30
star
19

googlereader

[DEAD AND UNMAINTAINED] Wrapper for Google Reader's undocumented and possibly "unstable" API. By unstable, I mean if they haven't documented it, then they could change it at anytime without notice.
Ruby
28
star
20

cassanity

Brings sanity to CQL + Ruby.
Ruby
25
star
21

columbus

Autodiscovers feeds from urls
Ruby
24
star
22

gemwhois

Whois for gems, because gem names are like domains in the 90's
Ruby
23
star
23

googlebase

[DEAD] Google Base Auth Class is a base for authenticating to google and making requests to google services.
Ruby
21
star
24

brow

๐Ÿคจ A generic background thread worker for shipping events to some API backend.
Ruby
20
star
25

wand

Mime-Type gem with fallback to unix file command. You most likely don't need this gem.
Ruby
20
star
26

javascripts

[DEAD] some various js files and examples for me to refer to and use
JavaScript
17
star
27

stars

collects github.com stars from people you follow
Ruby
14
star
28

adapter-mongo

Adapter for mongo
Ruby
13
star
29

adapter-redis

Redis adapter
Ruby
12
star
30

flipper-redis

[MOVED] into flipper, check out readme for more
11
star
31

mirrored

[DEAD] Mirrored is a wrapper for the mirrored del.icio.us and ma.gnolia apis.
Ruby
11
star
32

ag

Nothing serious or finished, just playing with events, feeds, activities, timelines, wooooooooooo
Ruby
10
star
33

adapter-riak

Riak adapter
Ruby
9
star
34

wufoo

simple wrapper for the wufoo submission api
Ruby
9
star
35

wordpress_live_search_plugin

[DEAD] live search plugin for wordpress that just works (with prototype)
PHP
8
star
36

twitter

The twitter gem has moved to https://github.com/sferik/twitter
7
star
37

common_helpers

[old and dead] helpers that i use often in apps, nothing special
Ruby
6
star
38

crdts

Nothing serious or finished, just playing with convergent replicated data types
Ruby
6
star
39

adapter-memcached

Memcached adapter
Ruby
6
star
40

snitch

[DEAD AND UNMAINTAINED] Drop dead easy subversion commit notifications.
Ruby
5
star
41

chinwag

[DEAD] chat app that steve and i are playing with, uses google app engine, html5, css3 and jquery
Python
5
star
42

toystore-mongo

Mongo integration for Toystore
Ruby
5
star
43

tao

Nothing serious or finished, just playing with facebook tao concepts
Ruby
5
star
44

scout-plugins

scout plugins as i make them...
Ruby
4
star
45

flipper-mongo

[MOVED] merged into flipper repo, check out readme for more
3
star
46

disk_queue

Nothing to see here. Move along.
Ruby
3
star
47

adapter-cassanity

Adapter for Cassanity
Ruby
1
star
48

clostar

star slurper
Clojure
1
star
49

jnunemaker.github.com

my new blog
CSS
1
star
50

prey

[UNFINISHED] Kestrel client gem based on thrift interface.
Ruby
1
star
51

flipper-cassanity

Cassanity adapter for Flipper.
Ruby
1
star