Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 173 lines (124 sloc) 4.647 kb
1023936 @jnunemaker Update readme formatting to markdown
authored
1 # Canable
c2a6637 @jnunemaker Initial commit to canable.
authored
2
bb3e972 @jnunemaker Update tagline
authored
3 Simple Ruby authorization system.
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
4
98aed24 @jnunemaker Added install.
authored
5 ## Install
6
7 ```
8 gem install canable
9 ```
10
1023936 @jnunemaker Update readme formatting to markdown
authored
11 ## Cans
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
12
13 Whatever class you want all permissions to run through should include Canable::Cans.
14
1023936 @jnunemaker Update readme formatting to markdown
authored
15 ```ruby
16 class User
17 include MongoMapper::Document
18 include Canable::Cans
19 end
20 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
21
af5199b @jnunemaker More readme formatting
authored
22 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)`.
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
23
1023936 @jnunemaker Update readme formatting to markdown
authored
24 ## Ables
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
25
26 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.
27
1023936 @jnunemaker Update readme formatting to markdown
authored
28 ```ruby
29 class Article
30 include MongoMapper::Document
31 include Canable::Ables
32 end
33 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
34
af5199b @jnunemaker More readme formatting
authored
35 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)`.
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
36
af5199b @jnunemaker More readme formatting
authored
37 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.
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
38
1023936 @jnunemaker Update readme formatting to markdown
authored
39 ```ruby
40 class Article
41 include MongoMapper::Document
42 include Canable::Ables
43 userstamps! # adds creator and updater
44
45 def updatable_by?(user)
46 creator == user
47 end
48
49 def destroyable_by?(user)
50 updatable_by?(user)
51 end
52 end
53 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
54
55 Lets look at some sample code now:
56
1023936 @jnunemaker Update readme formatting to markdown
authored
57 ```ruby
58 john = User.create(:name => 'John')
59 steve = User.create(:name => 'Steve')
60
61 ruby = Article.new(:title => 'Ruby')
62 john.can_create?(ruby) # true
63 steve.can_create?(ruby) # true
64
65 ruby.creator = john
66 ruby.save
67
68 john.can_view?(ruby) # true
69 steve.can_view?(ruby) # true
70
71 john.can_update?(ruby) # true
72 steve.can_update?(ruby) # false
73
74 john.can_destroy?(ruby) # true
75 steve.can_destroy?(ruby) # false
76 ```
77
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
78 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. Next, how would you use this in the controller.
79
1023936 @jnunemaker Update readme formatting to markdown
authored
80 ## Enforcers
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
81
1023936 @jnunemaker Update readme formatting to markdown
authored
82 ```ruby
83 class ApplicationController
84 include Canable::Enforcers
85 end
86 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
87
828f884 @jnunemaker Missed a bit of code formatting
authored
88 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:
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
89
1023936 @jnunemaker Update readme formatting to markdown
authored
90 ```ruby
91 class ApplicationController
92 include Canable::Enforcers
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
93
1023936 @jnunemaker Update readme formatting to markdown
authored
94 delegate :can_view?, :to => :current_user
95 helper_method :can_view? # so you can use it in your views
96 hide_action :can_view?
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
97
1023936 @jnunemaker Update readme formatting to markdown
authored
98 private
99 def enforce_view_permission(resource)
100 raise Canable::Transgression unless can_view?(resource)
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
101 end
1023936 @jnunemaker Update readme formatting to markdown
authored
102 end
103 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
104
105 Which means you can use it like this:
106
1023936 @jnunemaker Update readme formatting to markdown
authored
107 ```ruby
108 class ArticlesController < ApplicationController
109 def show
110 @article = Article.find!(params[:id])
111 enforce_view_permission(@article)
112 end
113 end
114 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
115
af5199b @jnunemaker More readme formatting
authored
116 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.).
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
117
1023936 @jnunemaker Update readme formatting to markdown
authored
118 ## Adding Your Own Actions
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
119
120 You can add your own actions like this:
121
1023936 @jnunemaker Update readme formatting to markdown
authored
122 ```ruby
123 Canable.add(:publish, :publishable)
124 ```
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
125
af5199b @jnunemaker More readme formatting
authored
126 The first parameter is the can method (ie: `can_publish?`) and the second is the able method (ie: `publishable_by?`).
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
127
c93ee06 @jopotts Default to Canable::Transgression when the current_user is nil.
jopotts authored
128 Ables can also be added as class methods. For example, to restrict access to an index action:
129
1023936 @jnunemaker Update readme formatting to markdown
authored
130 ```ruby
131 Canable.add(:index, :indexable)
132 ```
133
c93ee06 @jopotts Default to Canable::Transgression when the current_user is nil.
jopotts authored
134 Then enforce by passing the class instead of the instance:
135
1023936 @jnunemaker Update readme formatting to markdown
authored
136 ```ruby
137 class ArticlesController < ApplicationController
138 def index
139 @articles = Article.all
140 enforce_index_permission(Article)
141 end
142 end
143 ```
c93ee06 @jopotts Default to Canable::Transgression when the current_user is nil.
jopotts authored
144
145 Then in the article model, add the able check as a class method:
146
1023936 @jnunemaker Update readme formatting to markdown
authored
147 ```ruby
148 class Article
65abd79 @jnunemaker ellipses to comment + ellipses in readme
authored
149 # ...
1023936 @jnunemaker Update readme formatting to markdown
authored
150 def self.indexable_by?(user)
151 !user.nil?
152 end
153 end
154 ```
c93ee06 @jopotts Default to Canable::Transgression when the current_user is nil.
jopotts authored
155
1023936 @jnunemaker Update readme formatting to markdown
authored
156 ## Review
71dbef5 @jnunemaker Initial commit of readme and canable functionality. Tests are next.
authored
157
158 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.
c2a6637 @jnunemaker Initial commit to canable.
authored
159
1023936 @jnunemaker Update readme formatting to markdown
authored
160 ## Contributing
161
c2a6637 @jnunemaker Initial commit to canable.
authored
162 * Fork the project.
163 * Make your feature addition or bug fix.
164 * Add tests for it. This is important so I don't break it in a
165 future version unintentionally.
166 * Commit, do not mess with rakefile, version, or history.
167 (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)
168 * Send me a pull request. Bonus points for topic branches.
169
1023936 @jnunemaker Update readme formatting to markdown
authored
170 ## Copyright
c2a6637 @jnunemaker Initial commit to canable.
authored
171
172 Copyright (c) 2010 John Nunemaker. See LICENSE for details.
Something went wrong with that request. Please try again.