allowy 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +59 -18
- data/lib/allowy/version.rb +1 -1
- metadata +12 -12
data/README.md
CHANGED
@@ -3,21 +3,23 @@
|
|
3
3
|
Allowy is the authorization library that doesn't enforce tight DSL on you.
|
4
4
|
It is very simple yet powerful.
|
5
5
|
|
6
|
-
If you have any questions please contact me [@dnagir](http://www.ApproachE.com).
|
7
|
-
|
8
6
|
## Why another one?
|
9
7
|
|
10
8
|
I've been using really great [cancan](https://github.com/ryanb/cancan) gem by Ryan Bates for a long time.
|
11
9
|
It does its job amazingly well.
|
12
10
|
|
11
|
+
Allowy is basically the result of refactoring the CanCan Ability class. I then extracted it into a gem.
|
12
|
+
|
13
13
|
CanCan doesn't work very well for me when Ability definitions grow above 20 lines or so:
|
14
14
|
|
15
|
-
- it becomes **really
|
16
|
-
- DSL enforces you to use ActiveRecord-like scopes or
|
17
|
-
- The Ability class contains all the definitions for everything
|
15
|
+
- it becomes **really hard to track down** why something was (or not) allowed.
|
16
|
+
- **DSL enforces** you to use ActiveRecord-like scopes or blocks. It gets harder to maintain.
|
17
|
+
- The Ability class contains **all the definitions for everything**. Hard to test, hard to maintain unless carefully refactor it.
|
18
18
|
- Implicit permission - CanCan tries to be very smart (and is indeed) using aliases such as `:manage` but it makes even harder to maintain.
|
19
19
|
- Implicit permission - you can use any symbol to check permissions. `:love_people` will do, even if you never defined it.
|
20
|
-
- A little bit tight to ORM
|
20
|
+
- A little bit **tight to ORM**. When using with database such as neo4j, some smalish things don't work. So I prefer to be explicit.
|
21
|
+
- **Testing** an ability for a single class often depends on too many others.
|
22
|
+
- **Refacoring** of the abilities feels like rolling your own authorization library.
|
21
23
|
|
22
24
|
So I decided to put up allowy to solve those issue for me.
|
23
25
|
|
@@ -65,8 +67,8 @@ end
|
|
65
67
|
# Then, in rails, you would use it:
|
66
68
|
can? :view, page
|
67
69
|
cannot? :edit, page
|
68
|
-
authorize! :view, page # raises Allowy::AccessDenied if can?(:view, page)
|
69
|
-
can? :love_people, page # Will
|
70
|
+
authorize! :view, page # raises Allowy::AccessDenied if can?(:view, page) returns false
|
71
|
+
can? :love_people, page # Will raise error because `love_people` is not defined on the Access Control class
|
70
72
|
```
|
71
73
|
|
72
74
|
## Context
|
@@ -80,19 +82,19 @@ class PageAccess
|
|
80
82
|
include Allowy::AccessControl
|
81
83
|
|
82
84
|
def view?(page)
|
85
|
+
return true if context.params[:hiddedn_hack_for_admin]
|
83
86
|
context.user_signed_in? and page.published?
|
84
87
|
end
|
85
88
|
end
|
86
89
|
```
|
87
90
|
|
88
|
-
If you want to change the context in Rails then just override it
|
91
|
+
If you want to change the context in Rails then just override it in the controller or globally in the `ApplicationController`:
|
89
92
|
|
90
93
|
```ruby
|
91
|
-
class
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
delegate :current_company, :to => :context
|
94
|
+
class PagesController < ApplicationController
|
95
|
+
def allowy_context
|
96
|
+
{realy: 'anything', can_be: 'here', event: params}
|
97
|
+
end
|
96
98
|
end
|
97
99
|
```
|
98
100
|
|
@@ -104,16 +106,22 @@ I recommend creating your own base class to provide common context and maybe som
|
|
104
106
|
```ruby
|
105
107
|
class DefaultAccess
|
106
108
|
include Allowy::AccessControl
|
109
|
+
delegate :current_user, :to => :context
|
110
|
+
delegate :current_company, :to => :context
|
111
|
+
|
112
|
+
def domain_name
|
113
|
+
context.request.host
|
114
|
+
end
|
107
115
|
end
|
108
116
|
```
|
109
117
|
|
110
|
-
Then you can create multiple access control classes
|
118
|
+
Then you can create multiple access control classes:
|
111
119
|
|
112
120
|
```ruby
|
113
121
|
class PageAccess < DefaultAccess
|
114
122
|
# can? :view, page
|
115
123
|
def view?(page)
|
116
|
-
page and page.published?
|
124
|
+
page and page.published? and domain_name =~ /^www\./i
|
117
125
|
end
|
118
126
|
|
119
127
|
# can? :edit, page
|
@@ -124,6 +132,7 @@ class PageAccess < DefaultAccess
|
|
124
132
|
# can? :create, WikiPage
|
125
133
|
def create?(page_class)
|
126
134
|
# We can do something with WikiPage here if we need to
|
135
|
+
return false if page_class.count >= 2 # only 2 wiki pages allowed
|
127
136
|
# but can just ignore it and authorize based on current context only
|
128
137
|
current_user and current_user.admin?
|
129
138
|
end
|
@@ -136,13 +145,40 @@ class PageAccess < DefaultAccess
|
|
136
145
|
end
|
137
146
|
```
|
138
147
|
|
148
|
+
In your controller:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class PagesController < ApplicationController
|
152
|
+
def show
|
153
|
+
@page = Page.find(params[:id])
|
154
|
+
authorize! :view, @page # It will raise if declined
|
155
|
+
# can?, cannot? can be used too
|
156
|
+
end
|
157
|
+
|
158
|
+
# Add this to the ApplicationController to handle it globally
|
159
|
+
rescue_from Allowy::AccessDenied do |exception|
|
160
|
+
logger.debug "Access denied on #{exception.action} #{exception.subject.inspect}"
|
161
|
+
redirect_to new_user_session_url, :alert => exception.message
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
In your views:
|
167
|
+
|
168
|
+
```haml
|
169
|
+
# app/views/pages/show.html.haml
|
170
|
+
|
171
|
+
%h1= @page.name
|
172
|
+
= link_to "Edit", edit_page_path if can? :edit, @page
|
173
|
+
```
|
174
|
+
|
139
175
|
|
140
176
|
# Testing with RSpec
|
141
177
|
|
142
178
|
To test the access control classes you can just instantiate those passing context as a parameter.
|
143
|
-
Most of the
|
179
|
+
Most of the time you will stub out the context, so the test isolation is a piece of cake.
|
144
180
|
|
145
|
-
You need to `require 'allowy/rspec'
|
181
|
+
You need to `require 'allowy/rspec'`.
|
146
182
|
It will give you RSpec matcher `be_able_to` and `ignore_authorization!` macro for controller specs.
|
147
183
|
|
148
184
|
|
@@ -156,6 +192,11 @@ describe PageAccess do
|
|
156
192
|
describe "#view" do
|
157
193
|
it { should_not be_able_to :view, page }
|
158
194
|
|
195
|
+
# Or without the matcher
|
196
|
+
it "should not allow" do
|
197
|
+
subject.view?(page).should be_false
|
198
|
+
end
|
199
|
+
|
159
200
|
context "when published" do
|
160
201
|
before { page.publish! }
|
161
202
|
it { should be_able_to :view, page }
|
data/lib/allowy/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: allowy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70173734581140 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70173734581140
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70173734580640 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70173734580640
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: pry
|
38
|
-
requirement: &
|
38
|
+
requirement: &70173734579780 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70173734579780
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: guard
|
49
|
-
requirement: &
|
49
|
+
requirement: &70173734578960 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70173734578960
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: guard-rspec
|
60
|
-
requirement: &
|
60
|
+
requirement: &70173734578340 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70173734578340
|
69
69
|
description: Allowy provides CanCan-like way of checking permission but doesn't enforce
|
70
70
|
a tight DSL giving you more control
|
71
71
|
email:
|