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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 458fdd69a1955dde44434ec3ba661af445de0b754b09b8d61dfd7e959ba33632
4
- data.tar.gz: 5b99cfc5fb9d622d9b9b47a9777b3fe3bc51bc520b45ee52a29dfdbc633b3c7d
3
+ metadata.gz: acd5072223d85700246df5acd1445b292a918cf85a3b51b4ee42ae03c3648630
4
+ data.tar.gz: d084364761c8234ac5db4cc4434e88337913f07dea42cf7d2d3fdc15b3b8261d
5
5
  SHA512:
6
- metadata.gz: af0027d006aa080fe77ce5467ac3ec036d84d2ad3211caf21d267914fc18b46680ff3722dbc855801b7026bfdeeb420ffc5e53cac10611f78a82d063d2178486
7
- data.tar.gz: 686f7f73c012fc240a9734599cd2106e35dbe0b0694bff964aab9114fb3f8de9ff78a7a83dfcc57a39c10cce7c124cb05a592a2a5594dbe763cc198d0cc513fb
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
- If you need to deny access you can use the forbidden! method:
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
- If you want to change the `permission denied` message you can add to the access line:
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
- return head(:unauthorized) unless negotiates_formats?
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
- return head(:forbidden) unless negotiates_formats?
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
- # Configuring the same model twice keeps the first set of roles.
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
@@ -1,3 +1,3 @@
1
1
  module Petergate
2
- VERSION = "3.1.0"
2
+ VERSION = "3.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: petergate
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Isaac Sloan