oso-oso 0.15.1 → 0.21.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
  SHA1:
3
- metadata.gz: 32e04bc53de8b9a2a8820365219be2b0e345e765
4
- data.tar.gz: 911eea4f97fe5aba8cb0f97485e7c9c86c68772e
3
+ metadata.gz: a9fbbff1209cf44803895d162562cb6d9c8a4c37
4
+ data.tar.gz: 8196f85296430d4c85a283bfec132c05a86e1433
5
5
  SHA512:
6
- metadata.gz: 70a6be55d3f0291aa53ff067dfdeea504cd261b40f2aeb4c613887c78fb7cf8d86de91e34f169c2e1198fa0f1c49a999dda8d4d608b9e61ca3011cb72925a8cf
7
- data.tar.gz: e23bca87576a43d12e54d4206fbb50595f4bc6d8c0c06eb7a090c2fad5978c3a5f63f851d974ec5ec2469f6b9b20246826c58f29654d34ea5fe5fa8ef197d6cd
6
+ metadata.gz: 2d49aec9759922b92b874f981709a57b6c04afc1b30762302c6020a37bde144d0b56b0343482f7f18ed96f3c3d298a0e493967fbbf8939fb91cc7dc493417e54
7
+ data.tar.gz: 867123b379ef0ff554f9c433d317826e6b6e3b84a26008fe97e56f83d588737064ba71b61f4c5848969b7c6f6a0fd2cef9e8a488f238a86a152dcef7b452844e
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  vendor
10
+ active_record_test.db
10
11
 
11
12
  # rspec failure tracking
12
13
  .rspec_status
data/.rubocop.yml CHANGED
@@ -1,7 +1,10 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.4
3
3
  Exclude:
4
- - '**/*~'
5
- - 'bin/oso'
6
- - 'vendor/**/*'
4
+ - "**/*~"
5
+ - "bin/oso"
6
+ - "vendor/**/*"
7
7
  NewCops: enable
8
+ Naming/FileName:
9
+ Exclude:
10
+ - "lib/oso-oso.rb"
data/Gemfile.lock CHANGED
@@ -1,28 +1,44 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oso-oso (0.15.1)
4
+ oso-oso (0.21.0)
5
5
  ffi (~> 1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- ast (2.4.1)
11
- backport (1.1.2)
12
- benchmark (0.1.0)
10
+ activemodel (5.2.6)
11
+ activesupport (= 5.2.6)
12
+ activerecord (5.2.6)
13
+ activemodel (= 5.2.6)
14
+ activesupport (= 5.2.6)
15
+ arel (>= 9.0)
16
+ activesupport (5.2.6)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (>= 0.7, < 2)
19
+ minitest (~> 5.1)
20
+ tzinfo (~> 1.1)
21
+ arel (9.0.0)
22
+ ast (2.4.2)
23
+ backport (1.2.0)
24
+ benchmark (0.1.1)
13
25
  byebug (11.1.3)
14
26
  coderay (1.1.3)
27
+ concurrent-ruby (1.1.9)
15
28
  diff-lcs (1.4.4)
16
29
  e2mmap (0.1.0)
17
- ffi (1.15.1)
30
+ ffi (1.15.4)
31
+ i18n (1.8.10)
32
+ concurrent-ruby (~> 1.0)
18
33
  jaro_winkler (1.5.4)
19
34
  maruku (0.7.3)
20
35
  method_source (1.0.0)
21
36
  mini_portile2 (2.4.0)
37
+ minitest (5.14.4)
22
38
  nokogiri (1.10.10)
23
39
  mini_portile2 (~> 2.4.0)
24
- parallel (1.19.2)
25
- parser (2.7.1.4)
40
+ parallel (1.20.1)
41
+ parser (2.7.2.0)
26
42
  ast (~> 2.4.1)
27
43
  pry (0.13.1)
28
44
  coderay (~> 1.1)
@@ -32,23 +48,23 @@ GEM
32
48
  pry (~> 0.13.0)
33
49
  rainbow (3.0.0)
34
50
  rake (12.3.3)
35
- regexp_parser (1.7.1)
51
+ regexp_parser (2.1.1)
36
52
  reverse_markdown (2.0.0)
37
53
  nokogiri
38
- rexml (3.2.4)
39
- rspec (3.9.0)
40
- rspec-core (~> 3.9.0)
41
- rspec-expectations (~> 3.9.0)
42
- rspec-mocks (~> 3.9.0)
43
- rspec-core (3.9.2)
44
- rspec-support (~> 3.9.3)
45
- rspec-expectations (3.9.2)
54
+ rexml (3.2.5)
55
+ rspec (3.10.0)
56
+ rspec-core (~> 3.10.0)
57
+ rspec-expectations (~> 3.10.0)
58
+ rspec-mocks (~> 3.10.0)
59
+ rspec-core (3.10.1)
60
+ rspec-support (~> 3.10.0)
61
+ rspec-expectations (3.10.1)
46
62
  diff-lcs (>= 1.2.0, < 2.0)
47
- rspec-support (~> 3.9.0)
48
- rspec-mocks (3.9.1)
63
+ rspec-support (~> 3.10.0)
64
+ rspec-mocks (3.10.2)
49
65
  diff-lcs (>= 1.2.0, < 2.0)
50
- rspec-support (~> 3.9.0)
51
- rspec-support (3.9.3)
66
+ rspec-support (~> 3.10.0)
67
+ rspec-support (3.10.2)
52
68
  rubocop (0.89.1)
53
69
  parallel (~> 1.10)
54
70
  parser (>= 2.7.1.1)
@@ -58,10 +74,10 @@ GEM
58
74
  rubocop-ast (>= 0.3.0, < 1.0)
59
75
  ruby-progressbar (~> 1.7)
60
76
  unicode-display_width (>= 1.4.0, < 2.0)
61
- rubocop-ast (0.3.0)
62
- parser (>= 2.7.1.4)
63
- ruby-progressbar (1.10.1)
64
- solargraph (0.39.14)
77
+ rubocop-ast (0.8.0)
78
+ parser (>= 2.7.1.5)
79
+ ruby-progressbar (1.11.0)
80
+ solargraph (0.39.17)
65
81
  backport (~> 1.1)
66
82
  benchmark
67
83
  bundler (>= 1.17.2)
@@ -75,21 +91,28 @@ GEM
75
91
  thor (~> 1.0)
76
92
  tilt (~> 2.0)
77
93
  yard (~> 0.9, >= 0.9.24)
78
- thor (1.0.1)
94
+ sqlite3 (1.4.2)
95
+ thor (1.1.0)
96
+ thread_safe (0.3.6)
79
97
  tilt (2.0.10)
98
+ tzinfo (1.2.9)
99
+ thread_safe (~> 0.1)
80
100
  unicode-display_width (1.7.0)
81
- yard (0.9.25)
101
+ yard (0.9.26)
82
102
 
83
103
  PLATFORMS
84
104
  ruby
105
+ x86_64-darwin-20
85
106
 
86
107
  DEPENDENCIES
108
+ activerecord
87
109
  oso-oso!
88
110
  pry-byebug (~> 3.9.0)
89
111
  rake (~> 12.0)
90
112
  rspec (~> 3.0)
91
113
  rubocop (~> 0.89.1)
92
114
  solargraph (~> 0.39.14)
115
+ sqlite3
93
116
  yard (~> 0.9.25)
94
117
 
95
118
  BUNDLED WITH
data/README.md CHANGED
@@ -4,9 +4,7 @@
4
4
 
5
5
  Add this line to your application's Gemfile:
6
6
 
7
- ```ruby
8
- gem 'oso-oso'
9
- ```
7
+ gem 'oso-oso'
10
8
 
11
9
  And then execute:
12
10
 
Binary file
Binary file
Binary file
data/lib/oso/errors.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ class Error < ::RuntimeError
5
+ end
6
+
7
+ class AuthorizationError < Error
8
+ end
9
+
10
+ # Thrown by the +authorize+, +authorize_field+, and +authorize_request+
11
+ # methods when the action is not allowed.
12
+ #
13
+ # Most of the time, your app should handle this error by returning a 403 HTTP
14
+ # error to the client.
15
+ class ForbiddenError < AuthorizationError
16
+ def initialize
17
+ super(
18
+ 'Oso ForbiddenError -- The requested action was not allowed for the ' \
19
+ 'given resource. You should handle this error by returning a 403 error ' \
20
+ 'to the client.'
21
+ )
22
+ end
23
+ end
24
+
25
+ # Thrown by the +authorize+ method of an +Oso+ instance. This error indicates
26
+ # that the actor is not only not allowed to perform the given action, but also
27
+ # is not allowed to +"read"+ the given resource.
28
+ #
29
+ # Most of the time, your app should handle this error by returning a 404 HTTP
30
+ # error to the client.
31
+ #
32
+ # To control which action is used for the distinction between
33
+ # +NotFoundError+ and +ForbiddenError+, you can customize the
34
+ # +read_action+ on your +Oso+ instance.
35
+ class NotFoundError < AuthorizationError
36
+ def initialize
37
+ super(
38
+ 'Oso NotFoundError -- The current user does not have permission to read ' \
39
+ 'the given resource. You should handle this error by returning a 404 ' \
40
+ 'error to the client.'
41
+ )
42
+ end
43
+ end
44
+ end
data/lib/oso/oso.rb CHANGED
@@ -1,12 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
3
4
  require_relative 'polar/polar'
4
5
 
5
6
  module Oso
6
7
  # oso authorization API.
7
8
  class Oso < Polar::Polar
8
- def initialize
9
- super
9
+ # Create an Oso instance, which is used to configure and enforce an Oso
10
+ # policy in an app.
11
+ #
12
+ # @param forbidden_error [Class] Optionally override the "forbidden" error
13
+ # class thrown by the `authorize*` methods. Defaults to
14
+ # {Oso::ForbiddenError}.
15
+ # @param not_found_error [Class] Optionally override the "not found" error
16
+ # class thrown by {#authorize}. Defaults to {Oso::NotFoundError}.
17
+ # @param read_action The action used by the {#authorize} method to
18
+ # determine whether an authorization failure should
19
+ # raise a {Oso::NotFoundError} or a {Oso::ForbiddenError}
20
+ def initialize(not_found_error: NotFoundError, forbidden_error: ForbiddenError, read_action: 'read')
21
+ super()
22
+ @not_found_error = not_found_error
23
+ @forbidden_error = forbidden_error
24
+ @read_action = read_action
10
25
  end
11
26
 
12
27
  # Query the knowledge base to determine whether an actor is allowed to
@@ -17,7 +32,191 @@ module Oso
17
32
  # @param resource [Object] Object.
18
33
  # @return [Boolean] An access control decision.
19
34
  def allowed?(actor:, action:, resource:)
20
- !query_rule('allow', actor, action, resource).first.nil?
35
+ query_rule_once('allow', actor, action, resource)
36
+ end
37
+
38
+ # Ensure that +actor+ is allowed to perform +action+ on
39
+ # +resource+.
40
+ #
41
+ # If the action is permitted with an +allow+ rule in the policy, then
42
+ # this method returns +None+. If the action is not permitted by the
43
+ # policy, this method will raise an error.
44
+ #
45
+ # The error raised by this method depends on whether the actor can perform
46
+ # the +"read"+ action on the resource. If they cannot read the resource,
47
+ # then a {Oso::NotFoundError} error is raised. Otherwise, a
48
+ # {Oso::ForbiddenError} is raised.
49
+ #
50
+ # @param actor The actor performing the request.
51
+ # @param action The action the actor is attempting to perform.
52
+ # @param resource The resource being accessed.
53
+ # @param check_read [Boolean] If set to +false+, a {Oso::ForbiddenError} is
54
+ # always thrown on authorization failures, regardless of whether the actor
55
+ # can read the resource. Default is +true+.
56
+ #
57
+ # @raise [Oso::ForbiddenError] Raised if the actor does not have permission
58
+ # to perform this action on this resource, but _does_ have +"read"+
59
+ # permission on the resource.
60
+ # @raise [Oso::NotFoundError] Raised if the actor does not have permission
61
+ # to perform this action on this resource and additionally does not have
62
+ # permission to +"read"+ the resource.
63
+ def authorize(actor, action, resource, check_read: true)
64
+ return if query_rule_once('allow', actor, action, resource)
65
+
66
+ if check_read && (action == @read_action || !query_rule_once('allow', actor, @read_action, resource))
67
+ raise @not_found_error
68
+ end
69
+
70
+ raise @forbidden_error
71
+ end
72
+
73
+ # Ensure that +actor+ is allowed to send +request+ to the server.
74
+ #
75
+ # Checks the +allow_request+ rule of a policy.
76
+ #
77
+ # If the request is permitted with an +allow_request+ rule in the
78
+ # policy, then this method returns nothing. Otherwise, this method raises
79
+ # a {Oso::ForbiddenError}.
80
+ #
81
+ # @param actor The actor performing the request.
82
+ # @param request An object representing the request that was sent by the
83
+ # actor.
84
+ #
85
+ # @raise [Oso::ForbiddenError] Raised if the actor does not have permission
86
+ # to send the request.
87
+ def authorize_request(actor, request)
88
+ raise @forbidden_error unless query_rule_once('allow_request', actor, request)
89
+ end
90
+
91
+ # Ensure that +actor+ is allowed to perform +action+ on a given
92
+ # +resource+'s +field+.
93
+ #
94
+ # If the action is permitted by an +allow_field+ rule in the policy,
95
+ # then this method returns nothing. If the action is not permitted by the
96
+ # policy, this method will raise a {Oso::ForbiddenError}.
97
+ #
98
+ # @param actor The actor performing the request.
99
+ # @param action The action the actor is attempting to perform on the
100
+ # field.
101
+ # @param resource The resource being accessed.
102
+ # @param field The name of the field being accessed.
103
+ #
104
+ # @raise [Oso::ForbiddenError] Raised if the actor does not have permission
105
+ # to access this field.
106
+ def authorize_field(actor, action, resource, field)
107
+ raise @forbidden_error unless query_rule_once('allow_field', actor, action, resource, field)
108
+ end
109
+
110
+ # Determine the actions +actor+ is allowed to take on +resource+.
111
+ #
112
+ # Collects all actions allowed by allow rules in the Polar policy for the
113
+ # given combination of actor and resource.
114
+ #
115
+ # @param actor The actor for whom to collect allowed actions
116
+ # @param resource The resource being accessed
117
+ # @param allow_wildcard Flag to determine behavior if the policy
118
+ # includes a wildcard action. E.g., a rule allowing any action:
119
+ # +allow(_actor, _action, _resource)+. If +true+, the method will
120
+ # return +Set["*"]+, if +false+, the method will raise an exception.
121
+ # @return A set of the unique allowed actions.
122
+ def authorized_actions(actor, resource, allow_wildcard: false) # rubocop:disable Metrics/MethodLength
123
+ results = query_rule('allow', actor, Polar::Variable.new('action'), resource)
124
+ actions = Set.new
125
+ results.each do |result|
126
+ action = result['action']
127
+ if action.is_a?(Polar::Variable)
128
+ return Set['*'] if allow_wildcard
129
+
130
+ raise ::Oso::Error,
131
+ 'The result of authorized_actions() contained an '\
132
+ '"unconstrained" action that could represent any '\
133
+ 'action, but allow_wildcard was set to False. To fix, '\
134
+ 'set allow_wildcard to True and compare with the "*" '\
135
+ 'string.'
136
+ end
137
+ actions.add(action)
138
+ end
139
+ actions
140
+ end
141
+
142
+ # Determine the fields of +resource+ on which +actor+ is allowed to
143
+ # perform +action+.
144
+ #
145
+ # Uses +allow_field+ rules in the policy to find all allowed fields.
146
+ #
147
+ # @param actor The actor for whom to collect allowed fields.
148
+ # @param action The action being taken on the field.
149
+ # @param resource The resource being accessed.
150
+ # @param allow_wildcard Flag to determine behavior if the policy \
151
+ # includes a wildcard field. E.g., a rule allowing any field: \
152
+ # +allow_field(_actor, _action, _resource, _field)+. If +true+, the \
153
+ # method will return +Set["*"]+, if +false+, the method will raise an \
154
+ # exception.
155
+ # @return A set of the unique allowed fields.
156
+ def authorized_fields(actor, action, resource, allow_wildcard: false) # rubocop:disable Metrics/MethodLength
157
+ results = query_rule('allow_field', actor, action, resource, Polar::Variable.new('field'))
158
+ fields = Set.new
159
+ results.each do |result|
160
+ field = result['field']
161
+ if field.is_a?(Polar::Variable)
162
+ return Set['*'] if allow_wildcard
163
+
164
+ raise ::Oso::Error,
165
+ 'The result of authorized_fields() contained an '\
166
+ '"unconstrained" field that could represent any '\
167
+ 'field, but allow_wildcard was set to False. To fix, '\
168
+ 'set allow_wildcard to True and compare with the "*" '\
169
+ 'string.'
170
+ end
171
+ fields.add(field)
172
+ end
173
+ fields
174
+ end
175
+
176
+ # Create a query for resources of type +cls+ that +actor+ is
177
+ # allowed to perform +action+ on.
178
+ #
179
+ # @param actor The actor whose permissions to check.
180
+ # @param action The action being taken on the resource.
181
+ # @param resource_cls The resource being accessed.
182
+ #
183
+ # @return A query for resources accessible to the actor.
184
+ def authorized_query(actor, action, resource_cls) # rubocop:disable Metrics/MethodLength
185
+ resource = Polar::Variable.new 'resource'
186
+
187
+ results = query_rule(
188
+ 'allow',
189
+ actor,
190
+ action,
191
+ resource,
192
+ bindings: { 'resource' => type_constraint(resource, resource_cls) },
193
+ accept_expression: true
194
+ )
195
+
196
+ results = results.each_with_object([]) do |result, out|
197
+ result.each do |key, val|
198
+ out.push({ 'bindings' => { key => host.to_polar(val) } })
199
+ end
200
+ end
201
+
202
+ ::Oso::Polar::DataFiltering::FilterPlan
203
+ .parse(self, results, get_class_name(resource_cls))
204
+ .build_query
205
+ end
206
+
207
+ # Determine the resources of type +resource_cls+ that +actor+
208
+ # is allowed to perform +action+ on.
209
+ #
210
+ # @param actor The actor whose permissions to check.
211
+ # @param action The action being taken on the resource.
212
+ # @param resource_cls The resource being accessed.
213
+ #
214
+ # @return A list of resources accessible to the actor.
215
+ def authorized_resources(actor, action, resource_cls)
216
+ q = authorized_query actor, action, resource_cls
217
+ return [] if q.nil?
218
+
219
+ host.types[get_class_name resource_cls].exec_query[q]
21
220
  end
22
221
  end
23
222
  end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ # Data filtering interface for Ruby
6
+ module DataFiltering
7
+ # Represents a set of filter sequences that should allow the host
8
+ # to obtain the records satisfying a query.
9
+ class FilterPlan
10
+ attr_reader :result_sets
11
+
12
+ def self.parse(polar, partials, class_name)
13
+ types = polar.host.serialize_types
14
+ parsed_json = polar.ffi.build_filter_plan(types, partials, 'resource', class_name)
15
+ result_sets = parsed_json['result_sets'].map do |rset|
16
+ ResultSet.parse polar, rset
17
+ end
18
+
19
+ new polar: polar, result_sets: result_sets
20
+ end
21
+
22
+ def initialize(polar:, result_sets:)
23
+ @polar = polar
24
+ @result_sets = result_sets
25
+ end
26
+
27
+ def build_query # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
28
+ combine = nil
29
+ result_sets.each_with_object([]) do |rs, qb|
30
+ rs.resolve_order.each_with_object({}) do |i, set_results|
31
+ req = rs.requests[i]
32
+ cs = req.constraints.each { |c| c.ground set_results }
33
+ typ = @polar.host.types[req.class_tag]
34
+ q = typ.build_query[cs]
35
+ if i != rs.result_id
36
+ set_results[i] = typ.exec_query[q]
37
+ else
38
+ combine = typ.combine_query
39
+ qb.push q
40
+ end
41
+ end
42
+ end.reduce(&combine)
43
+ end
44
+
45
+ # Represents a sequence of filters for one set of results
46
+ class ResultSet
47
+ attr_reader :requests, :resolve_order, :result_id
48
+
49
+ def self.parse(polar, parsed_json)
50
+ resolve_order = parsed_json['resolve_order']
51
+ result_id = parsed_json['result_id']
52
+ requests = parsed_json['requests'].each_with_object({}) do |req, reqs|
53
+ reqs[req[0].to_i] = Request.parse(polar, req[1])
54
+ end
55
+
56
+ new resolve_order: resolve_order, result_id: result_id, requests: requests
57
+ end
58
+
59
+ def initialize(requests:, resolve_order:, result_id:)
60
+ @resolve_order = resolve_order
61
+ @requests = requests
62
+ @result_id = result_id
63
+ end
64
+ end
65
+
66
+ # Represents a filter for a result set
67
+ class Request
68
+ attr_reader :constraints, :class_tag
69
+
70
+ def self.parse(polar, parsed_json)
71
+ constraints = parsed_json['constraints'].map do |con|
72
+ Filter.parse polar, con
73
+ end
74
+ class_tag = parsed_json['class_tag']
75
+
76
+ new(constraints: constraints, class_tag: class_tag)
77
+ end
78
+
79
+ def initialize(constraints:, class_tag:)
80
+ @constraints = constraints
81
+ @class_tag = class_tag
82
+ end
83
+ end
84
+ end
85
+
86
+ # Represents relationships between resources, eg. one-one or one-many
87
+ class Relation
88
+ attr_reader :kind, :other_type, :my_field, :other_field
89
+
90
+ # Describe a Relation from one type to another.
91
+ # @param kind [String] The type of relation, either "one" or "many"
92
+ # @param other_type The name or class object of the related type
93
+ # @param my_field The field on this type that matches +other_type+
94
+ # @param other_field The field on +other_type+ that matches this type
95
+ def initialize(kind:, other_type:, my_field:, other_field:)
96
+ @kind = kind
97
+ @other_type = other_type
98
+ @my_field = my_field
99
+ @other_field = other_field
100
+ end
101
+ end
102
+
103
+ # Represents field-field relationships on one resource.
104
+ class Field
105
+ attr_reader :field
106
+
107
+ def initialize(field:)
108
+ @field = field
109
+ end
110
+ end
111
+
112
+ # Represents field-field relationships on different resources.
113
+ class Ref
114
+ attr_reader :field, :result_id
115
+
116
+ def initialize(field:, result_id:)
117
+ @field = field
118
+ @result_id = result_id
119
+ end
120
+ end
121
+
122
+ # Represents a condition that must hold on a resource.
123
+ class Filter
124
+ attr_reader :kind, :field, :value
125
+
126
+ CHECKS = {
127
+ 'Eq' => ->(a, b) { a == b },
128
+ 'In' => ->(a, b) { b.include? a },
129
+ 'Neq' => ->(a, b) { a != b },
130
+ 'Contains' => ->(a, b) { a.include? b }
131
+ }.freeze
132
+
133
+ # Create a new predicate for data filtering.
134
+ # @param kind [String] Represents a condition. One of "Eq", "Neq", "In", "Contains".
135
+ # @param field The field the condition applies to.
136
+ # @param value The value with which to compare the field according to the condition.
137
+ def initialize(kind:, field:, value:)
138
+ @kind = kind
139
+ @field = field
140
+ @value = value
141
+ @check = CHECKS[kind]
142
+ raise "Unknown constraint kind `#{kind}`" if @check.nil?
143
+ end
144
+
145
+ def ground(results)
146
+ return unless value.is_a? Ref
147
+
148
+ ref = value
149
+ @value = results[ref.result_id]
150
+ @value = value.map { |v| v.send ref.field } unless ref.field.nil?
151
+ end
152
+
153
+ def check(item)
154
+ val = value.is_a?(Field) ? item.send(value.field) : value
155
+ item = field.nil? ? item : item.send(field)
156
+ @check[item, val]
157
+ end
158
+
159
+ def self.parse(polar, constraint) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
160
+ kind = constraint['kind']
161
+ field = constraint['field']
162
+ value = constraint['value']
163
+
164
+ value_kind = value.keys.first
165
+ value = value[value_kind]
166
+
167
+ case value_kind
168
+ when 'Term'
169
+ value = polar.host.to_ruby value
170
+ when 'Ref'
171
+ child_field = value['field']
172
+ result_id = value['result_id']
173
+ value = Ref.new field: child_field, result_id: result_id
174
+ when 'Field'
175
+ value = Field.new field: value
176
+ else
177
+ raise "Unknown value kind `#{value_kind}`"
178
+ end
179
+
180
+ new kind: kind, field: field, value: value
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -3,7 +3,7 @@
3
3
  module Oso
4
4
  module Polar
5
5
  # Base error type for Oso::Polar.
6
- class Error < ::RuntimeError
6
+ class Error < ::Oso::Error
7
7
  attr_reader :stack_trace
8
8
 
9
9
  # @param message [String]
@@ -68,7 +68,7 @@ module Oso
68
68
  end
69
69
  end
70
70
  class DuplicateClassAliasError < PolarRuntimeError # rubocop:disable Style/Documentation
71
- # @param as [String]
71
+ # @param name [String]
72
72
  # @param old [Class]
73
73
  # @param new [Class]
74
74
  def initialize(name:, old:, new:)
@@ -101,7 +101,6 @@ module Oso
101
101
  class ParameterError < ApiError; end
102
102
 
103
103
  class ValidationError < Error; end
104
- class RolesValidationError < Error; end
105
104
 
106
105
  UNEXPECTED_EXPRESSION_MESSAGE = <<~MSG
107
106
  Received Expression from Polar VM. The Expression type is not yet supported in this language.
@@ -56,7 +56,7 @@ module Oso
56
56
  operational_error(subkind, msg: msg, details: details)
57
57
  when 'Parameter'
58
58
  api_error(subkind, msg: msg, details: details)
59
- when 'RolesValidation'
59
+ when 'Validation'
60
60
  validation_error(msg, details: details)
61
61
  end
62
62
  end
@@ -146,7 +146,7 @@ module Oso
146
146
  # @return [::Oso::Polar::ValidationError] the object converted into the expected format.
147
147
  private_class_method def self.validation_error(msg, details:)
148
148
  # This is currently the only type of validation error.
149
- ::Oso::Polar::RolesValidationError.new(msg, details: details)
149
+ ::Oso::Polar::ValidationError.new(msg, details: details)
150
150
  end
151
151
  end
152
152
  end