passpartu 1.0.0 → 1.0.3

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: 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