oso-oso 0.22.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +7 -6
- data/Makefile +1 -1
- data/ext/oso-oso/lib/libpolar.dylib +0 -0
- data/ext/oso-oso/lib/libpolar.so +0 -0
- data/ext/oso-oso/lib/polar.dll +0 -0
- data/lib/oso/oso.rb +22 -23
- data/lib/oso/polar/data/adapter/active_record_adapter.rb +57 -0
- data/lib/oso/polar/data/adapter.rb +22 -0
- data/lib/oso/polar/data/filter.rb +59 -0
- data/lib/oso/polar/data.rb +4 -0
- data/lib/oso/polar/data_filtering.rb +10 -5
- data/lib/oso/polar/errors.rb +0 -6
- data/lib/oso/polar/expression.rb +1 -1
- data/lib/oso/polar/ffi/error.rb +4 -41
- data/lib/oso/polar/ffi/polar.rb +60 -35
- data/lib/oso/polar/ffi/query.rb +48 -22
- data/lib/oso/polar/ffi/{source.rb → rust_string.rb} +9 -3
- data/lib/oso/polar/ffi.rb +28 -20
- data/lib/oso/polar/host.rb +31 -9
- data/lib/oso/polar/polar.rb +82 -15
- data/lib/oso/polar/query.rb +29 -11
- data/lib/oso/polar.rb +1 -0
- data/lib/oso/version.rb +1 -1
- metadata +7 -5
- data/lib/oso/polar/ffi/message.rb +0 -40
- data/lib/oso/polar/ffi/query_event.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c65ea3a330a9a56b2ac4e58594e379409f6a4617
|
4
|
+
data.tar.gz: 51cbba24a5b9f529df29a059f1cfe0230c50d36a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 324927c416971e4a1b8baa5eebb19b1c155b0f66607277ca416482d1e2e588ed44efe8a05824cf939fe85bb13b8bbf400769c1f979b0bdf5865b8630123c4d53
|
7
|
+
data.tar.gz: 4437ddc46fbcf28ade62f9537fcdb180cea4b7ceb11c065bb94ed9e3868c61276dbd9e9e107d7d61a4dc25e40d7ef52551bdb493a36b7a27f63e9a2c70dc157f
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
oso-oso (0.
|
4
|
+
oso-oso (0.25.0)
|
5
5
|
ffi (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -21,14 +21,14 @@ GEM
|
|
21
21
|
arel (9.0.0)
|
22
22
|
ast (2.4.2)
|
23
23
|
backport (1.2.0)
|
24
|
-
benchmark (0.
|
24
|
+
benchmark (0.2.0)
|
25
25
|
byebug (11.1.3)
|
26
26
|
coderay (1.1.3)
|
27
27
|
concurrent-ruby (1.1.9)
|
28
28
|
diff-lcs (1.4.4)
|
29
29
|
e2mmap (0.1.0)
|
30
30
|
ffi (1.15.4)
|
31
|
-
i18n (1.8.
|
31
|
+
i18n (1.8.11)
|
32
32
|
concurrent-ruby (~> 1.0)
|
33
33
|
jaro_winkler (1.5.4)
|
34
34
|
maruku (0.7.3)
|
@@ -49,7 +49,7 @@ GEM
|
|
49
49
|
rainbow (3.0.0)
|
50
50
|
rake (12.3.3)
|
51
51
|
regexp_parser (2.1.1)
|
52
|
-
reverse_markdown (2.
|
52
|
+
reverse_markdown (2.1.1)
|
53
53
|
nokogiri
|
54
54
|
rexml (3.2.5)
|
55
55
|
rspec (3.10.0)
|
@@ -64,7 +64,7 @@ GEM
|
|
64
64
|
rspec-mocks (3.10.2)
|
65
65
|
diff-lcs (>= 1.2.0, < 2.0)
|
66
66
|
rspec-support (~> 3.10.0)
|
67
|
-
rspec-support (3.10.
|
67
|
+
rspec-support (3.10.3)
|
68
68
|
rubocop (0.89.1)
|
69
69
|
parallel (~> 1.10)
|
70
70
|
parser (>= 2.7.1.1)
|
@@ -97,12 +97,13 @@ GEM
|
|
97
97
|
tilt (2.0.10)
|
98
98
|
tzinfo (1.2.9)
|
99
99
|
thread_safe (~> 0.1)
|
100
|
-
unicode-display_width (1.
|
100
|
+
unicode-display_width (1.8.0)
|
101
101
|
yard (0.9.26)
|
102
102
|
|
103
103
|
PLATFORMS
|
104
104
|
ruby
|
105
105
|
x86_64-darwin-20
|
106
|
+
x86_64-linux
|
106
107
|
|
107
108
|
DEPENDENCIES
|
108
109
|
activerecord
|
data/Makefile
CHANGED
Binary file
|
data/ext/oso-oso/lib/libpolar.so
CHANGED
Binary file
|
data/ext/oso-oso/lib/polar.dll
CHANGED
Binary file
|
data/lib/oso/oso.rb
CHANGED
@@ -181,27 +181,17 @@ module Oso
|
|
181
181
|
# @param resource_cls The resource being accessed.
|
182
182
|
#
|
183
183
|
# @return A query for resources accessible to the actor.
|
184
|
-
def authorized_query(actor, action, resource_cls)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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) } })
|
184
|
+
def authorized_query(actor, action, resource_cls)
|
185
|
+
if host.use_new_data_filtering?
|
186
|
+
|
187
|
+
unless host.types[resource_cls].build_query == ::Oso::Polar::Host::DEFAULT_BUILD_QUERY
|
188
|
+
warn 'Warning: redundant data filtering configuration detected'
|
199
189
|
end
|
200
|
-
end
|
201
190
|
|
202
|
-
|
203
|
-
|
204
|
-
|
191
|
+
new_authorized_query(actor, action, resource_cls)
|
192
|
+
else
|
193
|
+
old_authorized_query(actor, action, resource_cls)
|
194
|
+
end
|
205
195
|
end
|
206
196
|
|
207
197
|
# Determine the resources of type +resource_cls+ that +actor+
|
@@ -213,10 +203,15 @@ module Oso
|
|
213
203
|
#
|
214
204
|
# @return A list of resources accessible to the actor.
|
215
205
|
def authorized_resources(actor, action, resource_cls)
|
216
|
-
q = authorized_query
|
217
|
-
|
218
|
-
|
219
|
-
|
206
|
+
q = authorized_query(actor, action, resource_cls)
|
207
|
+
|
208
|
+
if host.use_new_data_filtering?
|
209
|
+
host.adapter.execute_query q
|
210
|
+
elsif q.nil?
|
211
|
+
[]
|
212
|
+
else
|
213
|
+
host.types[resource_cls].exec_query[q]
|
214
|
+
end
|
220
215
|
end
|
221
216
|
|
222
217
|
# Register default values for data filtering query functions.
|
@@ -228,5 +223,9 @@ module Oso
|
|
228
223
|
host.exec_query = exec_query if exec_query
|
229
224
|
host.combine_query = combine_query if combine_query
|
230
225
|
end
|
226
|
+
|
227
|
+
def data_filtering_adapter=(adapter)
|
228
|
+
host.adapter = adapter
|
229
|
+
end
|
231
230
|
end
|
232
231
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Oso
|
4
|
+
module Polar
|
5
|
+
module Data
|
6
|
+
class Adapter
|
7
|
+
# Example data filtering adapter for ActiveRecord
|
8
|
+
class ActiveRecordAdapter < Adapter
|
9
|
+
def build_query(filter) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
10
|
+
types = filter.types
|
11
|
+
query = filter.relations.reduce(filter.model.all) do |q, rel|
|
12
|
+
rec = types[rel.left].fields[rel.name]
|
13
|
+
q.joins(
|
14
|
+
"INNER JOIN #{rel.right.table_name} ON " \
|
15
|
+
"#{rel.left.table_name}.#{rec.my_field} = " \
|
16
|
+
"#{rel.right.table_name}.#{rec.other_field}"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
filter.conditions.map do |conjs|
|
21
|
+
conjs.reduce(query) do |q, conj|
|
22
|
+
q.where(*sqlize(conj))
|
23
|
+
end
|
24
|
+
end.reduce(:or).distinct
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute_query(query)
|
28
|
+
query.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
OPS = {
|
32
|
+
'Eq' => '=', 'In' => 'IN', 'Nin' => 'NOT IN', 'Neq' => '!=',
|
33
|
+
'Lt' => '<', 'Gt' => '>', 'Leq' => '<=', 'Geq' => '>='
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def sqlize(cond)
|
39
|
+
args = []
|
40
|
+
lhs = add_side cond.left, args
|
41
|
+
rhs = add_side cond.right, args
|
42
|
+
args.unshift "#{lhs} #{OPS[cond.cmp]} #{rhs}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_side(side, args)
|
46
|
+
if side.is_a? ::Oso::Polar::Data::Filter::Projection
|
47
|
+
"#{side.source.table_name}.#{side.field || side.source.primary_key}"
|
48
|
+
else
|
49
|
+
args.push side
|
50
|
+
'?'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Oso
|
4
|
+
module Polar
|
5
|
+
module Data
|
6
|
+
# Abstract data adapter
|
7
|
+
#
|
8
|
+
# An Adapter has to implement two methods.
|
9
|
+
class Adapter
|
10
|
+
# Make a query object from a filter
|
11
|
+
def build_query(_filter)
|
12
|
+
raise "build_query not implemented for #{self}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Make a list of objects from a query
|
16
|
+
def execute_query(_query)
|
17
|
+
raise "execute_query not implemented for #{self}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Oso
|
4
|
+
module Polar
|
5
|
+
# Data filtering interface for Ruby
|
6
|
+
module Data
|
7
|
+
# Abstract data filter used by the Adapter API.
|
8
|
+
class Filter
|
9
|
+
attr_reader :model, :relations, :conditions, :types
|
10
|
+
|
11
|
+
def initialize(model:, relations:, conditions:, types:)
|
12
|
+
@model = model
|
13
|
+
@relations = relations
|
14
|
+
@conditions = conditions
|
15
|
+
@types = types
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parse(polar, blob)
|
19
|
+
types = polar.host.types
|
20
|
+
model = types[blob['root']].klass.get
|
21
|
+
relations = blob['relations'].map do |rel|
|
22
|
+
Relation.parse(polar, *rel)
|
23
|
+
end
|
24
|
+
conditions = blob['conditions'].map do |disj|
|
25
|
+
disj.map { |conj| Condition.parse(polar, *conj) }
|
26
|
+
end
|
27
|
+
new(model: model, relations: relations, conditions: conditions, types: types)
|
28
|
+
end
|
29
|
+
|
30
|
+
Projection = Struct.new(:source, :field)
|
31
|
+
|
32
|
+
Relation = Struct.new(:left, :name, :right) do
|
33
|
+
def self.parse(polar, left, name, right)
|
34
|
+
Relation.new(polar.name_to_class(left), name, polar.name_to_class(right))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Condition = Struct.new(:left, :cmp, :right) do
|
39
|
+
def self.parse(polar, left, cmp, right)
|
40
|
+
Condition.new(parse_side(polar, left), cmp, parse_side(polar, right))
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.parse_side(polar, side)
|
44
|
+
key = side.keys.first
|
45
|
+
val = side[key]
|
46
|
+
case key
|
47
|
+
when 'Field'
|
48
|
+
Projection.new(polar.name_to_class(val[0]), val[1])
|
49
|
+
when 'Immediate'
|
50
|
+
polar.host.to_ruby('value' => [[val.keys.first, val.values.first]])
|
51
|
+
else
|
52
|
+
raise key
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -85,12 +85,17 @@ module Oso
|
|
85
85
|
|
86
86
|
yrefs, nrefs = xrefs.partition { |r| %w[In Eq].include? r.kind }
|
87
87
|
[[yrefs, 'In'], [nrefs, 'Nin']].each do |refs, kind|
|
88
|
-
next unless refs.any?
|
89
|
-
|
90
88
|
refs.group_by { |f| f.value.result_id }.each do |rid, fils|
|
91
|
-
|
92
|
-
|
93
|
-
|
89
|
+
if fils.length > 1
|
90
|
+
value = results[rid].map { |r| fils.map { |f| GETATTR[r, f.value.field] } }
|
91
|
+
field = fils.map(&:field)
|
92
|
+
rest.push(Filter.new(kind: kind, value: value, field: field))
|
93
|
+
else
|
94
|
+
fil = fils[0]
|
95
|
+
field = fil.value.field
|
96
|
+
value = results[rid].map { |r| field.nil? ? r : r.send(field) }
|
97
|
+
rest.push(Filter.new(kind: kind, field: fil.field, value: value))
|
98
|
+
end
|
94
99
|
end
|
95
100
|
end
|
96
101
|
rest
|
data/lib/oso/polar/errors.rb
CHANGED
@@ -23,11 +23,9 @@ module Oso
|
|
23
23
|
|
24
24
|
# Errors from across the FFI boundary.
|
25
25
|
|
26
|
-
class SerializationError < PolarRuntimeError; end
|
27
26
|
class UnsupportedError < PolarRuntimeError; end
|
28
27
|
class PolarTypeError < PolarRuntimeError; end
|
29
28
|
class StackOverflowError < PolarRuntimeError; end
|
30
|
-
class FileLoadingError < PolarRuntimeError; end
|
31
29
|
|
32
30
|
# Errors originating from this side of the FFI boundary.
|
33
31
|
|
@@ -96,10 +94,6 @@ module Oso
|
|
96
94
|
class UnrecognizedToken < ParseError; end
|
97
95
|
end
|
98
96
|
|
99
|
-
# Generic Polar API exception.
|
100
|
-
class ApiError < Error; end
|
101
|
-
class ParameterError < ApiError; end
|
102
|
-
|
103
97
|
class ValidationError < Error; end
|
104
98
|
|
105
99
|
UNEXPECTED_EXPRESSION_MESSAGE = <<~MSG
|
data/lib/oso/polar/expression.rb
CHANGED
data/lib/oso/polar/ffi/error.rb
CHANGED
@@ -6,29 +6,13 @@ module Oso
|
|
6
6
|
module Polar
|
7
7
|
module FFI
|
8
8
|
# Wrapper class for Error FFI pointer + operations.
|
9
|
-
class Error
|
10
|
-
def to_s
|
11
|
-
@to_s ||= read_string.force_encoding('UTF-8')
|
12
|
-
end
|
13
|
-
|
14
|
-
Rust = Module.new do
|
15
|
-
extend ::FFI::Library
|
16
|
-
ffi_lib FFI::LIB_PATH
|
17
|
-
|
18
|
-
attach_function :get, :polar_get_error, [], Error
|
19
|
-
attach_function :free, :string_free, [Error], :int32
|
20
|
-
end
|
21
|
-
private_constant :Rust
|
22
|
-
|
9
|
+
class Error
|
23
10
|
# Check for an FFI error and convert it into a Ruby exception.
|
24
11
|
#
|
25
12
|
# @return [::Oso::Polar::Error] if there's an FFI error.
|
26
13
|
# @return [::Oso::Polar::FFIErrorNotFound] if there isn't one.
|
27
|
-
def self.get(enrich_message) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
28
|
-
error =
|
29
|
-
return ::Oso::Polar::FFIErrorNotFound if error.null?
|
30
|
-
|
31
|
-
error = JSON.parse(error.to_s)
|
14
|
+
def self.get(error_str, enrich_message) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
15
|
+
error = JSON.parse(error_str.to_s)
|
32
16
|
msg = error['formatted']
|
33
17
|
kind, body = error['kind'].first
|
34
18
|
|
@@ -54,8 +38,6 @@ module Oso
|
|
54
38
|
runtime_error(subkind, msg: msg, details: details)
|
55
39
|
when 'Operational'
|
56
40
|
operational_error(subkind, msg: msg, details: details)
|
57
|
-
when 'Parameter'
|
58
|
-
api_error(subkind, msg: msg, details: details)
|
59
41
|
when 'Validation'
|
60
42
|
validation_error(msg, details: details)
|
61
43
|
end
|
@@ -92,18 +74,14 @@ module Oso
|
|
92
74
|
# @param msg [String]
|
93
75
|
# @param details [Hash<String, Object>]
|
94
76
|
# @return [::Oso::Polar::PolarRuntimeError] the object converted into the expected format.
|
95
|
-
private_class_method def self.runtime_error(kind, msg:, details:)
|
77
|
+
private_class_method def self.runtime_error(kind, msg:, details:)
|
96
78
|
case kind
|
97
|
-
when 'Serialization'
|
98
|
-
::Oso::Polar::SerializationError.new(msg, details: details)
|
99
79
|
when 'Unsupported'
|
100
80
|
::Oso::Polar::UnsupportedError.new(msg, details: details)
|
101
81
|
when 'TypeError'
|
102
82
|
::Oso::Polar::PolarTypeError.new(msg, details: details)
|
103
83
|
when 'StackOverflow'
|
104
84
|
::Oso::Polar::StackOverflowError.new(msg, details: details)
|
105
|
-
when 'FileLoading'
|
106
|
-
::Oso::Polar::FileLoadingError.new(msg, details: details)
|
107
85
|
else
|
108
86
|
::Oso::Polar::PolarRuntimeError.new(msg, details: details)
|
109
87
|
end
|
@@ -124,21 +102,6 @@ module Oso
|
|
124
102
|
end
|
125
103
|
end
|
126
104
|
|
127
|
-
# Map FFI API errors into Ruby exceptions.
|
128
|
-
#
|
129
|
-
# @param kind [String]
|
130
|
-
# @param msg [String]
|
131
|
-
# @param details [Hash<String, Object>]
|
132
|
-
# @return [::Oso::Polar::ApiError] the object converted into the expected format.
|
133
|
-
private_class_method def self.api_error(kind, msg:, details:)
|
134
|
-
case kind
|
135
|
-
when 'Parameter'
|
136
|
-
::Oso::Polar::ParameterError.new(msg, details: details)
|
137
|
-
else
|
138
|
-
::Oso::Polar::ApiError.new(msg, details: details)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
105
|
# Map FFI Validation errors into Ruby exceptions.
|
143
106
|
#
|
144
107
|
# @param msg [String]
|
data/lib/oso/polar/ffi/polar.rb
CHANGED
@@ -6,7 +6,7 @@ module Oso
|
|
6
6
|
module Polar
|
7
7
|
module FFI
|
8
8
|
# Wrapper class for Polar FFI pointer + operations.
|
9
|
-
class Polar < ::FFI::AutoPointer
|
9
|
+
class Polar < ::FFI::AutoPointer # rubocop:disable Metrics/ClassLength
|
10
10
|
attr_accessor :enrich_message
|
11
11
|
|
12
12
|
Rust = Module.new do
|
@@ -14,21 +14,28 @@ module Oso
|
|
14
14
|
ffi_lib FFI::LIB_PATH
|
15
15
|
|
16
16
|
attach_function :new, :polar_new, [], FFI::Polar
|
17
|
-
attach_function :load, :polar_load, [FFI::Polar, :string],
|
18
|
-
attach_function :clear_rules, :polar_clear_rules, [FFI::Polar],
|
17
|
+
attach_function :load, :polar_load, [FFI::Polar, :string], CResultVoid
|
18
|
+
attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], CResultVoid
|
19
19
|
attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
|
20
20
|
attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
|
21
|
-
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32],
|
22
|
-
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32],
|
23
|
-
attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string],
|
24
|
-
attach_function :register_mro, :polar_register_mro, [FFI::Polar, :string, :string],
|
25
|
-
attach_function :next_message, :polar_next_polar_message, [FFI::Polar],
|
21
|
+
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], CResultQuery
|
22
|
+
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32], CResultQuery
|
23
|
+
attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string], CResultVoid
|
24
|
+
attach_function :register_mro, :polar_register_mro, [FFI::Polar, :string, :string], CResultVoid
|
25
|
+
attach_function :next_message, :polar_next_polar_message, [FFI::Polar], CResultString
|
26
26
|
attach_function :free, :polar_free, [FFI::Polar], :int32
|
27
|
+
attach_function :result_free, :result_free, [:pointer], :int32
|
27
28
|
attach_function(
|
28
29
|
:build_filter_plan,
|
29
30
|
:polar_build_filter_plan,
|
30
31
|
[FFI::Polar, :string, :string, :string, :string],
|
31
|
-
|
32
|
+
CResultString
|
33
|
+
)
|
34
|
+
attach_function(
|
35
|
+
:build_data_filter,
|
36
|
+
:polar_build_data_filter,
|
37
|
+
[FFI::Polar, :string, :string, :string, :string],
|
38
|
+
CResultString
|
32
39
|
)
|
33
40
|
end
|
34
41
|
private_constant :Rust
|
@@ -36,10 +43,7 @@ module Oso
|
|
36
43
|
# @return [FFI::Polar]
|
37
44
|
# @raise [FFI::Error] if the FFI call returns an error.
|
38
45
|
def self.create
|
39
|
-
|
40
|
-
handle_error if polar.null?
|
41
|
-
|
42
|
-
polar
|
46
|
+
Rust.new
|
43
47
|
end
|
44
48
|
|
45
49
|
def build_filter_plan(types, partials, variable, class_tag)
|
@@ -47,9 +51,19 @@ module Oso
|
|
47
51
|
partials = JSON.dump(partials)
|
48
52
|
plan = Rust.build_filter_plan(self, types, partials, variable, class_tag)
|
49
53
|
process_messages
|
50
|
-
|
54
|
+
plan = check_result plan
|
55
|
+
# TODO(gw) more error checking?
|
56
|
+
JSON.parse plan.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_data_filter(types, partials, variable, class_tag)
|
60
|
+
types = JSON.dump(types)
|
61
|
+
partials = JSON.dump(partials)
|
62
|
+
plan = Rust.build_data_filter(self, types, partials, variable, class_tag)
|
63
|
+
process_messages
|
64
|
+
plan = check_result plan
|
51
65
|
# TODO(gw) more error checking?
|
52
|
-
JSON.parse plan
|
66
|
+
JSON.parse plan.to_s
|
53
67
|
end
|
54
68
|
|
55
69
|
# @param sources [Array<Source>]
|
@@ -57,14 +71,14 @@ module Oso
|
|
57
71
|
def load(sources)
|
58
72
|
loaded = Rust.load(self, JSON.dump(sources))
|
59
73
|
process_messages
|
60
|
-
|
74
|
+
check_result loaded
|
61
75
|
end
|
62
76
|
|
63
77
|
# @raise [FFI::Error] if the FFI call returns an error.
|
64
78
|
def clear_rules
|
65
79
|
cleared = Rust.clear_rules(self)
|
66
80
|
process_messages
|
67
|
-
|
81
|
+
check_result cleared
|
68
82
|
end
|
69
83
|
|
70
84
|
# @return [FFI::Query] if there are remaining inline queries.
|
@@ -79,12 +93,7 @@ module Oso
|
|
79
93
|
# @return [Integer]
|
80
94
|
# @raise [FFI::Error] if the FFI call returns an error.
|
81
95
|
def new_id
|
82
|
-
|
83
|
-
# TODO(gj): I don't think this error check is correct. If getting a new ID fails on the
|
84
|
-
# Rust side, it'll probably surface as a panic (e.g., the KB lock is poisoned).
|
85
|
-
handle_error if id.zero?
|
86
|
-
|
87
|
-
id
|
96
|
+
Rust.new_id(self)
|
88
97
|
end
|
89
98
|
|
90
99
|
# @param str [String] Query string.
|
@@ -93,9 +102,7 @@ module Oso
|
|
93
102
|
def new_query_from_str(str)
|
94
103
|
query = Rust.new_query_from_str(self, str, 0)
|
95
104
|
process_messages
|
96
|
-
|
97
|
-
|
98
|
-
query
|
105
|
+
check_result query
|
99
106
|
end
|
100
107
|
|
101
108
|
# @param term [Hash<String, Object>]
|
@@ -104,9 +111,7 @@ module Oso
|
|
104
111
|
def new_query_from_term(term)
|
105
112
|
query = Rust.new_query_from_term(self, JSON.dump(term), 0)
|
106
113
|
process_messages
|
107
|
-
|
108
|
-
|
109
|
-
query
|
114
|
+
check_result query
|
110
115
|
end
|
111
116
|
|
112
117
|
# @param name [String]
|
@@ -114,7 +119,7 @@ module Oso
|
|
114
119
|
# @raise [FFI::Error] if the FFI call returns an error.
|
115
120
|
def register_constant(value, name:)
|
116
121
|
registered = Rust.register_constant(self, name, JSON.dump(value))
|
117
|
-
|
122
|
+
check_result registered
|
118
123
|
end
|
119
124
|
|
120
125
|
# @param name [String]
|
@@ -122,11 +127,24 @@ module Oso
|
|
122
127
|
# @raise [FFI::Error] if the FFI call returns an error.
|
123
128
|
def register_mro(name, mro)
|
124
129
|
registered = Rust.register_mro(self, name, JSON.dump(mro))
|
125
|
-
|
130
|
+
check_result registered
|
126
131
|
end
|
127
132
|
|
128
133
|
def next_message
|
129
|
-
Rust.next_message(self)
|
134
|
+
check_result Rust.next_message(self)
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_message(message, enrich_message)
|
138
|
+
message = JSON.parse(message.to_s)
|
139
|
+
kind = message['kind']
|
140
|
+
msg = enrich_message.call(message['msg'])
|
141
|
+
|
142
|
+
case kind
|
143
|
+
when 'Print'
|
144
|
+
puts(msg)
|
145
|
+
when 'Warning'
|
146
|
+
warn(format('[warning] %<msg>s', msg: msg))
|
147
|
+
end
|
130
148
|
end
|
131
149
|
|
132
150
|
def process_messages
|
@@ -134,12 +152,19 @@ module Oso
|
|
134
152
|
message = next_message
|
135
153
|
break if message.null?
|
136
154
|
|
137
|
-
message
|
155
|
+
process_message(message, enrich_message)
|
138
156
|
end
|
139
157
|
end
|
140
158
|
|
141
|
-
def
|
142
|
-
|
159
|
+
def check_result(res)
|
160
|
+
result = res[:result]
|
161
|
+
error = res[:error]
|
162
|
+
Rust.result_free(res)
|
163
|
+
|
164
|
+
raise 'internal error: both result and error pointers are not null' if !error.null? && !result.zero?
|
165
|
+
raise FFI::Error.get(error, enrich_message) unless error.null?
|
166
|
+
|
167
|
+
result
|
143
168
|
end
|
144
169
|
end
|
145
170
|
end
|
data/lib/oso/polar/ffi/query.rb
CHANGED
@@ -13,15 +13,16 @@ module Oso
|
|
13
13
|
extend ::FFI::Library
|
14
14
|
ffi_lib FFI::LIB_PATH
|
15
15
|
|
16
|
-
attach_function :debug_command, :polar_debug_command, [FFI::Query, :string],
|
17
|
-
attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string],
|
18
|
-
attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32],
|
19
|
-
attach_function :application_error, :polar_application_error, [FFI::Query, :string],
|
20
|
-
attach_function :next_event, :polar_next_query_event, [FFI::Query],
|
21
|
-
attach_function :next_message, :polar_next_query_message, [FFI::Query],
|
22
|
-
attach_function :source, :polar_query_source_info, [FFI::Query],
|
16
|
+
attach_function :debug_command, :polar_debug_command, [FFI::Query, :string], CResultVoid
|
17
|
+
attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string], CResultVoid
|
18
|
+
attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32], CResultVoid
|
19
|
+
attach_function :application_error, :polar_application_error, [FFI::Query, :string], CResultVoid
|
20
|
+
attach_function :next_event, :polar_next_query_event, [FFI::Query], CResultString
|
21
|
+
attach_function :next_message, :polar_next_query_message, [FFI::Query], CResultString
|
22
|
+
attach_function :source, :polar_query_source_info, [FFI::Query], CResultString
|
23
23
|
attach_function :free, :query_free, [FFI::Query], :int32
|
24
|
-
attach_function :
|
24
|
+
attach_function :result_free, :result_free, [:pointer], :int32
|
25
|
+
attach_function :bind, :polar_bind, [FFI::Query, :string, :string], CResultVoid
|
25
26
|
end
|
26
27
|
private_constant :Rust
|
27
28
|
|
@@ -30,15 +31,15 @@ module Oso
|
|
30
31
|
def debug_command(cmd)
|
31
32
|
res = Rust.debug_command(self, cmd)
|
32
33
|
process_messages
|
33
|
-
|
34
|
+
check_result res
|
34
35
|
end
|
35
36
|
|
36
|
-
# @param
|
37
|
+
# @param value [Object]
|
37
38
|
# @param call_id [Integer]
|
38
39
|
# @raise [FFI::Error] if the FFI call returns an error.
|
39
|
-
def call_result(
|
40
|
-
res = Rust.call_result(self, call_id,
|
41
|
-
|
40
|
+
def call_result(value, call_id:)
|
41
|
+
res = Rust.call_result(self, call_id, JSON.dump(value))
|
42
|
+
check_result res
|
42
43
|
end
|
43
44
|
|
44
45
|
# @param result [Boolean]
|
@@ -47,14 +48,14 @@ module Oso
|
|
47
48
|
def question_result(result, call_id:)
|
48
49
|
result = result ? 1 : 0
|
49
50
|
res = Rust.question_result(self, call_id, result)
|
50
|
-
|
51
|
+
check_result res
|
51
52
|
end
|
52
53
|
|
53
54
|
# @param message [String]
|
54
55
|
# @raise [FFI::Error] if the FFI call returns an error.
|
55
56
|
def application_error(message)
|
56
57
|
res = Rust.application_error(self, message)
|
57
|
-
|
58
|
+
check_result res
|
58
59
|
end
|
59
60
|
|
60
61
|
# @return [::Oso::Polar::QueryEvent]
|
@@ -62,18 +63,32 @@ module Oso
|
|
62
63
|
def next_event
|
63
64
|
event = Rust.next_event(self)
|
64
65
|
process_messages
|
65
|
-
|
66
|
+
event = check_result event
|
66
67
|
|
67
68
|
::Oso::Polar::QueryEvent.new(JSON.parse(event.to_s))
|
68
69
|
end
|
69
70
|
|
70
71
|
def bind(name, value)
|
71
72
|
res = Rust.bind(self, name, JSON.dump(value))
|
72
|
-
|
73
|
+
check_result res
|
73
74
|
end
|
74
75
|
|
75
76
|
def next_message
|
76
|
-
Rust.next_message(self)
|
77
|
+
check_result Rust.next_message(self)
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_message(message, enrich_message)
|
81
|
+
message = JSON.parse(message.to_s)
|
82
|
+
kind = message['kind']
|
83
|
+
msg = message['msg']
|
84
|
+
msg = enrich_message.call(msg)
|
85
|
+
|
86
|
+
case kind
|
87
|
+
when 'Print'
|
88
|
+
puts(msg)
|
89
|
+
when 'Warning'
|
90
|
+
warn(format('[warning] %<msg>s', msg: msg))
|
91
|
+
end
|
77
92
|
end
|
78
93
|
|
79
94
|
def process_messages
|
@@ -81,7 +96,7 @@ module Oso
|
|
81
96
|
message = next_message
|
82
97
|
break if message.null?
|
83
98
|
|
84
|
-
message
|
99
|
+
process_message(message, enrich_message)
|
85
100
|
end
|
86
101
|
end
|
87
102
|
|
@@ -89,13 +104,24 @@ module Oso
|
|
89
104
|
# @raise [FFI::Error] if the FFI call returns an error.
|
90
105
|
def source
|
91
106
|
res = Rust.source(self)
|
92
|
-
|
107
|
+
res = check_result res
|
93
108
|
|
94
109
|
res.to_s
|
95
110
|
end
|
96
111
|
|
97
|
-
|
98
|
-
|
112
|
+
# Unwrap the result by (a) extracting the pointers for
|
113
|
+
# result and error, (b) freeing the result pointers, and then
|
114
|
+
# (c) either returning the result pointer, or constructing and
|
115
|
+
# raising the error.
|
116
|
+
def check_result(res)
|
117
|
+
result = res[:result]
|
118
|
+
error = res[:error]
|
119
|
+
Rust.result_free(res)
|
120
|
+
|
121
|
+
raise 'internal error: both result and error pointers are not null' if !error.null? && !result.zero?
|
122
|
+
raise FFI::Error.get(error, enrich_message) unless error.null?
|
123
|
+
|
124
|
+
result
|
99
125
|
end
|
100
126
|
end
|
101
127
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
module FFI
|
6
|
-
# Wrapper class for
|
7
|
-
|
8
|
+
# Wrapper class for Rust strings.
|
9
|
+
#
|
10
|
+
# Since we force all strings to go through this
|
11
|
+
# the `AutoPointer` class will handle
|
12
|
+
# actually freeing the string when deleting it
|
13
|
+
class RustString < ::FFI::AutoPointer
|
8
14
|
# @return [String]
|
9
15
|
def to_s
|
10
16
|
@to_s ||= read_string.force_encoding('UTF-8')
|
@@ -14,7 +20,7 @@ module Oso
|
|
14
20
|
extend ::FFI::Library
|
15
21
|
ffi_lib FFI::LIB_PATH
|
16
22
|
|
17
|
-
attach_function :free, :string_free, [
|
23
|
+
attach_function :free, :string_free, [RustString], :int32
|
18
24
|
end
|
19
25
|
|
20
26
|
private_constant :Rust
|
data/lib/oso/polar/ffi.rb
CHANGED
@@ -4,6 +4,7 @@ require 'ffi'
|
|
4
4
|
|
5
5
|
module Oso
|
6
6
|
module Polar
|
7
|
+
# FFI classes shared between all ffi/*.rb modules
|
7
8
|
module FFI
|
8
9
|
LIB = "#{::FFI::Platform::LIBPREFIX}polar.#{::FFI::Platform::LIBSUFFIX}"
|
9
10
|
RELEASE_PATH = File.expand_path(File.join(__dir__, "../../../ext/oso-oso/lib/#{LIB}"))
|
@@ -18,41 +19,50 @@ module Oso
|
|
18
19
|
|
19
20
|
# Wrapper class for Polar FFI pointer + operations.
|
20
21
|
class Polar < ::FFI::AutoPointer
|
22
|
+
def zero?
|
23
|
+
null?
|
24
|
+
end
|
25
|
+
|
21
26
|
def self.release(ptr)
|
22
27
|
Rust.free(ptr) unless ptr.null?
|
23
28
|
end
|
24
29
|
end
|
25
30
|
# Wrapper class for Query FFI pointer + operations.
|
26
31
|
class Query < ::FFI::AutoPointer
|
27
|
-
def
|
28
|
-
|
32
|
+
def zero?
|
33
|
+
null?
|
29
34
|
end
|
30
|
-
|
31
|
-
# Wrapper class for QueryEvent FFI pointer + operations.
|
32
|
-
class QueryEvent < ::FFI::AutoPointer
|
35
|
+
|
33
36
|
def self.release(ptr)
|
34
37
|
Rust.free(ptr) unless ptr.null?
|
35
38
|
end
|
36
39
|
end
|
37
|
-
|
38
|
-
class
|
39
|
-
|
40
|
-
|
40
|
+
|
41
|
+
# Wrapper class for Rust strings FFI pointer + operations.
|
42
|
+
class RustString < ::FFI::AutoPointer
|
43
|
+
def zero?
|
44
|
+
null?
|
41
45
|
end
|
42
|
-
|
43
|
-
# Wrapper class for Message FFI pointer + operations.
|
44
|
-
class Message < ::FFI::AutoPointer
|
46
|
+
|
45
47
|
def self.release(ptr)
|
46
48
|
Rust.free(ptr) unless ptr.null?
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
# Helper method to generate a Result type for different
|
53
|
+
# inner types
|
54
|
+
def self.result(result_klass)
|
55
|
+
Class.new(::FFI::Struct) do
|
56
|
+
layout :result, result_klass, :error, RustString
|
57
|
+
end.by_ref
|
55
58
|
end
|
59
|
+
|
60
|
+
# Defines the result type version of
|
61
|
+
# each of these structs
|
62
|
+
# result(T) => { result: T, error: string }
|
63
|
+
CResultVoid = result(:int)
|
64
|
+
CResultString = result(RustString)
|
65
|
+
CResultQuery = result(Query)
|
56
66
|
end
|
57
67
|
private_constant :FFI
|
58
68
|
end
|
@@ -60,7 +70,5 @@ end
|
|
60
70
|
|
61
71
|
require 'oso/polar/ffi/polar'
|
62
72
|
require 'oso/polar/ffi/query'
|
63
|
-
require 'oso/polar/ffi/query_event'
|
64
73
|
require 'oso/polar/ffi/error'
|
65
|
-
require 'oso/polar/ffi/
|
66
|
-
require 'oso/polar/ffi/source'
|
74
|
+
require 'oso/polar/ffi/rust_string'
|
data/lib/oso/polar/host.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'data'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
# Ruby code reloaders (i.e. the one used by rails) swap out the value of
|
@@ -67,7 +69,7 @@ module Oso
|
|
67
69
|
public
|
68
70
|
|
69
71
|
attr_writer :accept_expression
|
70
|
-
attr_accessor :build_query, :combine_query, :exec_query
|
72
|
+
attr_accessor :build_query, :combine_query, :exec_query, :adapter
|
71
73
|
|
72
74
|
DEFAULT_COMBINE_QUERY = proc { raise 'implement combine_query to use data filtering' }
|
73
75
|
DEFAULT_BUILD_QUERY = proc { raise 'implement build_query to use data filtering' }
|
@@ -95,9 +97,10 @@ module Oso
|
|
95
97
|
# @return [Class]
|
96
98
|
# @raise [UnregisteredClassError] if the class has not been registered.
|
97
99
|
def get_class(name)
|
98
|
-
|
100
|
+
typ = types[name]
|
101
|
+
raise UnregisteredClassError, name if typ.nil?
|
99
102
|
|
100
|
-
|
103
|
+
typ.klass.get
|
101
104
|
end
|
102
105
|
|
103
106
|
# Store a Ruby class in the {#types} cache.
|
@@ -317,7 +320,18 @@ module Oso
|
|
317
320
|
else
|
318
321
|
instance_id = nil
|
319
322
|
instance_id = types[value].id if value.is_a?(Class) && types.key?(value)
|
320
|
-
|
323
|
+
|
324
|
+
# only pass class_repr for registered types
|
325
|
+
class_repr = nil
|
326
|
+
class_repr = value.class.to_s if types.key?(value.class)
|
327
|
+
|
328
|
+
{
|
329
|
+
'ExternalInstance' => {
|
330
|
+
'instance_id' => cache_instance(value, id: instance_id),
|
331
|
+
'repr' => nil,
|
332
|
+
'class_repr' => class_repr
|
333
|
+
}
|
334
|
+
}
|
321
335
|
end
|
322
336
|
{ 'value' => value }
|
323
337
|
end
|
@@ -337,21 +351,25 @@ module Oso
|
|
337
351
|
value
|
338
352
|
when 'Number'
|
339
353
|
num = value.values.first
|
340
|
-
|
354
|
+
case value.keys.first
|
355
|
+
when 'Float'
|
341
356
|
case num
|
342
357
|
when 'Infinity'
|
343
|
-
|
358
|
+
Float::INFINITY
|
344
359
|
when '-Infinity'
|
345
|
-
|
360
|
+
-Float::INFINITY
|
346
361
|
when 'NaN'
|
347
|
-
|
362
|
+
Float::NAN
|
348
363
|
else
|
349
364
|
unless value['Float'].is_a? Float # rubocop:disable Metrics/BlockNesting
|
350
365
|
raise PolarRuntimeError, "Expected a floating point number, got \"#{value['Float']}\""
|
351
366
|
end
|
367
|
+
|
368
|
+
num
|
352
369
|
end
|
370
|
+
else
|
371
|
+
num
|
353
372
|
end
|
354
|
-
num
|
355
373
|
when 'List'
|
356
374
|
value.map { |el| to_ruby(el) }
|
357
375
|
when 'Dictionary'
|
@@ -389,6 +407,10 @@ module Oso
|
|
389
407
|
get_instance(Regexp.last_match[1].to_i).to_s
|
390
408
|
end
|
391
409
|
end
|
410
|
+
|
411
|
+
def use_new_data_filtering?
|
412
|
+
!adapter.nil?
|
413
|
+
end
|
392
414
|
end
|
393
415
|
end
|
394
416
|
end
|
data/lib/oso/polar/polar.rb
CHANGED
@@ -84,20 +84,8 @@ module Oso
|
|
84
84
|
@ffi_polar
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
def get_class_name(klass) # rubocop:disable Metrics/AbcSize
|
90
|
-
if host.types.key? klass
|
91
|
-
host.types[klass].name
|
92
|
-
elsif host.types.key? klass.name
|
93
|
-
host.types[klass.name].name
|
94
|
-
else
|
95
|
-
rec = host.types.values.find { |v| v.klass.get == klass }
|
96
|
-
raise "Unknown class `#{klass}`" if rec.nil?
|
97
|
-
|
98
|
-
host.types[klass] = rec
|
99
|
-
rec.name
|
100
|
-
end
|
87
|
+
def name_to_class(class_name)
|
88
|
+
host.types[class_name].klass.get
|
101
89
|
end
|
102
90
|
|
103
91
|
# Clear all rules and rule sources from the current Polar instance
|
@@ -259,10 +247,89 @@ module Oso
|
|
259
247
|
|
260
248
|
private
|
261
249
|
|
250
|
+
# new/old data filtering core API shared logic
|
251
|
+
def partial_query(actor, action, resource_cls) # rubocop:disable Metrics/MethodLength
|
252
|
+
var_name = 'resource'
|
253
|
+
resource = Variable.new var_name
|
254
|
+
|
255
|
+
partials = query_rule(
|
256
|
+
'allow',
|
257
|
+
actor,
|
258
|
+
action,
|
259
|
+
resource,
|
260
|
+
bindings: { var_name => type_constraint(resource, resource_cls) },
|
261
|
+
accept_expression: true
|
262
|
+
)
|
263
|
+
|
264
|
+
partials.each_with_object([]) do |result, out|
|
265
|
+
result.each do |key, val|
|
266
|
+
out.push prefilter_isas(key, val)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def new_authorized_query(actor, action, resource_class)
|
272
|
+
partials = partial_query(actor, action, resource_class)
|
273
|
+
types = host.serialize_types
|
274
|
+
class_name = class_to_name resource_class
|
275
|
+
plan = ffi.build_data_filter(types, partials, 'resource', class_name)
|
276
|
+
filter = ::Oso::Polar::Data::Filter.parse(self, plan)
|
277
|
+
host.adapter.build_query filter
|
278
|
+
end
|
279
|
+
|
280
|
+
def old_authorized_query(actor, action, resource_cls)
|
281
|
+
results = partial_query(actor, action, resource_cls)
|
282
|
+
::Oso::Polar::DataFiltering::FilterPlan
|
283
|
+
.parse(self, results, class_to_name(resource_cls))
|
284
|
+
.build_query
|
285
|
+
end
|
286
|
+
|
287
|
+
# handle Isa constraints in a partial query
|
288
|
+
def prefilter_isas(key, val) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
289
|
+
# this will usually be the case! sometimes not, if it's an instance.
|
290
|
+
if val.is_a?(Expression) && val.operator == 'And'
|
291
|
+
# get the isas
|
292
|
+
isas, othas = val.args.partition do |expr|
|
293
|
+
expr.operator == 'Isa' &&
|
294
|
+
expr.args[1].is_a?(Pattern) &&
|
295
|
+
expr.args[1].fields.empty?
|
296
|
+
end
|
297
|
+
|
298
|
+
# drop all the isas we can verify now, keep everything else
|
299
|
+
othas += isas.reject do |isa|
|
300
|
+
isa.args[0].is_a? name_to_class isa.args[1].tag
|
301
|
+
end
|
302
|
+
|
303
|
+
# TODO(gw) check the rest of them instead of just adding them?
|
304
|
+
val.args = othas
|
305
|
+
end
|
306
|
+
val = host.to_polar val
|
307
|
+
{ 'bindings' => { key => val } }
|
308
|
+
end
|
309
|
+
|
310
|
+
# get the (maybe user-supplied) name of a class.
|
311
|
+
# kind of a hack because of class autoreloading.
|
312
|
+
def class_to_name(klass) # rubocop:disable Metrics/AbcSize
|
313
|
+
if (rec = host.types[klass]) || (rec = host.types[klass.name])
|
314
|
+
rec.name
|
315
|
+
elsif (rec = host.types.values.find { |v| v.klass.get == klass })
|
316
|
+
host.types[klass] = rec
|
317
|
+
rec.name
|
318
|
+
else
|
319
|
+
raise NameError, "Unknown class `#{klass}`"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def try_class_to_name(klass)
|
324
|
+
class_to_name klass
|
325
|
+
rescue NameError
|
326
|
+
nil
|
327
|
+
end
|
328
|
+
|
262
329
|
def type_constraint(var, cls)
|
263
330
|
Expression.new(
|
264
331
|
'And',
|
265
|
-
[Expression.new('Isa', [var, Pattern.new(
|
332
|
+
[Expression.new('Isa', [var, Pattern.new(class_to_name(cls), {})])]
|
266
333
|
)
|
267
334
|
end
|
268
335
|
|
data/lib/oso/polar/query.rb
CHANGED
@@ -94,8 +94,7 @@ module Oso
|
|
94
94
|
else
|
95
95
|
instance.__send__(attribute, *args, **kwargs)
|
96
96
|
end
|
97
|
-
|
98
|
-
call_result(result, call_id: call_id)
|
97
|
+
call_result(host.to_polar(result), call_id: call_id)
|
99
98
|
rescue ArgumentError, NoMethodError => e
|
100
99
|
application_error(e.message)
|
101
100
|
call_result(nil, call_id: call_id)
|
@@ -143,7 +142,7 @@ module Oso
|
|
143
142
|
calls[call_id] = value.lazy
|
144
143
|
end
|
145
144
|
|
146
|
-
result =
|
145
|
+
result = next_call_result(call_id)
|
147
146
|
call_result(result, call_id: call_id)
|
148
147
|
rescue StopIteration
|
149
148
|
call_result(nil, call_id: call_id)
|
@@ -245,12 +244,32 @@ module Oso
|
|
245
244
|
|
246
245
|
def handle_relationship(call_id, instance, rel) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
247
246
|
typ = host.types[rel.other_type]
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
247
|
+
|
248
|
+
if host.use_new_data_filtering?
|
249
|
+
cls = typ.klass.get
|
250
|
+
|
251
|
+
condition = ::Oso::Polar::Data::Filter::Condition.new(
|
252
|
+
::Oso::Polar::Data::Filter::Projection.new(cls, rel.other_field),
|
253
|
+
'Eq',
|
254
|
+
instance.send(rel.my_field)
|
255
|
+
)
|
256
|
+
|
257
|
+
filter = ::Oso::Polar::Data::Filter.new(
|
258
|
+
model: cls,
|
259
|
+
relations: [],
|
260
|
+
conditions: [[condition]],
|
261
|
+
types: host.types
|
262
|
+
)
|
263
|
+
|
264
|
+
res = host.adapter.execute_query host.adapter.build_query(filter)
|
265
|
+
else
|
266
|
+
constraint = ::Oso::Polar::DataFiltering::Filter.new(
|
267
|
+
kind: 'Eq',
|
268
|
+
field: rel.other_field,
|
269
|
+
value: instance.send(rel.my_field)
|
270
|
+
)
|
271
|
+
res = typ.exec_query[typ.build_query[[constraint]]]
|
272
|
+
end
|
254
273
|
|
255
274
|
if rel.kind == 'one'
|
256
275
|
raise "multiple parents: #{res}" unless res.length == 1
|
@@ -258,8 +277,7 @@ module Oso
|
|
258
277
|
res = res[0]
|
259
278
|
end
|
260
279
|
|
261
|
-
|
262
|
-
call_result(res, call_id: call_id)
|
280
|
+
call_result(host.to_polar(res), call_id: call_id)
|
263
281
|
end
|
264
282
|
end
|
265
283
|
end
|
data/lib/oso/polar.rb
CHANGED
data/lib/oso/version.rb
CHANGED
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.
|
4
|
+
version: 0.25.0
|
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-
|
11
|
+
date: 2021-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -162,16 +162,18 @@ files:
|
|
162
162
|
- lib/oso/errors.rb
|
163
163
|
- lib/oso/oso.rb
|
164
164
|
- lib/oso/polar.rb
|
165
|
+
- lib/oso/polar/data.rb
|
166
|
+
- lib/oso/polar/data/adapter.rb
|
167
|
+
- lib/oso/polar/data/adapter/active_record_adapter.rb
|
168
|
+
- lib/oso/polar/data/filter.rb
|
165
169
|
- lib/oso/polar/data_filtering.rb
|
166
170
|
- lib/oso/polar/errors.rb
|
167
171
|
- lib/oso/polar/expression.rb
|
168
172
|
- lib/oso/polar/ffi.rb
|
169
173
|
- lib/oso/polar/ffi/error.rb
|
170
|
-
- lib/oso/polar/ffi/message.rb
|
171
174
|
- lib/oso/polar/ffi/polar.rb
|
172
175
|
- lib/oso/polar/ffi/query.rb
|
173
|
-
- lib/oso/polar/ffi/
|
174
|
-
- lib/oso/polar/ffi/source.rb
|
176
|
+
- lib/oso/polar/ffi/rust_string.rb
|
175
177
|
- lib/oso/polar/host.rb
|
176
178
|
- lib/oso/polar/pattern.rb
|
177
179
|
- lib/oso/polar/polar.rb
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Oso
|
6
|
-
module Polar
|
7
|
-
module FFI
|
8
|
-
# Wrapper class for Message FFI pointer + operations.
|
9
|
-
class Message < ::FFI::AutoPointer
|
10
|
-
# @return [String]
|
11
|
-
def to_s
|
12
|
-
@to_s ||= read_string.force_encoding('UTF-8')
|
13
|
-
end
|
14
|
-
|
15
|
-
Rust = Module.new do
|
16
|
-
extend ::FFI::Library
|
17
|
-
ffi_lib FFI::LIB_PATH
|
18
|
-
|
19
|
-
attach_function :free, :string_free, [Message], :int32
|
20
|
-
end
|
21
|
-
|
22
|
-
def process(enrich_message)
|
23
|
-
message = JSON.parse(to_s)
|
24
|
-
kind = message['kind']
|
25
|
-
msg = message['msg']
|
26
|
-
msg = enrich_message.call(msg)
|
27
|
-
|
28
|
-
case kind
|
29
|
-
when 'Print'
|
30
|
-
puts(msg)
|
31
|
-
when 'Warning'
|
32
|
-
warn(format('[warning] %<msg>s', msg: msg))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private_constant :Rust
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Oso
|
4
|
-
module Polar
|
5
|
-
module FFI
|
6
|
-
# Wrapper class for QueryEvent FFI pointer + operations.
|
7
|
-
class QueryEvent < ::FFI::AutoPointer
|
8
|
-
# @return [String]
|
9
|
-
def to_s
|
10
|
-
@to_s ||= read_string.force_encoding('UTF-8')
|
11
|
-
end
|
12
|
-
|
13
|
-
Rust = Module.new do
|
14
|
-
extend ::FFI::Library
|
15
|
-
ffi_lib FFI::LIB_PATH
|
16
|
-
|
17
|
-
attach_function :free, :string_free, [FFI::QueryEvent], :int32
|
18
|
-
end
|
19
|
-
private_constant :Rust
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|