controller_policies 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db99c9c096c0c752d9d035f928f7c92e9f6efff716975d2e4672b3c18443b16a
4
- data.tar.gz: 01a131a24ad0f546ada81e5d10c1d27564591147c09004a97e6691150d18be71
3
+ metadata.gz: 77ea62355c4991dfc6d3aa1da19d75bbb231e170c6451ed11cc43cb38d53f158
4
+ data.tar.gz: be3d3097f5385f681016c8fd7ee415fc3a6fbdf21acc1320ce6d8891c4f723d7
5
5
  SHA512:
6
- metadata.gz: 05ebbc1bad897b0b6fecf8280f625a5f4d73013255c0dee1e7ab5a6e564fc9aedd21772b9b0bd27d159cf698e09377b3b829dcf5ee60584c95a250841536a336
7
- data.tar.gz: 5e23aa2064c53ea42acea98b1bffaa89767558e4fe2a7a7938b2331b258f0dc0c661f1b8542d866cacf44ec453a222e34019b0c8b7e1b77e1091be89281b84bd
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.1'
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 Base
33
- DEFINITIONS = {
34
- code: 'policy_code',
35
- name: 'Policy Name',
36
- description: 'I am a policy.',
37
- actions: ['feature_app/users', 'data_app/products#index', 'subscriptions']
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
- #### #where(query)
118
+ ```ruby
119
+ Ability.all_codes
120
+ ```
99
121
 
100
- Filter abilities based on namespace. `query` can be a String, Module or Class.
122
+ #### #where(*queries)
101
123
 
102
- #### #find(query)
124
+ Filter abilities based on namespace. `queries` can be an array of Strings, Modules or Classes.
103
125
 
104
- Find an ability within a namespace. `query` can be a String, Module or Class.
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
- #### #mill(expression)
111
-
112
- Find an ability based on a matching string or regex. The matcher is based on the namespace. `expression` can be a Regexp or String.
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(query)
37
- case query.class.to_s
38
- when 'String'
39
- all.select { |ability| ability.namespace.to_s == trim(query).camelize }
40
- when 'Module', 'Class'
41
- all.select { |ability| ability.namespace == query }
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
- where(query_string).first
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
- match(expression).first
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.constantize
104
+ trim(file_path[policy_path.to_s.length..-4].split('/')[0...-1].join('/')).camelize
101
105
  end
102
106
  end
103
107
  end
@@ -10,5 +10,9 @@ module ControllerPolicies
10
10
 
11
11
  define_method(:ability?, &block)
12
12
  end
13
+
14
+ def no_enforced_policies(arguments = {})
15
+ skip_before_action :check_abilities_by_definition, arguments
16
+ end
13
17
  end
14
18
  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(Rails.root.join('app/policies'))
22
+ autoloader.ignore(Ability.policy_path)
23
23
  end
24
24
 
25
- Dir[Rails.root.join('app/policies/**/*.rb')].each { |definition| require definition }
25
+ Dir[Ability.policy_path.join('**/*.rb')].each { |definition| require definition }
26
26
  end
27
27
  end
28
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ControllerPolicies
4
- VERSION = '0.1.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -1,22 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module <%= name.camelize %>
4
- DEFINITIONS = [
5
- {
6
- # Used as identifier for the policy.
7
- code: 'Policy-Code',
8
- # Readable name for the policy.
9
- name: 'Readable Policy Name',
10
- # Readable description for the policy.
11
- description: 'Short description of what this policy allows',
12
- # Controller actions the policy applies to. It works as matchers based on routes.
13
- # It can be empty for manual policy checking.
14
- actions: ['feature_app/users', 'data_app/products#index', 'subscriptions']
15
- },
16
- {
17
- code: 'Another-Policy-Code',
18
- name: 'Another Policy',
19
- description: 'Long description.',
20
- }
21
- ].freeze
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: 0.1.0
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-04-12 00:00:00.000000000 Z
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.3
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.