oso-oso 0.12.4 → 0.14.1

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: 5adc1e8d113b85c152b3168283dc65faeeeee66c
4
- data.tar.gz: a24516694f568ef878f905e918f60ef8b62fcb59
3
+ metadata.gz: e611289b05e0398183cb6e7f29929b2854b1aaad
4
+ data.tar.gz: bd8af76c1433c16193b4cf2587cc5ca9f953c5b8
5
5
  SHA512:
6
- metadata.gz: 6dde362c0e9628faed46026a782aae82bb3d711640409a851823291b7c7ac0c517a107a7a668efed9acb1c370e9416116434564472090f8b99f706ca85483660
7
- data.tar.gz: 409b7fb99a4ac4497abbd6da542da7067245e8c1218885f3683c03cbd9a0c507287bc0e03f4449f6dc5c1eff0ae6ef6122b51967bbca194d632f7ad6baa81a72
6
+ metadata.gz: 6778dedbfe11bb27219ed96712e3e93e79d3187455aad86040764e25b34079a4645ec16de0afed876faa88b290e58fad7e22ccd2ee6aaa61529bd1f9b6231a28
7
+ data.tar.gz: d21fda13edd7176ecc3dad96258f57f19f51b3114ee02c1fb844277befd0abf9a8cfb035df0593a49977a884aa2bc3fbfd6734cc547be0ef5093d149f866c8d7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oso-oso (0.12.4)
4
+ oso-oso (0.14.1)
5
5
  ffi (~> 1.0)
6
6
 
7
7
  GEM
Binary file
Binary file
Binary file
data/lib/oso/oso.rb CHANGED
@@ -17,10 +17,7 @@ module Oso
17
17
  # @param resource [Object] Object.
18
18
  # @return [Boolean] An access control decision.
19
19
  def allowed?(actor:, action:, resource:)
20
- query_rule('allow', actor, action, resource).next
21
- true
22
- rescue StopIteration
23
- false
20
+ !query_rule('allow', actor, action, resource).first.nil?
24
21
  end
25
22
  end
26
23
  end
data/lib/oso/polar.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'oso/polar/errors'
4
+ require 'oso/polar/expression'
4
5
  require 'oso/polar/ffi'
5
6
  require 'oso/polar/host'
7
+ require 'oso/polar/pattern'
6
8
  require 'oso/polar/polar'
7
9
  require 'oso/polar/predicate'
8
10
  require 'oso/polar/query'
@@ -100,8 +100,11 @@ module Oso
100
100
  class ApiError < Error; end
101
101
  class ParameterError < ApiError; end
102
102
 
103
+ class ValidationError < Error; end
104
+ class RolesValidationError < Error; end
105
+
103
106
  UNEXPECTED_EXPRESSION_MESSAGE = <<~MSG
104
- Recieved Expression from Polar VM. The Expression type is not yet supported in this language.
107
+ Received Expression from Polar VM. The Expression type is not yet supported in this language.
105
108
 
106
109
  This may mean you performed an operation in your policy over an unbound variable.
107
110
  MSG
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ # Polar expression.
6
+ class Expression
7
+ attr_reader :operator, :args
8
+
9
+ # @param operator [String]
10
+ # @param args [Array<Object>]
11
+ def initialize(operator, args)
12
+ @operator = operator
13
+ @args = args
14
+ end
15
+
16
+ # @param other [Expression]
17
+ # @return [Boolean]
18
+ def ==(other)
19
+ operator == other.operator && args == other.args
20
+ end
21
+
22
+ # @see #==
23
+ alias eql? ==
24
+ end
25
+ end
26
+ end
@@ -24,14 +24,22 @@ module Oso
24
24
  #
25
25
  # @return [::Oso::Polar::Error] if there's an FFI error.
26
26
  # @return [::Oso::Polar::FFIErrorNotFound] if there isn't one.
27
- def self.get # rubocop:disable Metrics/MethodLength
27
+ def self.get # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
28
28
  error = Rust.get
29
29
  return ::Oso::Polar::FFIErrorNotFound if error.null?
30
30
 
31
31
  error = JSON.parse(error.to_s)
32
32
  msg = error['formatted']
33
33
  kind, body = error['kind'].first
34
- subkind, details = body.first
34
+
35
+ # Not all errors have subkind and details.
36
+ # TODO (gj): This bug may exist in other libraries.
37
+ if body.is_a? Hash
38
+ subkind, details = body.first
39
+ else
40
+ subkind, details = nil
41
+ end
42
+
35
43
  case kind
36
44
  when 'Parse'
37
45
  parse_error(subkind, msg: msg, details: details)
@@ -41,6 +49,8 @@ module Oso
41
49
  operational_error(subkind, msg: msg, details: details)
42
50
  when 'Parameter'
43
51
  api_error(subkind, msg: msg, details: details)
52
+ when 'RolesValidation'
53
+ validation_error(msg, details: details)
44
54
  end
45
55
  end
46
56
 
@@ -121,6 +131,16 @@ module Oso
121
131
  ::Oso::Polar::ApiError.new(msg, details: details)
122
132
  end
123
133
  end
134
+
135
+ # Map FFI Validation errors into Ruby exceptions.
136
+ #
137
+ # @param msg [String]
138
+ # @param details [Hash<String, Object>]
139
+ # @return [::Oso::Polar::ValidationError] the object converted into the expected format.
140
+ private_class_method def self.validation_error(msg, details:)
141
+ # This is currently the only type of validation error.
142
+ ::Oso::Polar::RolesValidationError.new(msg, details: details)
143
+ end
124
144
  end
125
145
  end
126
146
  end
@@ -12,6 +12,8 @@ module Oso
12
12
  ffi_lib FFI::LIB_PATH
13
13
 
14
14
  attach_function :new, :polar_new, [], FFI::Polar
15
+ attach_function :enable_roles, :polar_enable_roles, [FFI::Polar], :int32
16
+ attach_function :validate_roles_config, :polar_validate_roles_config, [FFI::Polar, :string], :int32
15
17
  attach_function :load, :polar_load, [FFI::Polar, :string, :string], :int32
16
18
  attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], :int32
17
19
  attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
@@ -33,6 +35,20 @@ module Oso
33
35
  polar
34
36
  end
35
37
 
38
+ # @raise [FFI::Error] if the FFI call returns an error.
39
+ def enable_roles
40
+ result = Rust.enable_roles(self)
41
+ process_messages
42
+ raise FFI::Error.get if result.zero?
43
+ end
44
+
45
+ # @raise [FFI::Error] if the FFI call returns an error.
46
+ def validate_roles_config(config)
47
+ result = Rust.validate_roles_config(self, JSON.dump(config))
48
+ process_messages
49
+ raise FFI::Error.get if result.zero?
50
+ end
51
+
36
52
  # @param src [String]
37
53
  # @param filename [String]
38
54
  # @raise [FFI::Error] if the FFI call returns an error.
@@ -2,6 +2,38 @@
2
2
 
3
3
  module Oso
4
4
  module Polar
5
+ # Ruby code reloaders (i.e. the one used by rails) swap out the value of
6
+ # a constant on code changes. Because of this, we can't reliably call
7
+ # `is_a?` on the constant that was passed to `register_class`.
8
+ #
9
+ # Example (where Foo is a class defined in foo.rb):
10
+ # > klass = Foo
11
+ # > Foo.new.is_a? klass
12
+ # => true
13
+ # > ... user changes foo.rb ...
14
+ # > Foo.new.is_a? klass
15
+ # => false
16
+ #
17
+ # To solve this, when we need to access the class (e.g. during isa), we
18
+ # look it up using const_get, which will always return the up-to-date
19
+ # version of the class.
20
+ class PolarClass
21
+ attr_reader :name, :anon_class
22
+
23
+ def initialize(klass)
24
+ @name = klass.name
25
+ # If the class doesn't have a name, it is anonymous, meaning we should
26
+ # actually store it directly
27
+ @anon_class = klass if klass.name.nil?
28
+ end
29
+
30
+ def get
31
+ return anon_class if anon_class
32
+
33
+ Object.const_get(name)
34
+ end
35
+ end
36
+
5
37
  # Translate between Polar and the host language (Ruby).
6
38
  class Host # rubocop:disable Metrics/ClassLength
7
39
  protected
@@ -12,13 +44,18 @@ module Oso
12
44
  attr_reader :classes
13
45
  # @return [Hash<Integer, Object>]
14
46
  attr_reader :instances
47
+ # @return [Boolean]
48
+ attr_reader :accept_expression
15
49
 
16
50
  public
17
51
 
52
+ attr_writer :accept_expression
53
+
18
54
  def initialize(ffi_polar)
19
55
  @ffi_polar = ffi_polar
20
56
  @classes = {}
21
57
  @instances = {}
58
+ @accept_expression = false
22
59
  end
23
60
 
24
61
  def initialize_copy(other)
@@ -35,7 +72,7 @@ module Oso
35
72
  def get_class(name)
36
73
  raise UnregisteredClassError, name unless classes.key? name
37
74
 
38
- classes[name]
75
+ classes[name].get
39
76
  end
40
77
 
41
78
  # Store a Ruby class in the {#classes} cache.
@@ -48,7 +85,7 @@ module Oso
48
85
  def cache_class(cls, name:)
49
86
  raise DuplicateClassAliasError.new name: name, old: get_class(name), new: cls if classes.key? name
50
87
 
51
- classes[name] = cls
88
+ classes[name] = PolarClass.new(cls)
52
89
  name
53
90
  end
54
91
 
@@ -73,7 +110,10 @@ module Oso
73
110
  def get_instance(id)
74
111
  raise UnregisteredInstanceError, id unless instance? id
75
112
 
76
- instances[id]
113
+ instance = instances[id]
114
+ return instance.get if instance.is_a? PolarClass
115
+
116
+ instance
77
117
  end
78
118
 
79
119
  # Cache a Ruby instance in the {#instances} cache, fetching a new id if
@@ -84,6 +124,8 @@ module Oso
84
124
  # @return [Integer] the instance ID.
85
125
  def cache_instance(instance, id: nil)
86
126
  id = ffi_polar.new_id if id.nil?
127
+ # Save the instance as a PolarClass if it is a non-anonymous class
128
+ instance = PolarClass.new(instance) if instance.is_a?(Class)
87
129
  instances[id] = instance
88
130
  id
89
131
  end
@@ -107,6 +149,33 @@ module Oso
107
149
  raise PolarRuntimeError, "Error constructing instance of #{cls_name}: #{e}"
108
150
  end
109
151
 
152
+ OPS = {
153
+ 'Lt' => :<,
154
+ 'Gt' => :>,
155
+ 'Eq' => :==,
156
+ 'Geq' => :>=,
157
+ 'Leq' => :<=,
158
+ 'Neq' => :!=
159
+ }.freeze
160
+
161
+ # Compare two values
162
+ #
163
+ # @param op [String] operation to perform.
164
+ # @param args [Array<Object>] left and right args to operation.
165
+ # @raise [PolarRuntimeError] if operation fails or is unsupported.
166
+ # @return [Boolean]
167
+ def operator(operation, args)
168
+ left, right = args
169
+ op = OPS[operation]
170
+ raise PolarRuntimeError, "Unsupported external operation '#{left.class} #{operation} #{right.class}'" if op.nil?
171
+
172
+ begin
173
+ left.__send__ op, right
174
+ rescue StandardError
175
+ raise PolarRuntimeError, "External operation '#{left.class} #{operation} #{right.class}' failed."
176
+ end
177
+ end
178
+
110
179
  # Check if the left class is more specific than the right class
111
180
  # with respect to the given instance.
112
181
  #
@@ -132,17 +201,6 @@ module Oso
132
201
  instance.is_a? cls
133
202
  end
134
203
 
135
- # Check if two instances unify
136
- #
137
- # @param left_instance_id [Integer]
138
- # @param right_instance_id [Integer]
139
- # @return [Boolean]
140
- def unify?(left_instance_id, right_instance_id)
141
- left_instance = get_instance(left_instance_id)
142
- right_instance = get_instance(right_instance_id)
143
- left_instance == right_instance
144
- end
145
-
146
204
  # Turn a Ruby value into a Polar term that's ready to be sent across the
147
205
  # FFI boundary.
148
206
  #
@@ -173,7 +231,16 @@ module Oso
173
231
  { 'Call' => { 'name' => value.name, 'args' => value.args.map { |el| to_polar(el) } } }
174
232
  when value.instance_of?(Variable)
175
233
  # This is supported so that we can query for unbound variables
176
- { 'Variable' => value }
234
+ { 'Variable' => value.name }
235
+ when value.instance_of?(Expression)
236
+ { 'Expression' => { 'operator' => value.operator, 'args' => value.args.map { |el| to_polar(el) } } }
237
+ when value.instance_of?(Pattern)
238
+ dict = to_polar(value.fields)['value']
239
+ if value.tag.nil?
240
+ { 'Pattern' => dict }
241
+ else
242
+ { 'Pattern' => { 'Instance' => { 'tag' => value.tag, 'fields' => dict['Dictionary'] } } }
243
+ end
177
244
  else
178
245
  { 'ExternalInstance' => { 'instance_id' => cache_instance(value), 'repr' => value.to_s } }
179
246
  end
@@ -219,7 +286,24 @@ module Oso
219
286
  when 'Call'
220
287
  Predicate.new(value['name'], args: value['args'].map { |a| to_ruby(a) })
221
288
  when 'Variable'
222
- Variable.new(value['name'])
289
+ Variable.new(value)
290
+ when 'Expression'
291
+ raise UnexpectedPolarTypeError, tag unless accept_expression
292
+
293
+ args = value['args'].map { |a| to_ruby(a) }
294
+ Expression.new(value['operator'], args)
295
+ when 'Pattern'
296
+ case value.keys.first
297
+ when 'Instance'
298
+ tag = value.values.first['tag']
299
+ fields = value.values.first['fields']['fields'].transform_values { |v| to_ruby(v) }
300
+ Pattern.new(tag, fields)
301
+ when 'Dictionary'
302
+ fields = value.values.first['fields'].transform_values { |v| to_ruby(v) }
303
+ Pattern.new(nil, fields)
304
+ else
305
+ raise UnexpectedPolarTypeError, "#{value.keys.first} variant of Pattern"
306
+ end
223
307
  else
224
308
  raise UnexpectedPolarTypeError, tag
225
309
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ # Polar pattern.
6
+ class Pattern
7
+ attr_reader :tag, :fields
8
+
9
+ # @param tag [String]
10
+ # @param fields [Hash<String, Object>]
11
+ def initialize(tag, fields)
12
+ @tag = tag
13
+ @fields = fields
14
+ end
15
+
16
+ # @param other [Pattern]
17
+ # @return [Boolean]
18
+ def ==(other)
19
+ tag == other.tag && fields == other.fields
20
+ end
21
+
22
+ # @see #==
23
+ alias eql? ==
24
+ end
25
+ end
26
+ end
@@ -37,6 +37,7 @@ module Oso
37
37
  def initialize
38
38
  @ffi_polar = FFI::Polar.create
39
39
  @host = Host.new(ffi_polar)
40
+ @polar_roles_enabled = false
40
41
 
41
42
  # Register global constants.
42
43
  register_constant nil, name: 'nil'
@@ -50,11 +51,48 @@ module Oso
50
51
  register_class String
51
52
  end
52
53
 
54
+ def enable_roles # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
55
+ return if polar_roles_enabled
56
+
57
+ roles_helper = Class.new do
58
+ def self.join(separator, left, right)
59
+ [left, right].join(separator)
60
+ end
61
+ end
62
+ register_constant(roles_helper, name: '__oso_internal_roles_helpers__')
63
+ ffi_polar.enable_roles
64
+ self.polar_roles_enabled = true
65
+
66
+ # validate config
67
+ validation_query_results = []
68
+ loop do
69
+ query = ffi_polar.next_inline_query
70
+ break if query.nil?
71
+
72
+ new_host = host.dup
73
+ new_host.accept_expression = true
74
+ results = Query.new(query, host: new_host).to_a
75
+ raise InlineQueryFailedError, query.source if results.empty?
76
+
77
+ validation_query_results.push results
78
+ end
79
+
80
+ # turn bindings back into polar
81
+ validation_query_results = validation_query_results.map do |results|
82
+ results.map do |result|
83
+ { 'bindings' => result.transform_values { |v| host.to_polar(v) } }
84
+ end
85
+ end
86
+
87
+ ffi_polar.validate_roles_config(validation_query_results)
88
+ end
89
+
53
90
  # Clear all rules and rule sources from the current Polar instance
54
91
  #
55
92
  # @return [self] for chaining.
56
93
  def clear_rules
57
94
  ffi_polar.clear_rules
95
+ ffi_polar.enable_roles if polar_roles_enabled
58
96
  self
59
97
  end
60
98
 
@@ -81,7 +119,7 @@ module Oso
81
119
  # @raise [InlineQueryFailedError] on the first failed inline query.
82
120
  # @raise [Error] if any of the FFI calls raise one.
83
121
  # @return [self] for chaining.
84
- def load_str(str, filename: nil) # rubocop:disable Metrics/MethodLength
122
+ def load_str(str, filename: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
85
123
  raise NullByteInPolarFileError if str.chomp("\0").include?("\0")
86
124
 
87
125
  ffi_polar.load(str, filename: filename)
@@ -89,12 +127,15 @@ module Oso
89
127
  next_query = ffi_polar.next_inline_query
90
128
  break if next_query.nil?
91
129
 
92
- begin
93
- Query.new(next_query, host: host).results.next
94
- rescue StopIteration
95
- raise InlineQueryFailedError, next_query.source
96
- end
130
+ raise InlineQueryFailedError, next_query.source if Query.new(next_query, host: host).first.nil?
131
+ end
132
+
133
+ # If roles are enabled, re-validate config when new rules are loaded.
134
+ if polar_roles_enabled
135
+ self.polar_roles_enabled = false
136
+ enable_roles
97
137
  end
138
+
98
139
  self
99
140
  end
100
141
 
@@ -118,7 +159,7 @@ module Oso
118
159
  else
119
160
  raise InvalidQueryTypeError
120
161
  end
121
- Query.new(ffi_query, host: new_host).results
162
+ Query.new(ffi_query, host: new_host)
122
163
  end
123
164
 
124
165
  # Query for a rule.
@@ -174,6 +215,7 @@ module Oso
174
215
 
175
216
  # @return [FFI::Polar]
176
217
  attr_reader :ffi_polar
218
+ attr_accessor :polar_roles_enabled
177
219
 
178
220
  # The R and L in REPL for systems where readline is available.
179
221
  def repl_readline(prompt)
@@ -214,7 +256,7 @@ module Oso
214
256
  end
215
257
 
216
258
  begin
217
- results = Query.new(ffi_query, host: host).results.to_a
259
+ results = Query.new(ffi_query, host: host).to_a
218
260
  rescue PolarRuntimeError => e
219
261
  print_error(e)
220
262
  return
@@ -6,8 +6,7 @@ module Oso
6
6
  module Polar
7
7
  # A single Polar query.
8
8
  class Query # rubocop:disable Metrics/ClassLength
9
- # @return [Enumerator]
10
- attr_reader :results
9
+ include Enumerable
11
10
 
12
11
  # @param ffi_query [FFI::Query]
13
12
  # @param host [Oso::Polar::Host]
@@ -15,7 +14,6 @@ module Oso
15
14
  @calls = {}
16
15
  @ffi_query = ffi_query
17
16
  @host = host
18
- @results = start
19
17
  end
20
18
 
21
19
  private
@@ -120,61 +118,57 @@ module Oso
120
118
  # @yieldparam [Hash<String, Object>]
121
119
  # @return [Enumerator]
122
120
  # @raise [Error] if any of the FFI calls raise one.
123
- def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
124
- Enumerator.new do |yielder| # rubocop:disable Metrics/BlockLength
125
- loop do # rubocop:disable Metrics/BlockLength
126
- event = ffi_query.next_event
127
- case event.kind
128
- when 'Done'
129
- break
130
- when 'Result'
131
- yielder << event.data['bindings'].transform_values { |v| host.to_ruby(v) }
132
- when 'MakeExternal'
133
- handle_make_external(event.data)
134
- when 'ExternalCall'
135
- call_id = event.data['call_id']
136
- instance = event.data['instance']
137
- attribute = event.data['attribute']
138
- args = event.data['args'] || []
139
- kwargs = event.data['kwargs'] || {}
140
- handle_call(attribute, call_id: call_id, instance: instance, args: args, kwargs: kwargs)
141
- when 'ExternalIsSubSpecializer'
142
- instance_id = event.data['instance_id']
143
- left_tag = event.data['left_class_tag']
144
- right_tag = event.data['right_class_tag']
145
- answer = host.subspecializer?(instance_id, left_tag: left_tag, right_tag: right_tag)
146
- question_result(answer, call_id: event.data['call_id'])
147
- when 'ExternalIsa'
148
- instance = event.data['instance']
149
- class_tag = event.data['class_tag']
150
- answer = host.isa?(instance, class_tag: class_tag)
151
- question_result(answer, call_id: event.data['call_id'])
152
- when 'ExternalUnify'
153
- left_instance_id = event.data['left_instance_id']
154
- right_instance_id = event.data['right_instance_id']
155
- answer = host.unify?(left_instance_id, right_instance_id)
156
- question_result(answer, call_id: event.data['call_id'])
157
- when 'Debug'
158
- puts event.data['message'] if event.data['message']
159
- print 'debug> '
160
- begin
161
- input = $stdin.readline.chomp.chomp(';')
162
- rescue EOFError
163
- next
164
- end
165
- command = JSON.dump(host.to_polar(input))
166
- ffi_query.debug_command(command)
167
- when 'ExternalOp'
168
- raise UnimplementedOperationError, 'comparison operators'
169
- when 'NextExternal'
170
- call_id = event.data['call_id']
171
- iterable = event.data['iterable']
172
- handle_next_external(call_id, iterable)
173
- else
174
- raise "Unhandled event: #{JSON.dump(event.inspect)}"
121
+ def each # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
122
+ loop do # rubocop:disable Metrics/BlockLength
123
+ event = ffi_query.next_event
124
+ case event.kind
125
+ when 'Done'
126
+ break
127
+ when 'Result'
128
+ yield event.data['bindings'].transform_values { |v| host.to_ruby(v) }
129
+ when 'MakeExternal'
130
+ handle_make_external(event.data)
131
+ when 'ExternalCall'
132
+ call_id = event.data['call_id']
133
+ instance = event.data['instance']
134
+ attribute = event.data['attribute']
135
+ args = event.data['args'] || []
136
+ kwargs = event.data['kwargs'] || {}
137
+ handle_call(attribute, call_id: call_id, instance: instance, args: args, kwargs: kwargs)
138
+ when 'ExternalIsSubSpecializer'
139
+ instance_id = event.data['instance_id']
140
+ left_tag = event.data['left_class_tag']
141
+ right_tag = event.data['right_class_tag']
142
+ answer = host.subspecializer?(instance_id, left_tag: left_tag, right_tag: right_tag)
143
+ question_result(answer, call_id: event.data['call_id'])
144
+ when 'ExternalIsa'
145
+ instance = event.data['instance']
146
+ class_tag = event.data['class_tag']
147
+ answer = host.isa?(instance, class_tag: class_tag)
148
+ question_result(answer, call_id: event.data['call_id'])
149
+ when 'Debug'
150
+ puts event.data['message'] if event.data['message']
151
+ print 'debug> '
152
+ begin
153
+ input = $stdin.readline.chomp.chomp(';')
154
+ rescue EOFError
155
+ next
175
156
  end
157
+ command = JSON.dump(host.to_polar(input))
158
+ ffi_query.debug_command(command)
159
+ when 'ExternalOp'
160
+ op = event.data['operator']
161
+ args = event.data['args'].map(&host.method(:to_ruby))
162
+ answer = host.operator(op, args)
163
+ question_result(answer, call_id: event.data['call_id'])
164
+ when 'NextExternal'
165
+ call_id = event.data['call_id']
166
+ iterable = event.data['iterable']
167
+ handle_next_external(call_id, iterable)
168
+ else
169
+ raise "Unhandled event: #{JSON.dump(event.inspect)}"
176
170
  end
177
- end.lazy
171
+ end
178
172
  end
179
173
  end
180
174
  end
data/lib/oso/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Oso
4
- VERSION = '0.12.4'
4
+ VERSION = '0.14.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oso-oso
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.4
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oso Security, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-09 00:00:00.000000000 Z
11
+ date: 2021-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -133,6 +133,7 @@ files:
133
133
  - lib/oso/oso.rb
134
134
  - lib/oso/polar.rb
135
135
  - lib/oso/polar/errors.rb
136
+ - lib/oso/polar/expression.rb
136
137
  - lib/oso/polar/ffi.rb
137
138
  - lib/oso/polar/ffi/error.rb
138
139
  - lib/oso/polar/ffi/message.rb
@@ -141,6 +142,7 @@ files:
141
142
  - lib/oso/polar/ffi/query_event.rb
142
143
  - lib/oso/polar/ffi/source.rb
143
144
  - lib/oso/polar/host.rb
145
+ - lib/oso/polar/pattern.rb
144
146
  - lib/oso/polar/polar.rb
145
147
  - lib/oso/polar/predicate.rb
146
148
  - lib/oso/polar/query.rb