petergate 3.1.0 → 3.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.
- checksums.yaml +4 -4
- data/README.md +37 -2
- data/lib/petergate/action_controller/base.rb +8 -9
- data/lib/petergate/active_record/base.rb +5 -21
- data/lib/petergate/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: acd5072223d85700246df5acd1445b292a918cf85a3b51b4ee42ae03c3648630
|
|
4
|
+
data.tar.gz: d084364761c8234ac5db4cc4434e88337913f07dea42cf7d2d3fdc15b3b8261d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffef6582b1bcd3d55d01c4655dff65a7d12ee47fbefeb32ba2339911db89827923a05e876396261c00849f966ecb36b3470247c057eb99dc60cf0822c8ee26f5
|
|
7
|
+
data.tar.gz: 9c4e7e84b9e07811651f6cd172f4b32b92540bd47a24a2ec986f028d64b7a3786d3a800aefe879f9d81f04700360a05f007d36148a22dea8b80b5456cc2d938e
|
data/README.md
CHANGED
|
@@ -130,6 +130,9 @@ Inside your views you can use logged_in?(:admin, :customer, :etc) to show or hid
|
|
|
130
130
|
<%= link_to "destroy", destroy_listing_path(listing) if logged_in?(:admin, :customer, :etc) %>
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
`logged_in?` tests roles. To ask only whether anyone is signed in, without
|
|
134
|
+
caring which role they hold, use `user_logged_in?`.
|
|
135
|
+
|
|
133
136
|
If you need to access available roles within your project you can by calling:
|
|
134
137
|
|
|
135
138
|
```ruby
|
|
@@ -140,7 +143,16 @@ User.first.available_roles # the same list, from an instance
|
|
|
140
143
|
`ROLES` is a constant on the model, so it is also reachable from your own
|
|
141
144
|
instance methods. A subclass shares its parent's roles.
|
|
142
145
|
|
|
143
|
-
|
|
146
|
+
#### Denying access yourself
|
|
147
|
+
|
|
148
|
+
Two helpers are available in controllers and in views:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
forbidden! # refuse someone who is signed in
|
|
152
|
+
unauthorized! # send a visitor to authentication, via authenticate_user!
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`forbidden!` is the one you want in your own filters:
|
|
144
156
|
|
|
145
157
|
```ruby
|
|
146
158
|
before_action :check_active_user
|
|
@@ -149,12 +161,35 @@ def check_active_user
|
|
|
149
161
|
forbidden! unless current_user.active
|
|
150
162
|
end
|
|
151
163
|
```
|
|
152
|
-
|
|
164
|
+
|
|
165
|
+
Both answer a `js`, `json` or `xml` request with a bare `403` or `401` rather
|
|
166
|
+
than a redirect, and do the same in an `ActionController::API` controller,
|
|
167
|
+
which has no format negotiation to offer.
|
|
168
|
+
|
|
169
|
+
##### The denial message
|
|
170
|
+
|
|
171
|
+
`forbidden!` takes one for a single call, and `access` sets a default for the
|
|
172
|
+
whole controller:
|
|
153
173
|
|
|
154
174
|
```ruby
|
|
175
|
+
forbidden! "Your account is suspended"
|
|
176
|
+
|
|
155
177
|
access user: [:show, :index], message: "You shall not pass"
|
|
156
178
|
```
|
|
157
179
|
|
|
180
|
+
The message is resolved in this order, first match winning:
|
|
181
|
+
|
|
182
|
+
| | Source |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| 1 | the argument passed to `forbidden!` |
|
|
185
|
+
| 2 | an `msg` request header |
|
|
186
|
+
| 3 | the `message:` option on `access` |
|
|
187
|
+
| 4 | `"Permission Denied"` |
|
|
188
|
+
|
|
189
|
+
Note the second entry: the `msg` header is read off the request, so a caller
|
|
190
|
+
can replace a message you set with `message:`. It is undocumented legacy
|
|
191
|
+
behaviour rather than something to rely on.
|
|
192
|
+
|
|
158
193
|
#### User Admin Example Form for Multiple Roles
|
|
159
194
|
|
|
160
195
|
```slim
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Included into ActionController::Base *and* ActionController::API: Rails runs
|
|
2
|
+
# the :action_controller load hook for both. See the hook at the bottom of this
|
|
3
|
+
# file for why that hook rather than the Base/API-specific pair.
|
|
1
4
|
module Petergate
|
|
2
5
|
module ActionController
|
|
3
6
|
module Base
|
|
@@ -106,15 +109,10 @@ module Petergate
|
|
|
106
109
|
defined?(self.class.controller_message) ? self.class.controller_message : 'Permission Denied'
|
|
107
110
|
end
|
|
108
111
|
|
|
109
|
-
# ActionController::API does not include ActionController::MimeResponds,
|
|
110
|
-
# so `respond_to` is unavailable there. API controllers get a bare status
|
|
111
|
-
# code instead of an HTML redirect, which is what an API caller expects.
|
|
112
|
-
def negotiates_formats?
|
|
113
|
-
respond_to?(:respond_to)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
112
|
def unauthorized!
|
|
117
|
-
|
|
113
|
+
# ActionController::API has no MimeResponds, so no respond_to; a bare
|
|
114
|
+
# status is the right answer for an API caller regardless.
|
|
115
|
+
return head(:unauthorized) if is_a?(::ActionController::API)
|
|
118
116
|
|
|
119
117
|
respond_to do |format|
|
|
120
118
|
format.any(:js, :json, :xml) do
|
|
@@ -127,7 +125,8 @@ module Petergate
|
|
|
127
125
|
end
|
|
128
126
|
|
|
129
127
|
def forbidden!(msg = nil)
|
|
130
|
-
|
|
128
|
+
# See unauthorized! -- API controllers cannot respond_to.
|
|
129
|
+
return head(:forbidden) if is_a?(::ActionController::API)
|
|
131
130
|
|
|
132
131
|
respond_to do |format|
|
|
133
132
|
format.any(:js, :json, :xml) do
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# Included into ActiveRecord::Base, through the load hook at the bottom of
|
|
2
|
+
# this file.
|
|
1
3
|
module Petergate
|
|
2
4
|
module ActiveRecord
|
|
3
5
|
module Base
|
|
@@ -6,27 +8,7 @@ module Petergate
|
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
module ClassMethods
|
|
9
|
-
# True when a class this one inherits from has already been through
|
|
10
|
-
# petergate. Only the superclass chain is walked: an included module
|
|
11
|
-
# carrying its own ROLES is somebody else's constant, not a sign that
|
|
12
|
-
# this model is already configured.
|
|
13
|
-
def petergate_configured_by_ancestor?
|
|
14
|
-
klass = superclass
|
|
15
|
-
|
|
16
|
-
while klass && klass != ::ActiveRecord::Base && klass != ::Object
|
|
17
|
-
return true if klass.const_defined?(:ROLES, false)
|
|
18
|
-
klass = klass.superclass
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
11
|
def petergate(roles: [:admin], multiple: true)
|
|
25
|
-
# A subclass shares everything its parent configured -- roles, scopes
|
|
26
|
-
# and callbacks. Running again would define scopes for roles the model
|
|
27
|
-
# can never hold and register a second after_initialize.
|
|
28
|
-
return if petergate_configured_by_ancestor?
|
|
29
|
-
|
|
30
12
|
if multiple
|
|
31
13
|
serialize :roles, coder: YAML
|
|
32
14
|
after_initialize do
|
|
@@ -39,7 +21,9 @@ module Petergate
|
|
|
39
21
|
end
|
|
40
22
|
|
|
41
23
|
instance_eval do
|
|
42
|
-
#
|
|
24
|
+
# Own constant only, so each model that calls petergate gets its own
|
|
25
|
+
# roles rather than deferring to whichever model loaded first.
|
|
26
|
+
# Configuring the same model twice keeps the first set.
|
|
43
27
|
const_set('ROLES', (roles + [:user]).uniq.map(&:to_sym)) unless const_defined?(:ROLES, false)
|
|
44
28
|
|
|
45
29
|
if multiple
|
data/lib/petergate/version.rb
CHANGED