active_entry 1.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +182 -0
- data/Rakefile +3 -0
- data/lib/active_entry.rb +54 -0
- data/lib/active_entry/controller_methods.rb +83 -0
- data/lib/active_entry/errors.rb +66 -0
- data/lib/active_entry/railtie.rb +9 -0
- data/lib/active_entry/version.rb +3 -0
- data/lib/tasks/active_entry_tasks.rake +4 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 15c793e52a2f7f0b43f1752bb61303f2fae758c763264fe3e73561ca3c272b8b
|
4
|
+
data.tar.gz: 49ba592be85bb9f3f1642d748e4cbc235b4f49461dca4483773fb1de85252028
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1e009ca5bbd3b9c2d4153f96cf4dd864dd83196c317c276a7d2a71bde273325c6a59f2340e28ca6cadc83c4430da0fefa1f07846ffdd0d9e42409c2b91eab73e
|
7
|
+
data.tar.gz: 9e62985d0726b6323126b3929d7a3b9ad2d1d6d51e8488da8a6f7061b8c2d9efb94f284b260d69d344f0a8f140afa5b1428a4fb9c20d632a037542ca1d28da34
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 tobiasfeistmantl
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
[<img src="active_entry_logo.png" alt="Active Entry Logo" width="250px"/>](https://github.com/TFM-Agency/active_entry)
|
2
|
+
|
3
|
+
# Active Entry - Simple and flexible authentication and authorization
|
4
|
+
|
5
|
+
Active Entry is a simple and secure authentication and authorization system for your Rails application, which lets you to authenticate and authorize directly in your controllers.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'active_entry'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install active_entry
|
22
|
+
```
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
With Active Entry authentication and authorization is done in your Rails controllers. To enable authentication and authorization in one of your controllers, just add a before action for `authenticate!` and `authorize!` and the user has to authenticate and authorize on every call.
|
26
|
+
You probably want to control authentication and authorization for every controller action you have in your app. To enable this, just add the before action to the `ApplicationController`.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class ApplicationController < ActionController::Base
|
30
|
+
before_action :authenticate!, :authorize!
|
31
|
+
# ...
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
If you try to open a page, you will get an `ActiveEntry::AuthenticationNotPerformedError` or `ActiveEntry::AuthorizationNotPerformedError`. This means that you have to instruct Active Entry when a user is authenticated/authorized and when not.
|
36
|
+
You can do this by defining the methods `authenticated?` and `authorized?` in your controller.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
class DashboardController < ApplicationController
|
40
|
+
# Actions ...
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def authenticated?
|
45
|
+
return true if user_signed_in?
|
46
|
+
end
|
47
|
+
|
48
|
+
def authorized?
|
49
|
+
return true if current_user.admin?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Active Entry expects boolean return values from `authenticated?` and `authorized?`. `true` signals successful authentication/authorization, everything else not.
|
55
|
+
|
56
|
+
If the user is signed in, he is authenticated and authorized if he is an admin, otherwise an `ActiveEntry::NotAuthenticatedError` or `ActiveEntry::NotAuthorizedError` will be raised.
|
57
|
+
Now you just have to catch this error and react accordingly. Rails has the convinient `rescue_from` for that.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class ApplicationController < ActionController::Base
|
61
|
+
# ...
|
62
|
+
|
63
|
+
rescue_from ActiveEntry::NotAuthenticatedError, with: :not_authenticated
|
64
|
+
rescue_from ActiveEntry::NotAuthorizedError, with: :not_authorized
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def not_authenticated
|
69
|
+
flash[:danger] = "You are not authenticated!"
|
70
|
+
redirect_to login_path
|
71
|
+
end
|
72
|
+
|
73
|
+
def not_authorized
|
74
|
+
flash[:danger] = "You are not authorized to call this action!"
|
75
|
+
redirect_to root_path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
In this example above, the user will be redirected with a flash message. But you can do whatever you want. For example logging.
|
81
|
+
|
82
|
+
Active Entry also has a few helper methods which help you to distinguish between RESTful controller actions.
|
83
|
+
|
84
|
+
The following methods are available:
|
85
|
+
|
86
|
+
* `read_action?` - If the called action just read. Actions: `index`, `show`
|
87
|
+
* `write_action?` - If the called action writes something. Actions: `new`, `create`, `edit`, `update`, `destroy`
|
88
|
+
* `change_action?` - If something will be updated or destroyed. Actions: `edit`, `update`, `destroy`
|
89
|
+
* `create_action?` - If something will be created. Actions: `new`, `create`
|
90
|
+
* `update_action?` - If something will be updated. Actions: `edit`, `update`
|
91
|
+
* `destroy_action?` - If something will be destroyed. Action: `destroy`
|
92
|
+
|
93
|
+
So you can for example do:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
def authorized?
|
97
|
+
return true if read_action? # Everybody is authorized to call read actions
|
98
|
+
|
99
|
+
if write_action?
|
100
|
+
return true if admin_signed_in? # Just admins are allowed to call write actions
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
This is pretty much everything you have to do for basic authentication or authorization!
|
106
|
+
|
107
|
+
## Pass a custom error hash
|
108
|
+
You can pass an error hash to the exception and use this in your rescue method:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
class ApplicationController < ActionController::Base
|
112
|
+
before_action :authenticate!, :authorize!
|
113
|
+
|
114
|
+
# ...
|
115
|
+
|
116
|
+
rescue_from ActiveEntry::NotAuthenticatedError, with: :not_authenticated
|
117
|
+
rescue_from ActiveEntry::NotAuthorizedError, with: :not_authorized
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def not_authenticated(exception)
|
122
|
+
flash[:danger] = "You are not authenticated! Code: #{exception.error[:code]}"
|
123
|
+
redirect_to root_path
|
124
|
+
end
|
125
|
+
|
126
|
+
def not_authorized(exception)
|
127
|
+
flash[:danger] = "You are not authorized to call this action! Code: #{exception.error[:code]}"
|
128
|
+
redirect_to root_path
|
129
|
+
end
|
130
|
+
|
131
|
+
def authenticated?(error)
|
132
|
+
error[:code] = "ERROR"
|
133
|
+
|
134
|
+
return true if user_signed_in?
|
135
|
+
end
|
136
|
+
|
137
|
+
def authorized?(error)
|
138
|
+
error[:code] = "ERROR"
|
139
|
+
|
140
|
+
return true if read_action? # Everybody is authorized to call read actions
|
141
|
+
|
142
|
+
if write_action?
|
143
|
+
return true if admin_signed_in? # Just admins are allowed to call write actions
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
## Known Issues
|
150
|
+
The authentication/authorization is done in a before action. These Rails controller before callbacks are done in defined order. If you set an instance variable which is needed in the `authenticated?` or `authorized?` method, you have to call the before action after the other method again.
|
151
|
+
|
152
|
+
For example if you set `@user` in your controller in the `set_user` before action and you want to use the variable in `authorized?` action, you have to add the `authenticate!` or `authorize!` method after the `set_user` again, otherwise `@user` won't be available in `authenticate!` or `authorized?` yet.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class UsersController < ApplicationController
|
156
|
+
before_action :set_user
|
157
|
+
before_action :authenticate!, :authorize!
|
158
|
+
|
159
|
+
def show
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def authenticated?
|
165
|
+
return true if user_signed_in?
|
166
|
+
end
|
167
|
+
|
168
|
+
def authorized?
|
169
|
+
return true if current_user == @user
|
170
|
+
end
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
## Contributing
|
175
|
+
Create pull requests on Github and help us to improve this Gem. There are some guidelines to follow:
|
176
|
+
|
177
|
+
* Follow the conventions
|
178
|
+
* Test all your implementations
|
179
|
+
* Document methods that aren't self-explaining (we are using [YARD](http://yardoc.org/))
|
180
|
+
|
181
|
+
## License
|
182
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/active_entry.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "active_entry/version"
|
2
|
+
require "active_entry/errors"
|
3
|
+
require "active_entry/controller_methods"
|
4
|
+
require "active_entry/railtie" if defined? Rails::Railtie
|
5
|
+
|
6
|
+
module ActiveEntry
|
7
|
+
# Authenticates the user
|
8
|
+
def authenticate!
|
9
|
+
# Raise an error if the #authenticate? action isn't defined.
|
10
|
+
#
|
11
|
+
# This ensures that you actually do authentication in your controller.
|
12
|
+
raise ActiveEntry::AuthenticationNotPerformedError unless defined?(authenticated?)
|
13
|
+
|
14
|
+
error = {}
|
15
|
+
is_authenticated = nil
|
16
|
+
|
17
|
+
if method(:authenticated?).arity > 0
|
18
|
+
is_authenticated = authenticated?(error)
|
19
|
+
else
|
20
|
+
is_authenticated = authenticated?
|
21
|
+
end
|
22
|
+
|
23
|
+
# If the authenticated? method returns not true
|
24
|
+
# it raises the ActiveEntry::NotAuthenticatedError.
|
25
|
+
#
|
26
|
+
# Use the .rescue_from method from ActionController::Base
|
27
|
+
# to catch the exception and show the user a proper error message.
|
28
|
+
raise ActiveEntry::NotAuthenticatedError.new(error) unless is_authenticated == true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Authorizes the user.
|
32
|
+
def authorize!
|
33
|
+
# Raise an error if the #authorize? action isn't defined.
|
34
|
+
#
|
35
|
+
# This ensures that you actually do authorization in your controller.
|
36
|
+
raise ActiveEntry::AuthorizationNotPerformedError unless defined?(authorized?)
|
37
|
+
|
38
|
+
error = {}
|
39
|
+
is_authorized = nil
|
40
|
+
|
41
|
+
if method(:authorized?).arity > 0
|
42
|
+
is_authorized = authorized?(error)
|
43
|
+
else
|
44
|
+
is_authorized = authorized?
|
45
|
+
end
|
46
|
+
|
47
|
+
# If the authorized? method does not return true
|
48
|
+
# it raises the ActiveEntry::NotAuthorizedError
|
49
|
+
#
|
50
|
+
# Use the .rescue_from method from ActionController::Base
|
51
|
+
# to catch the exception and show the user a proper error message.
|
52
|
+
raise ActiveEntry::NotAuthorizedError.new(error) unless is_authorized == true
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# @author Tobias Feistmantl
|
2
|
+
#
|
3
|
+
# Helper methods for your controller
|
4
|
+
# to identify RESTful actions.
|
5
|
+
module ActiveEntry
|
6
|
+
# @return [Boolean]
|
7
|
+
# True if the called action
|
8
|
+
# is a only-read action.
|
9
|
+
def read_action?
|
10
|
+
action_name == 'index' ||
|
11
|
+
action_name == 'show'
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Boolean]
|
15
|
+
# True if the called action
|
16
|
+
# is a write action.
|
17
|
+
def write_action?
|
18
|
+
action_name == 'new' ||
|
19
|
+
action_name == 'create' ||
|
20
|
+
action_name == 'edit' ||
|
21
|
+
action_name == 'update' ||
|
22
|
+
action_name == 'destroy'
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean]
|
26
|
+
# True if the called action
|
27
|
+
# is a change action.
|
28
|
+
def change_action?
|
29
|
+
action_name == 'edit' ||
|
30
|
+
action_name == 'update' ||
|
31
|
+
action_name == 'destroy'
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
# True if the called action
|
36
|
+
# is the index action.
|
37
|
+
def index_action?
|
38
|
+
action_name == 'index'
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Boolean]
|
42
|
+
# True if the called action
|
43
|
+
# is the show action.
|
44
|
+
def show_action?
|
45
|
+
action_name == 'show'
|
46
|
+
end
|
47
|
+
|
48
|
+
# @note
|
49
|
+
# Also true for the pseudo
|
50
|
+
# update action `new`.
|
51
|
+
#
|
52
|
+
# @note
|
53
|
+
# Only true for create methods
|
54
|
+
# such as new and create.
|
55
|
+
#
|
56
|
+
# @return [Boolean]
|
57
|
+
# True if the called action
|
58
|
+
# is a create action.
|
59
|
+
def create_action?
|
60
|
+
action_name == 'new' ||
|
61
|
+
action_name == 'create'
|
62
|
+
end
|
63
|
+
|
64
|
+
# @note
|
65
|
+
# Also true for the pseudo
|
66
|
+
# update action `edit`.
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
# True if the called action
|
70
|
+
# is a update action.
|
71
|
+
def update_action?
|
72
|
+
action_name == 'edit' ||
|
73
|
+
action_name == 'update'
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Boolean]
|
77
|
+
# True if it's a destroy action.
|
78
|
+
def destroy_action?
|
79
|
+
action_name == 'destroy'
|
80
|
+
end
|
81
|
+
|
82
|
+
alias delete_action? destroy_action?
|
83
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# @author Tobias Feistmantl
|
2
|
+
module ActiveEntry
|
3
|
+
# Generic authorization error.
|
4
|
+
# Other, more specific, errors inherit from this one.
|
5
|
+
#
|
6
|
+
# @raise [AuthorizationError]
|
7
|
+
# if something generic is happening.
|
8
|
+
class AuthorizationError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
# Error for controllers in which authorization isn't handled.
|
12
|
+
#
|
13
|
+
# @raise [AuthorizationNotPerformedError]
|
14
|
+
# if the #authorized? method isn't defined
|
15
|
+
# in the controller class.
|
16
|
+
class AuthorizationNotPerformedError < AuthorizationError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Error if user unauthorized.
|
20
|
+
#
|
21
|
+
# @raise [NotAuthorizedError]
|
22
|
+
# if authorized? isn't returning true.
|
23
|
+
#
|
24
|
+
# @note
|
25
|
+
# Should always be called at the end
|
26
|
+
# of the #authorize! method.
|
27
|
+
class NotAuthorizedError < AuthorizationError
|
28
|
+
attr_reader :error
|
29
|
+
|
30
|
+
def initialize(error={})
|
31
|
+
@error = error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Base class for authentication errors.
|
37
|
+
#
|
38
|
+
# @raise [AuthenticationError]
|
39
|
+
# if something generic happens.
|
40
|
+
class AuthenticationError < StandardError
|
41
|
+
end
|
42
|
+
|
43
|
+
# Error for controllers in which authentication isn't handled.
|
44
|
+
#
|
45
|
+
# @raise [AuthenticationNotPerformedError]
|
46
|
+
# if the #authenticated? method isn't defined
|
47
|
+
# in the controller class.
|
48
|
+
class AuthenticationNotPerformedError < AuthenticationError
|
49
|
+
end
|
50
|
+
|
51
|
+
# Error if user not authenticated
|
52
|
+
#
|
53
|
+
# @raise [NotAuthenticatedError]
|
54
|
+
# if authenticated? isn't returning true.
|
55
|
+
#
|
56
|
+
# @note
|
57
|
+
# Should always be called at the end
|
58
|
+
# of the #authenticate! method.
|
59
|
+
class NotAuthenticatedError < AuthenticationError
|
60
|
+
attr_reader :error
|
61
|
+
|
62
|
+
def initialize(error={})
|
63
|
+
@error = error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_entry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- TFM Agency GmbH
|
8
|
+
- Tobias Feistmantl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2021-03-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 4.0.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 4.0.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec-rails
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: guard-rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: An easy and flexible access control system. No need for policies, abilities,
|
71
|
+
etc. Do authentication and authorization directly in your controller.
|
72
|
+
email:
|
73
|
+
- hello@tfm.agency
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- MIT-LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/active_entry.rb
|
82
|
+
- lib/active_entry/controller_methods.rb
|
83
|
+
- lib/active_entry/errors.rb
|
84
|
+
- lib/active_entry/railtie.rb
|
85
|
+
- lib/active_entry/version.rb
|
86
|
+
- lib/tasks/active_entry_tasks.rake
|
87
|
+
homepage: https://github.com/TFM-Agency/active_entry
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata:
|
91
|
+
homepage_uri: https://github.com/TFM-Agency/active_entry
|
92
|
+
source_code_uri: https://github.com/TFM-Agency/active_entry
|
93
|
+
changelog_uri: https://github.com/TFM-Agency/active_entry/commits/main
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubygems_version: 3.2.3
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: An easy and flexible access control system for your Rails app.
|
113
|
+
test_files: []
|