simple_authorize 0.1.0
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 +7 -0
- data/CHANGELOG.md +38 -0
- data/LICENSE.txt +21 -0
- data/README.md +321 -0
- data/Rakefile +12 -0
- data/lib/generators/simple_authorize/install/install_generator.rb +25 -0
- data/lib/generators/simple_authorize/install/templates/README +80 -0
- data/lib/generators/simple_authorize/install/templates/application_policy.rb +30 -0
- data/lib/generators/simple_authorize/install/templates/simple_authorize.rb +16 -0
- data/lib/simple_authorize/configuration.rb +40 -0
- data/lib/simple_authorize/controller.rb +320 -0
- data/lib/simple_authorize/policy.rb +93 -0
- data/lib/simple_authorize/version.rb +5 -0
- data/lib/simple_authorize.rb +28 -0
- data/sig/simple_authorize.rbs +4 -0
- metadata +102 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 79902cf5162234cb48b57c7a0584265292141771e59594199c7adee9dea7b1c7
|
|
4
|
+
data.tar.gz: 3022a11895f81c9a02918d88bb1929c4c75afb13eec0b0315e36cbdbaecc0745
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 24db25e4450b4f5cb060b902f6bb99b9544aaf39397757ad0403b2feeb4ee99a678632219eec710cce36c0923fad9cb0da9465e85fa9edf60de7028e99a524a9
|
|
7
|
+
data.tar.gz: 2cffd9ff55d8a33d6093297d8c2beea976403cdf5c2d0c5b77a1f40990fff673ee43c763264964d721be1d97c68d74190bbf35c4674419ee6da999ea19a25262
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of SimpleAuthorize
|
|
12
|
+
- Policy-based authorization system
|
|
13
|
+
- Controller concern with authorization methods
|
|
14
|
+
- Base policy class with default deny-all policies
|
|
15
|
+
- Policy scope support for filtering collections
|
|
16
|
+
- Strong parameters integration via `permitted_attributes` and `policy_params`
|
|
17
|
+
- Automatic verification module (opt-in)
|
|
18
|
+
- Headless policy support for policies without models
|
|
19
|
+
- Namespace support for policies
|
|
20
|
+
- Role-based helper methods (`admin_user?`, `contributor_user?`, `viewer_user?`)
|
|
21
|
+
- Custom error handling with `NotAuthorizedError`
|
|
22
|
+
- Install generator (`rails generate simple_authorize:install`)
|
|
23
|
+
- Configuration system via initializer
|
|
24
|
+
- Comprehensive documentation and examples
|
|
25
|
+
- Test helper methods for easy testing
|
|
26
|
+
- Backwards compatibility aliases for Pundit-style usage
|
|
27
|
+
|
|
28
|
+
## [0.1.0] - 2025-11-01
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Initial gem structure
|
|
32
|
+
- Core authorization framework extracted from production Rails application
|
|
33
|
+
- MIT license
|
|
34
|
+
- README with comprehensive documentation
|
|
35
|
+
- Generator templates for installation
|
|
36
|
+
|
|
37
|
+
[Unreleased]: https://github.com/scottlaplant/simple_authorize/compare/v0.1.0...HEAD
|
|
38
|
+
[0.1.0]: https://github.com/scottlaplant/simple_authorize/releases/tag/v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Scott
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# SimpleAuthorize
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/simple_authorize)
|
|
4
|
+
[](https://github.com/scottlaplant/simple_authorize/actions)
|
|
5
|
+
|
|
6
|
+
SimpleAuthorize is a lightweight, powerful authorization framework for Rails that provides policy-based access control without external dependencies. Inspired by Pundit, it offers a clean API for managing permissions in your Rails applications.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ๐ **Policy-Based Authorization** - Define authorization rules in dedicated policy classes
|
|
11
|
+
- ๐ฏ **Scope Filtering** - Automatically filter collections based on user permissions
|
|
12
|
+
- ๐ **Role-Based Access** - Built-in support for role-based authorization
|
|
13
|
+
- ๐ **Zero Dependencies** - No external gems required (only Rails)
|
|
14
|
+
- โ
**Strong Parameters Integration** - Automatically build permitted params from policies
|
|
15
|
+
- ๐งช **Test Friendly** - Easy to test policies in isolation
|
|
16
|
+
- ๐ **Rails Generators** - Quickly scaffold policies for your models
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Add this line to your application's Gemfile:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
gem 'simple_authorize'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And then execute:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bundle install
|
|
30
|
+
rails generate simple_authorize:install
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This will create:
|
|
34
|
+
- `config/initializers/simple_authorize.rb` - Configuration file
|
|
35
|
+
- `app/policies/application_policy.rb` - Base policy class
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### 1. Include SimpleAuthorize in your ApplicationController
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
class ApplicationController < ActionController::Base
|
|
43
|
+
include SimpleAuthorize::Controller
|
|
44
|
+
rescue_from_authorization_errors
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Create a Policy
|
|
49
|
+
|
|
50
|
+
Create a policy class for your model in `app/policies/`:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# app/policies/post_policy.rb
|
|
54
|
+
class PostPolicy < ApplicationPolicy
|
|
55
|
+
def index?
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def show?
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create?
|
|
64
|
+
user.present?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def update?
|
|
68
|
+
user.present? && (record.user_id == user.id || user.admin?)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def destroy?
|
|
72
|
+
update?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Scope < ApplicationPolicy::Scope
|
|
76
|
+
def resolve
|
|
77
|
+
if user&.admin?
|
|
78
|
+
scope.all
|
|
79
|
+
else
|
|
80
|
+
scope.where(published: true)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Use Authorization in Your Controllers
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class PostsController < ApplicationController
|
|
91
|
+
def index
|
|
92
|
+
@posts = policy_scope(Post)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def show
|
|
96
|
+
@post = Post.find(params[:id])
|
|
97
|
+
authorize @post
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def create
|
|
101
|
+
@post = Post.new(post_params)
|
|
102
|
+
authorize @post
|
|
103
|
+
|
|
104
|
+
if @post.save
|
|
105
|
+
redirect_to @post
|
|
106
|
+
else
|
|
107
|
+
render :new
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def post_params
|
|
114
|
+
params.require(:post).permit(:title, :body, :published)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. Use in Views
|
|
120
|
+
|
|
121
|
+
Check permissions in your views:
|
|
122
|
+
|
|
123
|
+
```erb
|
|
124
|
+
<% if policy(@post).update? %>
|
|
125
|
+
<%= link_to "Edit", edit_post_path(@post) %>
|
|
126
|
+
<% end %>
|
|
127
|
+
|
|
128
|
+
<% if policy(@post).destroy? %>
|
|
129
|
+
<%= link_to "Delete", post_path(@post), method: :delete %>
|
|
130
|
+
<% end %>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Core Concepts
|
|
134
|
+
|
|
135
|
+
### Policies
|
|
136
|
+
|
|
137
|
+
Policies are plain Ruby objects that encapsulate authorization logic. Each policy corresponds to a model and defines what actions users can perform.
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
class PostPolicy < ApplicationPolicy
|
|
141
|
+
def update?
|
|
142
|
+
# Only the owner or an admin can update
|
|
143
|
+
user.present? && (record.user_id == user.id || user.admin?)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Scopes
|
|
149
|
+
|
|
150
|
+
Scopes filter collections based on user permissions:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
class PostPolicy < ApplicationPolicy
|
|
154
|
+
class Scope < ApplicationPolicy::Scope
|
|
155
|
+
def resolve
|
|
156
|
+
if user&.admin?
|
|
157
|
+
scope.all
|
|
158
|
+
else
|
|
159
|
+
scope.where(published: true)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Use in controllers:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
def index
|
|
170
|
+
@posts = policy_scope(Post)
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Strong Parameters
|
|
175
|
+
|
|
176
|
+
SimpleAuthorize can automatically build permitted parameters from policies:
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
class PostPolicy < ApplicationPolicy
|
|
180
|
+
def permitted_attributes
|
|
181
|
+
if user&.admin?
|
|
182
|
+
[:title, :body, :published, :featured]
|
|
183
|
+
else
|
|
184
|
+
[:title, :body]
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use in controllers:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
def post_params
|
|
194
|
+
policy_params(Post, :post)
|
|
195
|
+
# Or manually:
|
|
196
|
+
# params.require(:post).permit(*permitted_attributes(Post.new))
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Advanced Features
|
|
201
|
+
|
|
202
|
+
### Headless Policies
|
|
203
|
+
|
|
204
|
+
For policies that don't correspond to a model:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
class DashboardPolicy < ApplicationPolicy
|
|
208
|
+
def show?
|
|
209
|
+
user&.admin?
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# In controller:
|
|
214
|
+
def show
|
|
215
|
+
authorize_headless(DashboardPolicy)
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Custom Query Methods
|
|
220
|
+
|
|
221
|
+
Define custom authorization queries:
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
class PostPolicy < ApplicationPolicy
|
|
225
|
+
def publish?
|
|
226
|
+
user&.admin? || (user&.contributor? && owner?)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# In controller:
|
|
231
|
+
authorize @post, :publish?
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Automatic Verification
|
|
235
|
+
|
|
236
|
+
Ensure every action is authorized:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
class ApplicationController < ActionController::Base
|
|
240
|
+
include SimpleAuthorize::Controller
|
|
241
|
+
include SimpleAuthorize::Controller::AutoVerify # Enable auto-verification
|
|
242
|
+
rescue_from_authorization_errors
|
|
243
|
+
end
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This will require `authorize` or `policy_scope` in all actions.
|
|
247
|
+
|
|
248
|
+
Skip verification when needed:
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
class PublicController < ApplicationController
|
|
252
|
+
skip_authorization_check :index, :show
|
|
253
|
+
end
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Testing
|
|
257
|
+
|
|
258
|
+
Test policies in isolation:
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
require 'test_helper'
|
|
262
|
+
|
|
263
|
+
class PostPolicyTest < ActiveSupport::TestCase
|
|
264
|
+
test "admin can update any post" do
|
|
265
|
+
admin = users(:admin)
|
|
266
|
+
post = posts(:one)
|
|
267
|
+
policy = PostPolicy.new(admin, post)
|
|
268
|
+
|
|
269
|
+
assert policy.update?
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
test "user can only update their own posts" do
|
|
273
|
+
user = users(:regular)
|
|
274
|
+
own_post = posts(:user_post)
|
|
275
|
+
other_post = posts(:other_post)
|
|
276
|
+
|
|
277
|
+
assert PostPolicy.new(user, own_post).update?
|
|
278
|
+
refute PostPolicy.new(user, other_post).update?
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Development
|
|
284
|
+
|
|
285
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
286
|
+
|
|
287
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
288
|
+
|
|
289
|
+
## Comparison with Pundit
|
|
290
|
+
|
|
291
|
+
SimpleAuthorize is heavily inspired by Pundit and offers a similar API. Key differences:
|
|
292
|
+
|
|
293
|
+
| Feature | SimpleAuthorize | Pundit |
|
|
294
|
+
|---------|----------------|--------|
|
|
295
|
+
| Dependencies | None (Rails only) | Standalone gem |
|
|
296
|
+
| Base class | `SimpleAuthorize::Policy` | `ApplicationPolicy` (user-defined) |
|
|
297
|
+
| Installation | Generator creates base policy | Manual setup required |
|
|
298
|
+
| Module name | `SimpleAuthorize::Controller` | `Pundit` |
|
|
299
|
+
| Compatibility | Rails 6.0+ | Rails 4.0+ |
|
|
300
|
+
|
|
301
|
+
Migration from Pundit is straightforward - most code will work with minimal changes.
|
|
302
|
+
|
|
303
|
+
## Contributing
|
|
304
|
+
|
|
305
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/scottlaplant/simple_authorize.
|
|
306
|
+
|
|
307
|
+
1. Fork it
|
|
308
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
309
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
310
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
311
|
+
5. Create new Pull Request
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
316
|
+
|
|
317
|
+
## Credits
|
|
318
|
+
|
|
319
|
+
Created by Scott LaPlant
|
|
320
|
+
|
|
321
|
+
Inspired by [Pundit](https://github.com/varvet/pundit) by Elabs
|
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module SimpleAuthorize
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Creates a SimpleAuthorize initializer and ApplicationPolicy base class"
|
|
11
|
+
|
|
12
|
+
def copy_initializer
|
|
13
|
+
template "simple_authorize.rb", "config/initializers/simple_authorize.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def copy_application_policy
|
|
17
|
+
template "application_policy.rb", "app/policies/application_policy.rb"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def show_readme
|
|
21
|
+
readme "README" if behavior == :invoke
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
===============================================================================
|
|
2
|
+
|
|
3
|
+
SimpleAuthorize has been installed!
|
|
4
|
+
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
Next steps:
|
|
8
|
+
|
|
9
|
+
1. Include SimpleAuthorize::Controller in your ApplicationController:
|
|
10
|
+
|
|
11
|
+
class ApplicationController < ActionController::Base
|
|
12
|
+
include SimpleAuthorize::Controller
|
|
13
|
+
rescue_from_authorization_errors
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
2. Create policies for your models in app/policies/:
|
|
17
|
+
|
|
18
|
+
# app/policies/post_policy.rb
|
|
19
|
+
class PostPolicy < ApplicationPolicy
|
|
20
|
+
def index?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create?
|
|
29
|
+
user.present?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def update?
|
|
33
|
+
user.present? && (record.user_id == user.id || user.admin?)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def destroy?
|
|
37
|
+
update?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Scope < ApplicationPolicy::Scope
|
|
41
|
+
def resolve
|
|
42
|
+
if user&.admin?
|
|
43
|
+
scope.all
|
|
44
|
+
else
|
|
45
|
+
scope.where(published: true)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
3. Use authorization in your controllers:
|
|
52
|
+
|
|
53
|
+
class PostsController < ApplicationController
|
|
54
|
+
def index
|
|
55
|
+
@posts = policy_scope(Post)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def show
|
|
59
|
+
@post = Post.find(params[:id])
|
|
60
|
+
authorize @post
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create
|
|
64
|
+
@post = Post.new(post_params)
|
|
65
|
+
authorize @post
|
|
66
|
+
# ...
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
4. (Optional) Enable automatic verification:
|
|
71
|
+
|
|
72
|
+
class ApplicationController < ActionController::Base
|
|
73
|
+
include SimpleAuthorize::Controller
|
|
74
|
+
include SimpleAuthorize::Controller::AutoVerify
|
|
75
|
+
rescue_from_authorization_errors
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
For more information, see: https://github.com/yourusername/simple_authorize
|
|
79
|
+
|
|
80
|
+
===============================================================================
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Base policy class that all other policies inherit from
|
|
4
|
+
# Inherits from SimpleAuthorize::Policy which provides default deny-all policies
|
|
5
|
+
class ApplicationPolicy < SimpleAuthorize::Policy
|
|
6
|
+
# Override default policies here if needed
|
|
7
|
+
# For example, allow all logged-in users to view index:
|
|
8
|
+
# def index?
|
|
9
|
+
# logged_in?
|
|
10
|
+
# end
|
|
11
|
+
|
|
12
|
+
# Add custom helper methods here
|
|
13
|
+
# protected
|
|
14
|
+
#
|
|
15
|
+
# def owned_by_user?
|
|
16
|
+
# record.user_id == user&.id
|
|
17
|
+
# end
|
|
18
|
+
|
|
19
|
+
# Scope class for filtering collections
|
|
20
|
+
class Scope < SimpleAuthorize::Policy::Scope
|
|
21
|
+
# Override the resolve method to customize collection filtering
|
|
22
|
+
# def resolve
|
|
23
|
+
# if admin?
|
|
24
|
+
# scope.all
|
|
25
|
+
# else
|
|
26
|
+
# scope.where(published: true)
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Configure SimpleAuthorize
|
|
4
|
+
SimpleAuthorize.configure do |config|
|
|
5
|
+
# Default error message shown to users when not authorized
|
|
6
|
+
# config.default_error_message = "You are not authorized to perform this action."
|
|
7
|
+
|
|
8
|
+
# Enable automatic verification (requires including SimpleAuthorize::Controller::AutoVerify)
|
|
9
|
+
# config.auto_verify = false
|
|
10
|
+
|
|
11
|
+
# The method to call to get the current user (default: current_user)
|
|
12
|
+
# config.current_user_method = :current_user
|
|
13
|
+
|
|
14
|
+
# Custom redirect path for unauthorized access (default: uses referrer or root_path)
|
|
15
|
+
# config.unauthorized_redirect_path = "/unauthorized"
|
|
16
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAuthorize
|
|
4
|
+
class Configuration
|
|
5
|
+
# Default error message shown to users when not authorized
|
|
6
|
+
attr_accessor :default_error_message
|
|
7
|
+
|
|
8
|
+
# Whether to enable automatic verification (opt-in)
|
|
9
|
+
attr_accessor :auto_verify
|
|
10
|
+
|
|
11
|
+
# The method to call to get the current user (default: current_user)
|
|
12
|
+
attr_accessor :current_user_method
|
|
13
|
+
|
|
14
|
+
# Custom redirect path for unauthorized access
|
|
15
|
+
attr_accessor :unauthorized_redirect_path
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@default_error_message = "You are not authorized to perform this action."
|
|
19
|
+
@auto_verify = false
|
|
20
|
+
@current_user_method = :current_user
|
|
21
|
+
@unauthorized_redirect_path = nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
attr_writer :configuration
|
|
27
|
+
|
|
28
|
+
def configuration
|
|
29
|
+
@configuration ||= Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def configure
|
|
33
|
+
yield(configuration)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reset_configuration!
|
|
37
|
+
@configuration = Configuration.new
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAuthorize
|
|
4
|
+
# Enhanced authorization system with full feature parity
|
|
5
|
+
# Provides comprehensive authorization without external dependencies
|
|
6
|
+
module Controller
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
# Custom error classes for authorization
|
|
10
|
+
class NotAuthorizedError < StandardError
|
|
11
|
+
attr_reader :query, :record, :policy
|
|
12
|
+
|
|
13
|
+
def initialize(options = {})
|
|
14
|
+
if options.is_a?(String)
|
|
15
|
+
# Handle plain string message
|
|
16
|
+
super(options)
|
|
17
|
+
else
|
|
18
|
+
# Handle hash options
|
|
19
|
+
@query = options[:query]
|
|
20
|
+
@record = options[:record]
|
|
21
|
+
@policy = options[:policy]
|
|
22
|
+
|
|
23
|
+
message = options[:message] || "not allowed to #{@query} this #{@record.class}"
|
|
24
|
+
super(message)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class PolicyNotDefinedError < StandardError; end
|
|
30
|
+
class AuthorizationNotPerformedError < StandardError; end
|
|
31
|
+
class PolicyScopingNotPerformedError < StandardError; end
|
|
32
|
+
# Alias for backwards compatibility
|
|
33
|
+
ScopingNotPerformedError = PolicyScopingNotPerformedError
|
|
34
|
+
|
|
35
|
+
included do
|
|
36
|
+
# Make these available as helper methods in views
|
|
37
|
+
helper_method :policy, :policy_scope, :authorized_user if respond_to?(:helper_method)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Module to enable automatic verification - opt-in for safety
|
|
41
|
+
module AutoVerify
|
|
42
|
+
extend ActiveSupport::Concern
|
|
43
|
+
|
|
44
|
+
included do
|
|
45
|
+
# Track whether authorization was performed
|
|
46
|
+
after_action :verify_authorized, except: :index
|
|
47
|
+
after_action :verify_policy_scoped, only: :index
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Core authorization methods
|
|
52
|
+
|
|
53
|
+
def authorize(record, query = nil, policy_class: nil)
|
|
54
|
+
query ||= "#{action_name}?"
|
|
55
|
+
@_policy = policy(record, policy_class: policy_class)
|
|
56
|
+
|
|
57
|
+
unless @_policy.public_send(query)
|
|
58
|
+
raise NotAuthorizedError.new(query: query, record: record, policy: @_policy)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@authorization_performed = true
|
|
62
|
+
record
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Authorize and raise exception if not authorized
|
|
66
|
+
def authorize!(record, query = nil, policy_class: nil)
|
|
67
|
+
authorize(record, query, policy_class: policy_class)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get or instantiate policy for a record
|
|
71
|
+
def policy(record, policy_class: nil, namespace: nil)
|
|
72
|
+
policy_class ||= if namespace
|
|
73
|
+
policy_class_for(record, namespace: namespace)
|
|
74
|
+
else
|
|
75
|
+
policy_class_for(record)
|
|
76
|
+
end
|
|
77
|
+
policy_class.new(authorized_user, record)
|
|
78
|
+
rescue NameError
|
|
79
|
+
raise PolicyNotDefinedError, "unable to find policy `#{policy_class}` for `#{record}`"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Ensure policy exists, raising if not found
|
|
83
|
+
def policy!(record, policy_class: nil)
|
|
84
|
+
policy(record, policy_class: policy_class)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Scope a relation using the policy scope
|
|
88
|
+
def policy_scope(scope, policy_scope_class: nil)
|
|
89
|
+
@policy_scoping_performed = true
|
|
90
|
+
|
|
91
|
+
policy_scope_class ||= policy_scope_class_for(scope)
|
|
92
|
+
policy_scope_class.new(authorized_user, scope).resolve
|
|
93
|
+
rescue NameError
|
|
94
|
+
raise PolicyNotDefinedError, "unable to find scope `#{policy_scope_class}` for `#{scope}`"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Ensure scope exists, raising if not found
|
|
98
|
+
def policy_scope!(scope, policy_scope_class: nil)
|
|
99
|
+
policy_scope(scope, policy_scope_class: policy_scope_class)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Get permitted attributes for strong parameters
|
|
103
|
+
def permitted_attributes(record, action = nil)
|
|
104
|
+
action ||= action_name
|
|
105
|
+
policy = policy(record)
|
|
106
|
+
method_name = "permitted_attributes_for_#{action}"
|
|
107
|
+
|
|
108
|
+
if policy.respond_to?(method_name)
|
|
109
|
+
policy.public_send(method_name)
|
|
110
|
+
elsif policy.respond_to?(:permitted_attributes)
|
|
111
|
+
policy.permitted_attributes
|
|
112
|
+
else
|
|
113
|
+
raise PolicyNotDefinedError, "unable to find permitted attributes for #{record}"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Automatically build permitted params from policy
|
|
118
|
+
def policy_params(record, param_key = nil)
|
|
119
|
+
param_key ||= record.model_name.param_key
|
|
120
|
+
params.require(param_key).permit(*permitted_attributes(record))
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Verify that authorization was performed
|
|
124
|
+
def verify_authorized
|
|
125
|
+
return if authorization_performed?
|
|
126
|
+
raise AuthorizationNotPerformedError, "#{self.class}##{action_name} is missing authorization"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Verify that scoping was performed for index actions
|
|
130
|
+
def verify_policy_scoped
|
|
131
|
+
return if policy_scoped?
|
|
132
|
+
raise PolicyScopingNotPerformedError, "#{self.class}##{action_name} is missing policy scope"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Skip authorization verification for specific actions
|
|
136
|
+
def skip_authorization
|
|
137
|
+
@authorization_performed = true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Skip policy scope verification for specific actions
|
|
141
|
+
def skip_policy_scope
|
|
142
|
+
@policy_scoping_performed = true
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Check if authorization was performed
|
|
146
|
+
def authorization_performed?
|
|
147
|
+
@authorization_performed == true
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Check if policy scoping was performed
|
|
151
|
+
def policy_scoped?
|
|
152
|
+
@policy_scoping_performed == true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Get the user for authorization (can be overridden)
|
|
156
|
+
def authorized_user
|
|
157
|
+
current_user
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Reset authorization tracking (useful in tests)
|
|
161
|
+
def reset_authorization
|
|
162
|
+
@authorization_performed = nil
|
|
163
|
+
@policy_scoping_performed = nil
|
|
164
|
+
@_policy = nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Support for headless policies (policies without a model)
|
|
168
|
+
def authorize_headless(policy_class, query = nil)
|
|
169
|
+
query ||= "#{action_name}?"
|
|
170
|
+
policy = policy_class.new(authorized_user, nil)
|
|
171
|
+
|
|
172
|
+
unless policy.public_send(query)
|
|
173
|
+
raise NotAuthorizedError.new(query: query, record: policy_class, policy: policy)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
@authorization_performed = true
|
|
177
|
+
true
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Check if user can perform action without raising
|
|
181
|
+
def allowed_to?(action, record, policy_class: nil)
|
|
182
|
+
policy = policy(record, policy_class: policy_class)
|
|
183
|
+
policy.public_send("#{action}?")
|
|
184
|
+
rescue PolicyNotDefinedError
|
|
185
|
+
false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Get all allowed actions for a record
|
|
189
|
+
def allowed_actions(record)
|
|
190
|
+
policy = policy(record)
|
|
191
|
+
actions = []
|
|
192
|
+
|
|
193
|
+
%i[index? show? create? update? destroy?].each do |method|
|
|
194
|
+
actions << method.to_s.delete("?").to_sym if policy.respond_to?(method) && policy.public_send(method)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
actions
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Role helper methods
|
|
201
|
+
def admin_user?
|
|
202
|
+
current_user&.admin?
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def contributor_user?
|
|
206
|
+
current_user&.contributor?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def viewer_user?
|
|
210
|
+
current_user&.viewer?
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Alias for handle_unauthorized that matches common convention
|
|
214
|
+
def user_not_authorized(exception = nil)
|
|
215
|
+
handle_unauthorized(exception)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
protected
|
|
219
|
+
|
|
220
|
+
def policy_class_for(record, namespace: nil)
|
|
221
|
+
klass = record.class
|
|
222
|
+
record_class = if record.is_a?(Class)
|
|
223
|
+
record.name
|
|
224
|
+
elsif record.respond_to?(:model_name)
|
|
225
|
+
record.model_name.to_s
|
|
226
|
+
elsif klass.respond_to?(:model_name)
|
|
227
|
+
klass.model_name.to_s
|
|
228
|
+
else
|
|
229
|
+
klass.name
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
policy_class_name = if namespace
|
|
233
|
+
"#{namespace.to_s.camelize}::#{record_class}Policy"
|
|
234
|
+
else
|
|
235
|
+
"#{record_class}Policy"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
begin
|
|
239
|
+
policy_class_name.constantize
|
|
240
|
+
rescue NameError
|
|
241
|
+
# Fall back to non-namespaced policy if namespaced one doesn't exist
|
|
242
|
+
if namespace
|
|
243
|
+
"#{record_class}Policy".constantize
|
|
244
|
+
else
|
|
245
|
+
raise
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def policy_scope_class_for(scope)
|
|
251
|
+
if scope.respond_to?(:model_name)
|
|
252
|
+
"#{scope.model_name}Policy::Scope".constantize
|
|
253
|
+
elsif scope.is_a?(Class)
|
|
254
|
+
"#{scope}Policy::Scope".constantize
|
|
255
|
+
else
|
|
256
|
+
"#{scope.class}Policy::Scope".constantize
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Handle authorization errors
|
|
261
|
+
def handle_unauthorized(exception = nil)
|
|
262
|
+
flash[:alert] = "You are not authorized to perform this action."
|
|
263
|
+
safe_redirect_path = safe_referrer_path || root_path
|
|
264
|
+
|
|
265
|
+
if exception
|
|
266
|
+
redirect_to(safe_redirect_path, status: :see_other)
|
|
267
|
+
else
|
|
268
|
+
redirect_to(safe_redirect_path)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Safely get referrer path, only if it's from our own domain
|
|
273
|
+
def safe_referrer_path
|
|
274
|
+
referrer = request.referrer
|
|
275
|
+
return nil unless referrer.present?
|
|
276
|
+
|
|
277
|
+
referrer_uri = URI.parse(referrer)
|
|
278
|
+
request_uri = URI.parse(request.url)
|
|
279
|
+
|
|
280
|
+
# Only allow referrers from the same host
|
|
281
|
+
if referrer_uri.host == request_uri.host
|
|
282
|
+
referrer_uri.path
|
|
283
|
+
else
|
|
284
|
+
nil
|
|
285
|
+
end
|
|
286
|
+
rescue URI::InvalidURIError
|
|
287
|
+
nil
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Class methods for controller configuration
|
|
291
|
+
class_methods do
|
|
292
|
+
# Rescue from authorization errors
|
|
293
|
+
def rescue_from_authorization_errors
|
|
294
|
+
rescue_from SimpleAuthorize::Controller::NotAuthorizedError, with: :handle_unauthorized
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Skip authorization for specific actions
|
|
298
|
+
def skip_authorization_check(*actions)
|
|
299
|
+
skip_after_action :verify_authorized, only: actions
|
|
300
|
+
skip_after_action :verify_policy_scoped, only: actions
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Skip all authorization checks for controller
|
|
304
|
+
def skip_all_authorization_checks
|
|
305
|
+
skip_after_action :verify_authorized
|
|
306
|
+
skip_after_action :verify_policy_scoped
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Configure which actions need authorization
|
|
310
|
+
def authorize_actions(*actions)
|
|
311
|
+
after_action :verify_authorized, only: actions
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Configure which actions need policy scoping
|
|
315
|
+
def scope_actions(*actions)
|
|
316
|
+
after_action :verify_policy_scoped, only: actions
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAuthorize
|
|
4
|
+
# Base policy class that all other policies inherit from
|
|
5
|
+
class Policy
|
|
6
|
+
attr_reader :user, :record
|
|
7
|
+
|
|
8
|
+
def initialize(user, record)
|
|
9
|
+
@user = user
|
|
10
|
+
@record = record
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Default policies - deny everything by default
|
|
14
|
+
def index?
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show?
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def new?
|
|
27
|
+
create?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def update?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def edit?
|
|
35
|
+
update?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def destroy?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Helper methods
|
|
43
|
+
protected
|
|
44
|
+
|
|
45
|
+
def admin?
|
|
46
|
+
user&.admin?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def contributor?
|
|
50
|
+
user&.contributor?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def viewer?
|
|
54
|
+
user&.viewer?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def owner?
|
|
58
|
+
record.respond_to?(:user_id) && record.user_id == user&.id
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def logged_in?
|
|
62
|
+
user.present?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def can_create_content?
|
|
66
|
+
user&.can_create_content?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def can_manage_content?
|
|
70
|
+
user&.can_manage_content?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Scope class for filtering collections
|
|
74
|
+
class Scope
|
|
75
|
+
attr_reader :user, :scope
|
|
76
|
+
|
|
77
|
+
def initialize(user, scope)
|
|
78
|
+
@user = user
|
|
79
|
+
@scope = scope
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def resolve
|
|
83
|
+
scope.all
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
protected
|
|
87
|
+
|
|
88
|
+
def admin?
|
|
89
|
+
user&.admin?
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/all"
|
|
4
|
+
require_relative "simple_authorize/version"
|
|
5
|
+
require_relative "simple_authorize/configuration"
|
|
6
|
+
require_relative "simple_authorize/controller"
|
|
7
|
+
require_relative "simple_authorize/policy"
|
|
8
|
+
|
|
9
|
+
module SimpleAuthorize
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
# Railtie for automatic integration with Rails
|
|
13
|
+
class Railtie < Rails::Railtie
|
|
14
|
+
initializer "simple_authorize.configure" do
|
|
15
|
+
ActiveSupport.on_load(:action_controller) do
|
|
16
|
+
# Make SimpleAuthorize::Controller available as Authorization for backwards compatibility
|
|
17
|
+
::Authorization = SimpleAuthorize::Controller unless defined?(::Authorization)
|
|
18
|
+
# Make SimpleAuthorize::Policy available as ApplicationPolicy for backwards compatibility
|
|
19
|
+
::ApplicationPolicy = SimpleAuthorize::Policy unless defined?(::ApplicationPolicy)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Generate initializer template
|
|
24
|
+
generators do
|
|
25
|
+
require_relative "generators/simple_authorize/install/install_generator"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: simple_authorize
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Scott
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activesupport
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '6.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '6.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: railties
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '6.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '6.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '6.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '6.0'
|
|
54
|
+
description: SimpleAuthorize is a lightweight authorization framework for Rails that
|
|
55
|
+
provides policy-based access control, role management, and scope filtering without
|
|
56
|
+
requiring external gems. Inspired by Pundit but completely standalone.
|
|
57
|
+
email:
|
|
58
|
+
- scottlaplant@users.noreply.github.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- CHANGELOG.md
|
|
64
|
+
- LICENSE.txt
|
|
65
|
+
- README.md
|
|
66
|
+
- Rakefile
|
|
67
|
+
- lib/generators/simple_authorize/install/install_generator.rb
|
|
68
|
+
- lib/generators/simple_authorize/install/templates/README
|
|
69
|
+
- lib/generators/simple_authorize/install/templates/application_policy.rb
|
|
70
|
+
- lib/generators/simple_authorize/install/templates/simple_authorize.rb
|
|
71
|
+
- lib/simple_authorize.rb
|
|
72
|
+
- lib/simple_authorize/configuration.rb
|
|
73
|
+
- lib/simple_authorize/controller.rb
|
|
74
|
+
- lib/simple_authorize/policy.rb
|
|
75
|
+
- lib/simple_authorize/version.rb
|
|
76
|
+
- sig/simple_authorize.rbs
|
|
77
|
+
homepage: https://github.com/scottlaplant/simple_authorize
|
|
78
|
+
licenses:
|
|
79
|
+
- MIT
|
|
80
|
+
metadata:
|
|
81
|
+
homepage_uri: https://github.com/scottlaplant/simple_authorize
|
|
82
|
+
source_code_uri: https://github.com/scottlaplant/simple_authorize
|
|
83
|
+
changelog_uri: https://github.com/scottlaplant/simple_authorize/blob/main/CHANGELOG.md
|
|
84
|
+
bug_tracker_uri: https://github.com/scottlaplant/simple_authorize/issues
|
|
85
|
+
rdoc_options: []
|
|
86
|
+
require_paths:
|
|
87
|
+
- lib
|
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: 3.0.0
|
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
requirements: []
|
|
99
|
+
rubygems_version: 3.6.9
|
|
100
|
+
specification_version: 4
|
|
101
|
+
summary: Simple, powerful authorization for Rails without external dependencies
|
|
102
|
+
test_files: []
|