passpartu 1.0.0 → 1.0.3

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: 982e32959b5022a455e42d2c1de190c0851f358cfd34c699ece8394e6af0d951
4
- data.tar.gz: 2e81288b0af25837d0d4d97b14562e33f2813ca0b8741adbce66451568a99487
3
+ metadata.gz: 7ff6ec8f5f35ec4a606df7c651868203f755d0d6f5b6152d23a152fa91d45126
4
+ data.tar.gz: 4ed57d9ced50f0c8ab11725835be3cc2ecb37a9309433146429312d22417dd13
5
5
  SHA512:
6
- metadata.gz: 3cebce1276892e0fd925be52bc3ed817b83b6aeb96d9b056b9d999d288b9cbe891189f36a8d6b904b1dac53bbd90eb10b2d6628e42f727d063bc300abbeb697d
7
- data.tar.gz: 6caeffe92ffe0f8fe158c0d10c9d4eca18e88ac3acad3c14d7346673ca6740c7335564d53f10a9263f6ae2bcc533077fb18d84cfb25c68cac71717e7b4c01ff7
6
+ metadata.gz: c55101f249104c06a591cffa4df5126e3bf01a0f81a1bcd96d766b91d657f7997cfaebc81ae22e4da56265951ec47b751565c97a220ec17042945b9ba3e0fbb7
7
+ data.tar.gz: 5618f1ecf4e1b8ad3b27f802cefd2213e65ee08125e840aa32aab603b1ca85868fc4d79464bcacf4d9be75103174cff9474c05ef8d6d3058d0fd56381cc86f69
data/README.md CHANGED
@@ -1,8 +1,14 @@
1
- # Passpartu v1.0.0 - [changelog](https://github.com/coaxsoft/passpartu/blob/master/CHANGELOG.md)
1
+ # Passpartu v1.0.3 - [changelog](https://github.com/coaxsoft/passpartu/blob/master/CHANGELOG.md)
2
2
 
3
3
  Passpartu makes policies great again (works awesome with [Pundit](https://rubygems.org/gems/pundit)).
4
4
 
5
+ ### Tested with ruby:
6
+ - 2.7.3
7
+ - 3.0.0
8
+ - 3.1.1
9
+
5
10
  Instead of this:
11
+
6
12
  ```ruby
7
13
  class PostPolicy < ApplicationPolicy
8
14
  def update?
@@ -12,6 +18,7 @@ end
12
18
  ```
13
19
 
14
20
  just this:
21
+
15
22
  ```ruby
16
23
  class PostPolicy < ApplicationPolicy
17
24
  def update?
@@ -19,19 +26,24 @@ class PostPolicy < ApplicationPolicy
19
26
  end
20
27
  end
21
28
  ```
29
+
22
30
  ## Usage
31
+
23
32
  Include `Passpartu` into your policy model.
33
+
24
34
  ```ruby
25
35
  class User
26
36
  include Passpartu
27
37
  end
28
38
  ```
39
+
29
40
  NOTE: Your `User` model must respond to `role` method that returns a string or a symbol!
30
41
 
31
42
  Keep all your policies in one place.
32
43
  Create `./config/passpartu.yml` and start writing your policies.
33
44
 
34
45
  #### Example of `passpartu.yml`
46
+
35
47
  ```yml
36
48
  # ./config/passpartu.yml
37
49
  manager: &manager
@@ -65,18 +77,25 @@ admin:
65
77
  ```
66
78
 
67
79
  ## Features
80
+
68
81
  #### CRUD
82
+
69
83
  It's possible to use `crud` key to set values for `create`, `read`, `update`, `delete` at once.
70
84
  `create`, `read`, `update`, `delete` has higher priority than `crud`
85
+
71
86
  In case `crud: true` and `delete: false` - result `false`
72
87
 
88
+
73
89
  #### Only
90
+
74
91
  It's possible to include specific roles to checks
92
+
75
93
  ```ruby
76
94
  user_admin.can?(:orders, :edit) # check policy for admin and returns true if policy true
77
95
  user_admin.can?(:orders, :edit, only: :admin) # returns true because the user is an admin and we included only admin
78
96
  user_manager.can?(:orders, :edit, only: :admin) # returns false because user is manager and we included only admin
79
97
  ```
98
+
80
99
  It's possible to give an array as only attribute
81
100
 
82
101
  ```ruby
@@ -85,18 +104,21 @@ It's possible to give an array as only attribute
85
104
  ```
86
105
 
87
106
  Note: `only` has higher priority than `except/skip`. Do not use both.
107
+
88
108
  ```ruby
89
109
  user_admin.can?(:orders, :edit, only: :admin, except: :admin) # returns true
90
110
  ```
91
111
 
92
-
93
112
  #### Skip (except)
113
+
94
114
  It's possible to exclude roles from checks
115
+
95
116
  ```ruby
96
117
  user_admin.can?(:orders, :edit) # check policy for admin and returns true if policy true
97
118
  user_admin.can?(:orders, :edit, except: :admin) # returns false because user is admin and we excluded admin
98
119
 
99
120
  ```
121
+
100
122
  It's possible to give an array as except attribute
101
123
 
102
124
  ```ruby
@@ -107,6 +129,7 @@ It's possible to give an array as except attribute
107
129
  `skip` alias to `except`
108
130
 
109
131
  Note: `expect` has higher priority than `skip`. Do not use both.
132
+
110
133
  ```ruby
111
134
  user_agent.can?(:orders, :edit, except: [:admin, :manager]) { user_agent.orders.include?(order) }
112
135
  # equals to
@@ -114,7 +137,9 @@ Note: `expect` has higher priority than `skip`. Do not use both.
114
137
  ```
115
138
 
116
139
  #### Per role methods
140
+
117
141
  Check user roles AND policy rule
142
+
118
143
  ```ruby
119
144
  # check if user admin AND returns true if policy true
120
145
  user_admin.admin_can?(:orders, :edit) # true
@@ -124,6 +149,7 @@ Check user roles AND policy rule
124
149
  ```
125
150
 
126
151
  #### Code blocks
152
+
127
153
  ```ruby
128
154
  # check rules as usual AND code in the block
129
155
  user_agent.can?(:orders, :edit, except: [:admin, :manager]) { user_agent.orders.include?(order) }
@@ -133,7 +159,9 @@ Check user roles AND policy rule
133
159
  ```
134
160
 
135
161
  #### Waterfall check
162
+
136
163
  Allow or restrict absolutely everything for particular role or/and particular domain.
164
+
137
165
  ```ruby
138
166
  # ./config/initializers/passpartu.rb
139
167
 
@@ -153,6 +181,7 @@ medium_looser:
153
181
  delete: false
154
182
  products: true
155
183
  ```
184
+
156
185
  ```ruby
157
186
  user_super_admin.can?(:do, :whatever, :want) # true
158
187
  user_super_loser.can?(:do, :whatever, :want) # false
@@ -161,10 +190,10 @@ user_medium_loser.can?(:orders, :delete) # false
161
190
  user_medium_loser.can?(:products, :create) # true
162
191
  user_medium_loser.can?(:products, :create, :and_delete) # true
163
192
  ```
164
-
165
-
166
193
  ##### Real life example
194
+
167
195
  You need to check custom rule for agent
196
+
168
197
  ```yml
169
198
  # ./config/passpartu.yml
170
199
 
@@ -202,9 +231,13 @@ You can configure Passpartu by creating `./config/initializers/passpartu.rb`.
202
231
  Passpartu.configure do |config|
203
232
  config.policy_file = './config/passpartu.yml'
204
233
  config.raise_policy_missed_error = true
234
+ config.check_waterfall = false
205
235
  end
236
+
206
237
  ```
238
+
207
239
  ### Raise policy missed errors
240
+
208
241
  By default Passpartu will raise an PolicyMissedError if policy is missed in `passpartu.yml`. In initializer set `config.raise_policy_missed_error = false` in order to return `false` in case when policy is not defined. This is a good approach to write only "positive" policies (only true) and automatically restricts everything that is not mentioned in `passpartu.yml`
209
242
 
210
243
  ## Installation
@@ -244,4 +277,5 @@ The gem is available as open source under the terms of the [MIT License](https:/
244
277
  Everyone interacting in the Passpartu project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/coaxsoft/passpartu/blob/master/CODE_OF_CONDUCT.md).
245
278
 
246
279
  ## Idea
280
+
247
281
  Initially designed and created by [Orest Falchuk](https://github.com/OrestF)
@@ -3,6 +3,7 @@
3
3
  module Passpartu
4
4
  class Patcher
5
5
  attr_reader :klass
6
+
6
7
  def initialize(klass)
7
8
  raise PolicyYmlNotFoundError if Passpartu.policy.nil?
8
9
 
@@ -15,11 +16,11 @@ module Passpartu
15
16
 
16
17
  def call
17
18
  klass.class_eval do
18
- define_method('can?') do |*keys, only: nil, except: nil, skip: nil, &block|
19
+ define_method(:can?) do |*keys, only: nil, except: nil, skip: nil, &block|
19
20
  Passpartu::BlockVerify.call(role, keys, only: only, except: except, skip: skip, &block)
20
21
  end
21
22
 
22
- Passpartu.policy.keys.each do |policy_role|
23
+ Passpartu.policy.each_key do |policy_role|
23
24
  define_method("#{policy_role}_can?") do |*keys, only: nil, except: nil, skip: nil, &block|
24
25
  role.to_s == policy_role && Passpartu::BlockVerify.call(role, keys,
25
26
  only: only,
@@ -5,6 +5,7 @@ module Passpartu
5
5
  class PolicyMissedError < StandardError; end
6
6
 
7
7
  attr_reader :result
8
+
8
9
  def initialize(result)
9
10
  @result = result
10
11
  end
@@ -23,7 +24,7 @@ module Passpartu
23
24
  private
24
25
 
25
26
  def boolean?
26
- [TrueClass, FalseClass].include?(result.class)
27
+ [true, false].include?(result)
27
28
  end
28
29
 
29
30
  def raise_error?
@@ -25,14 +25,14 @@ module Passpartu
25
25
  def call
26
26
  return false if role_ignore?
27
27
 
28
- check_waterfall_if
29
28
  default_check
30
29
  check_crud_if
31
30
 
32
31
  validate_result
33
32
  rescue StandardError => e
34
33
  if ['TrueClass does not have #dig method', 'FalseClass does not have #dig method'].include?(e.message)
35
- raise WaterfallError.new "Looks like you want to use check_waterfall feature, but it's set to 'false'. Otherwise check your #{Passpartu.config.policy_file} for validness"
34
+ raise WaterfallError,
35
+ "Looks like you want to use check_waterfall feature, but it's set to 'false'. Otherwise check your #{Passpartu.config.policy_file} for validness"
36
36
  else
37
37
  raise e
38
38
  end
@@ -72,12 +72,6 @@ module Passpartu
72
72
  %w[create read update delete].include?(keys[-1])
73
73
  end
74
74
 
75
- def check_waterfall_if
76
- return unless Passpartu.config.check_waterfall && policy_missed?
77
-
78
- @result = Passpartu::CheckWaterfall.call(role, keys)
79
- end
80
-
81
75
  def blank?(item)
82
76
  item.respond_to?(:empty?) ? !!item.empty? : !item
83
77
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Passpartu
4
- VERSION = '1.0.0'
4
+ VERSION = '1.0.3'
5
5
  end
data/lib/passpartu.rb CHANGED
@@ -6,7 +6,6 @@ require_relative 'passpartu/patcher'
6
6
  require_relative 'passpartu/verify'
7
7
  require_relative 'passpartu/block_verify'
8
8
  require_relative 'passpartu/validate_result'
9
- require_relative 'passpartu/check_waterfall'
10
9
  require_relative 'passpartu/user' # for testing only
11
10
 
12
11
  module Passpartu
@@ -14,32 +13,32 @@ module Passpartu
14
13
  class PolicyYmlNotFoundError < StandardError; end
15
14
  class WaterfallError < StandardError; end
16
15
 
17
- def self.included(policy_class)
18
- Passpartu::Patcher.call(policy_class)
19
- end
20
-
21
- def self.policy
22
- config.policy
23
- end
24
-
25
16
  class << self
26
17
  attr_accessor :config
27
- end
28
18
 
29
- def self.configure
30
- self.config ||= Config.new
31
- yield(config)
19
+ def included(policy_class)
20
+ Passpartu::Patcher.call(policy_class)
21
+ end
22
+
23
+ def policy
24
+ config.policy
25
+ end
26
+
27
+ def configure
28
+ self.config ||= Config.new
29
+ yield(config)
30
+ end
32
31
  end
33
32
 
34
33
  class Config
35
- attr_accessor :policy, :raise_policy_missed_error, :check_waterfall
36
- attr_reader :policy_file
34
+ attr_accessor :raise_policy_missed_error
35
+ attr_reader :policy_file, :check_waterfall, :policy
37
36
 
38
37
  DEFAULT_POLICY_FILE = './config/passpartu.yml'
39
38
 
40
39
  def initialize
41
40
  @policy_file = DEFAULT_POLICY_FILE
42
- @policy = YAML.load_file(policy_file) if File.exist?(policy_file)
41
+ self.policy = load_policy_file(policy_file) if File.exist?(policy_file)
43
42
  @raise_policy_missed_error = true
44
43
  @check_waterfall = false
45
44
  end
@@ -49,11 +48,56 @@ module Passpartu
49
48
 
50
49
  raise PolicyYmlNotFoundError unless File.exist?(policy_file)
51
50
 
52
- @policy = YAML.load_file(policy_file)
51
+ self.policy = load_policy_file(policy_file)
52
+ end
53
+
54
+ def check_waterfall=(value)
55
+ @check_waterfall = value
56
+
57
+ @check_waterfall.tap do |check_waterfall|
58
+ if check_waterfall
59
+ @raise_policy_missed_error = false
60
+ self.policy = @policy
61
+ end
62
+ end
53
63
  end
54
- end
55
64
 
56
- configure {}
65
+ private
66
+
67
+ def load_policy_file(path)
68
+ RUBY_VERSION.to_f >= 3.1 ? YAML.load_file(path, aliases: true) : YAML.load_file(path)
69
+ end
70
+
71
+ def policy=(value)
72
+ @policy = patch_policy_booleans_if(value)
73
+ end
74
+
75
+ # patch all booleans in hash to support check_waterfall
76
+ def patch_policy_booleans_if(hash)
77
+ return hash unless @check_waterfall
78
+
79
+ hash.transform_values! do |value|
80
+ case value
81
+ when true
82
+ value.define_singleton_method(:dig) { |*_keys| true }
83
+ when false
84
+ value.define_singleton_method(:dig) { |*_keys| false }
85
+ else
86
+ patch_policy_booleans_if(value)
87
+ end
88
+
89
+ value
90
+ end
91
+ end
92
+
93
+ def blank?(item)
94
+ item.respond_to?(:empty?) ? !!item.empty? : !item
95
+ end
96
+
97
+ def present?(item)
98
+ !blank?(item)
99
+ end
100
+ end
57
101
  end
58
102
 
59
103
  initializer = './config/initializers/passpartu.rb'
data/passpartu.gemspec CHANGED
@@ -38,7 +38,10 @@ Gem::Specification.new do |spec|
38
38
  spec.require_paths = ['lib']
39
39
  spec.files = Dir['README.md', 'lib/**/*', 'lib/*', 'passpartu.gemspec']
40
40
 
41
- spec.add_development_dependency 'bundler', '~> 2.0'
42
- spec.add_development_dependency 'rake', '~> 10.0'
43
- spec.add_development_dependency 'rspec', '~> 3.0'
41
+ spec.add_development_dependency 'bundler', '~> 2.3'
42
+ spec.add_development_dependency 'rake', '~> 13.0'
43
+ spec.add_development_dependency 'rspec', '~> 3.11'
44
+ spec.add_development_dependency 'codecov', '~> 0.6'
45
+ spec.add_development_dependency 'dotenv', '~> 2.7'
46
+ spec.add_development_dependency 'simplecov', '~> 0.21'
44
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passpartu
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - OrestF
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-08 00:00:00.000000000 Z
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,42 +16,84 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '2.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '2.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '3.11'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '3.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: codecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: dotenv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.21'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.21'
55
97
  description: |-
56
98
  Passpartu is a great tool to manage your policies.
57
99
  Keep all your policy rules in one file - passpartu.yml.
@@ -64,7 +106,6 @@ files:
64
106
  - README.md
65
107
  - lib/passpartu.rb
66
108
  - lib/passpartu/block_verify.rb
67
- - lib/passpartu/check_waterfall.rb
68
109
  - lib/passpartu/patcher.rb
69
110
  - lib/passpartu/user.rb
70
111
  - lib/passpartu/validate_result.rb
@@ -75,7 +116,7 @@ homepage: https://github.com/coaxsoft/passpartu
75
116
  licenses:
76
117
  - MIT
77
118
  metadata: {}
78
- post_install_message:
119
+ post_install_message:
79
120
  rdoc_options: []
80
121
  require_paths:
81
122
  - lib
@@ -90,9 +131,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
131
  - !ruby/object:Gem::Version
91
132
  version: '0'
92
133
  requirements: []
93
- rubyforge_project:
94
- rubygems_version: 2.7.7
95
- signing_key:
134
+ rubygems_version: 3.1.6
135
+ signing_key:
96
136
  specification_version: 4
97
137
  summary: Passpartu makes policies great again
98
138
  test_files: []
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Passpartu
4
- class CheckWaterfall
5
- attr_reader :waterfall, :policy_hash
6
- def initialize(role, keys)
7
- @waterfall = [role] + keys
8
- @policy_hash = Passpartu.policy
9
- end
10
-
11
- def self.call(role, keys)
12
- new(role, keys).call
13
- end
14
-
15
- def call
16
- patch_boolean_classes
17
- @result = policy_hash.dig(*waterfall)
18
- reset_boolean_classes
19
-
20
- @result
21
- end
22
-
23
- def patch_boolean_classes
24
- TrueClass.define_method(:dig) { |*_keys| true }
25
- FalseClass.define_method(:dig) { |*_keys| false }
26
- end
27
-
28
- def reset_boolean_classes
29
- TrueClass.undef_method(:dig)
30
- FalseClass.undef_method(:dig)
31
- end
32
- end
33
- end