controller_policies 0.1.0 → 1.0.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 +4 -4
- data/README.md +62 -18
- data/lib/ability.rb +18 -14
- data/lib/controller_policies/action_controller_patch.rb +4 -0
- data/lib/controller_policies/railtie.rb +2 -2
- data/lib/controller_policies/version.rb +1 -1
- data/lib/generators/templates/definitions.rb.tt +21 -19
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77ea62355c4991dfc6d3aa1da19d75bbb231e170c6451ed11cc43cb38d53f158
|
4
|
+
data.tar.gz: be3d3097f5385f681016c8fd7ee415fc3a6fbdf21acc1320ce6d8891c4f723d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 289d8b06bca9542f79799779f2fba6444434ce41b85725e0c89cea6a182f5add24c5a32dbfbd9ed52aae4607cd36c9cd830db4b780fecf050075d1c15b94bd4a
|
7
|
+
data.tar.gz: 0d06f4170f6d708f487ce08e4533b93aaaac9916e342fa7a58eaeae3a86be0fde188459ea2299819cc6cc4c553402fc8d80373a00d2c98a3136f6a86ffc3513d
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
Add this line to your application's Gemfile:
|
6
6
|
|
7
7
|
```ruby
|
8
|
-
gem 'controller_policies', '~> 0
|
8
|
+
gem 'controller_policies', '~> 1.0'
|
9
9
|
```
|
10
10
|
|
11
11
|
## Usage
|
@@ -20,7 +20,7 @@ rails g policy_definition my/namespace
|
|
20
20
|
|
21
21
|
This will generate a file: `app/policies/my/namespace/definitions.rb`
|
22
22
|
|
23
|
-
The developer should edit this file and add the policies for the app.
|
23
|
+
The developer should edit this file and add the policies for the app. **It is important to note that the location of the definitions file should reflect the namespace of the associated controllers.**
|
24
24
|
|
25
25
|
### `actions` key
|
26
26
|
|
@@ -29,13 +29,15 @@ The `actions` key is an array of strings that contain a list of supported contro
|
|
29
29
|
For example, you have this definition:
|
30
30
|
|
31
31
|
```ruby
|
32
|
-
module
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
module Policies
|
33
|
+
module Base
|
34
|
+
DEFINITIONS = {
|
35
|
+
code: 'policy_code',
|
36
|
+
name: 'Policy Name',
|
37
|
+
description: 'I am a policy.',
|
38
|
+
actions: ['feature_app/users', 'data_app/products#index', 'subscriptions']
|
39
|
+
}
|
40
|
+
end
|
39
41
|
end
|
40
42
|
```
|
41
43
|
|
@@ -61,7 +63,7 @@ Simply add the line `has_enforced_policies`, and pass a block with one argument
|
|
61
63
|
```ruby
|
62
64
|
class MyController < ApplicationController
|
63
65
|
has_enforced_policies do |ability_code|
|
64
|
-
current_user.abilities.include? ability_code
|
66
|
+
render 'unauthorized' unless current_user.abilities.include? ability_code
|
65
67
|
end
|
66
68
|
|
67
69
|
# ...
|
@@ -73,14 +75,28 @@ class MyController < ApplicationController
|
|
73
75
|
has_enforced_policies
|
74
76
|
|
75
77
|
def ability?(ability_code)
|
76
|
-
current_user.abilities.include? ability_code
|
78
|
+
render 'unauthorized' unless current_user.abilities.include? ability_code
|
77
79
|
end
|
78
80
|
# ...
|
79
81
|
end
|
80
82
|
```
|
81
83
|
|
84
|
+
It is recommended to use `render` or `redirect_to` within this block **to prevent the controllers from executing the action** when the ability did not exist in the data. The ability checking is done in a `before_action` callback, hence using `render` or `redirect_to` will stop further controller actions. This is a Rails behavior.
|
85
|
+
|
82
86
|
Since storing abilities are very flexible and there are truly infinite ways of doing it, *this gem did not support that feature.* Instead, the developer must define their own ability checking.
|
83
87
|
|
88
|
+
## Skipping Policy Enforcement in Certain Actions
|
89
|
+
|
90
|
+
There might be an event where there is a need to skip automatic policy enforcements in certain actions. As explained above, the policy enforcement is done in a `before_action` callback. To skip a policy enforcement, simply use the `skip_before_action :check_abilities_by_definition` method from Rails. The `:only` and `:except` options are also available to filter actions.
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class MyOtherController < MyController
|
94
|
+
skip_before_action :check_abilities_by_definition, only: [:new, :edit]
|
95
|
+
|
96
|
+
# ...
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
84
100
|
## Ability
|
85
101
|
|
86
102
|
The Ability class is a model for abilities that come from the definition files.
|
@@ -91,25 +107,37 @@ The Ability class is a model for abilities that come from the definition files.
|
|
91
107
|
|
92
108
|
Get all abilities from all definitions.
|
93
109
|
|
110
|
+
```ruby
|
111
|
+
Ability.all
|
112
|
+
```
|
113
|
+
|
94
114
|
#### #all_codes
|
95
115
|
|
96
116
|
Get all ability codes from all definitions.
|
97
117
|
|
98
|
-
|
118
|
+
```ruby
|
119
|
+
Ability.all_codes
|
120
|
+
```
|
99
121
|
|
100
|
-
|
122
|
+
#### #where(*queries)
|
101
123
|
|
102
|
-
|
124
|
+
Filter abilities based on namespace. `queries` can be an array of Strings, Modules or Classes.
|
103
125
|
|
104
|
-
|
126
|
+
```ruby
|
127
|
+
Ability.where(Policies::FeatureOne, Policies::FeatureTwo, Policies::FeatureOne::SubFeatureA)
|
128
|
+
```
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
Ability.where('/feature_one', '/feature_two', '/feature_one/sub_feature_a')
|
132
|
+
```
|
105
133
|
|
106
134
|
#### #match(expression)
|
107
135
|
|
108
136
|
Match abilities based on a matching string or regex. The matcher is based on the namespace. `expression` can be a Regexp or String.
|
109
137
|
|
110
|
-
|
111
|
-
|
112
|
-
|
138
|
+
```ruby
|
139
|
+
Ability.match(/Policies::FeatureOne(::)?(.)*/)
|
140
|
+
```
|
113
141
|
|
114
142
|
### Instance Methods
|
115
143
|
|
@@ -117,18 +145,34 @@ Find an ability based on a matching string or regex. The matcher is based on the
|
|
117
145
|
|
118
146
|
The code of the ability.
|
119
147
|
|
148
|
+
```ruby
|
149
|
+
ability.code
|
150
|
+
```
|
151
|
+
|
120
152
|
#### #name
|
121
153
|
|
122
154
|
The name of the ability.
|
123
155
|
|
156
|
+
```ruby
|
157
|
+
ability.name
|
158
|
+
```
|
159
|
+
|
124
160
|
#### #description
|
125
161
|
|
126
162
|
The description of the ability.
|
127
163
|
|
164
|
+
```ruby
|
165
|
+
ability.description
|
166
|
+
```
|
167
|
+
|
128
168
|
#### #actions
|
129
169
|
|
130
170
|
Controller actions that the ability can check against.
|
131
171
|
|
172
|
+
```ruby
|
173
|
+
ability.actions
|
174
|
+
```
|
175
|
+
|
132
176
|
## Contributing
|
133
177
|
|
134
178
|
Bug reports and pull requests are welcome on GitHub at https://github.com/tieeeeen1994/controller_policies. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/tieeeeen1994/controller_policies/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/ability.rb
CHANGED
@@ -33,19 +33,23 @@ class Ability
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Filter abilities based on namespace.
|
36
|
-
def where(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
def where(*queries)
|
37
|
+
results = []
|
38
|
+
queries.each do |query|
|
39
|
+
case query.class.to_s
|
40
|
+
when 'String'
|
41
|
+
results += all.select { |ability| ability.namespace.to_s == "Policies::#{trim(query).camelize}" }
|
42
|
+
when 'Module', 'Class'
|
43
|
+
results += all.select { |ability| ability.namespace == query }
|
44
|
+
end
|
42
45
|
end
|
46
|
+
results
|
43
47
|
end
|
44
48
|
|
45
49
|
# Find an ability within a namespace.
|
46
|
-
def find(query_string)
|
47
|
-
|
48
|
-
end
|
50
|
+
# def find(query_string)
|
51
|
+
# where(query_string).first
|
52
|
+
# end
|
49
53
|
|
50
54
|
# Match abilities based on a matching string or regex. The matcher is based on the namespace.
|
51
55
|
def match(expression)
|
@@ -56,9 +60,9 @@ class Ability
|
|
56
60
|
end
|
57
61
|
|
58
62
|
# Find an ability based on a matching string or regex. The matcher is based on the namespace.
|
59
|
-
def mill(expression)
|
60
|
-
|
61
|
-
end
|
63
|
+
# def mill(expression)
|
64
|
+
# match(expression).first
|
65
|
+
# end
|
62
66
|
|
63
67
|
# Path to the policy folder.
|
64
68
|
def policy_path
|
@@ -78,7 +82,7 @@ class Ability
|
|
78
82
|
end
|
79
83
|
|
80
84
|
def definition_files_post_processing(file_path)
|
81
|
-
module_constant = convert_namespace(file_path)
|
85
|
+
module_constant = "Policies::#{convert_namespace(file_path)}".constantize
|
82
86
|
policy_definitions = module_constant::DEFINITIONS
|
83
87
|
policy_definitions.map do |policy_definition|
|
84
88
|
policy_definition[:namespace] = module_constant
|
@@ -97,7 +101,7 @@ class Ability
|
|
97
101
|
end
|
98
102
|
|
99
103
|
def convert_namespace(file_path)
|
100
|
-
trim(file_path[policy_path.to_s.length..-4].split('/')[0...-1].join('/')).camelize
|
104
|
+
trim(file_path[policy_path.to_s.length..-4].split('/')[0...-1].join('/')).camelize
|
101
105
|
end
|
102
106
|
end
|
103
107
|
end
|
@@ -19,10 +19,10 @@ module ControllerPolicies
|
|
19
19
|
|
20
20
|
initializer 'controller_policies.autoloaders' do
|
21
21
|
Rails.autoloaders.each do |autoloader|
|
22
|
-
autoloader.ignore(
|
22
|
+
autoloader.ignore(Ability.policy_path)
|
23
23
|
end
|
24
24
|
|
25
|
-
Dir[
|
25
|
+
Dir[Ability.policy_path.join('**/*.rb')].each { |definition| require definition }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -1,22 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
3
|
+
module Policies
|
4
|
+
module <%= name.camelize %>
|
5
|
+
DEFINITIONS = [
|
6
|
+
{
|
7
|
+
# Used as identifier for the policy.
|
8
|
+
code: 'Policy-Code',
|
9
|
+
# Readable name for the policy.
|
10
|
+
name: 'Readable Policy Name',
|
11
|
+
# Readable description for the policy.
|
12
|
+
description: 'Short description of what this policy allows',
|
13
|
+
# Controller actions the policy applies to. It works as matchers based on routes.
|
14
|
+
# It can be empty for manual policy checking.
|
15
|
+
actions: ['feature_app/users', 'data_app/products#index', 'subscriptions']
|
16
|
+
},
|
17
|
+
{
|
18
|
+
code: 'Another-Policy-Code',
|
19
|
+
name: 'Another Policy',
|
20
|
+
description: 'Long description.',
|
21
|
+
}
|
22
|
+
].freeze
|
23
|
+
end
|
22
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: controller_policies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tien
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: '0'
|
67
67
|
requirements: []
|
68
|
-
rubygems_version: 3.5.
|
68
|
+
rubygems_version: 3.5.11
|
69
69
|
signing_key:
|
70
70
|
specification_version: 4
|
71
71
|
summary: Allows the developer to define policies for controllers.
|