oso-oso 0.2.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +14 -13
- data/Makefile +10 -1
- data/README.md +6 -9
- data/Rakefile +0 -1
- data/bin/oso +7 -0
- 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/http.rb +1 -1
- data/lib/oso/oso.rb +14 -33
- data/lib/oso/path_mapper.rb +1 -1
- data/lib/oso/polar.rb +1 -0
- data/lib/oso/polar/errors.rb +25 -9
- data/lib/oso/polar/ffi.rb +14 -5
- data/lib/oso/polar/ffi/error.rb +2 -2
- data/lib/oso/polar/ffi/message.rb +37 -0
- data/lib/oso/polar/ffi/polar.rb +28 -18
- data/lib/oso/polar/ffi/query.rb +25 -0
- data/lib/oso/polar/host.rb +248 -0
- data/lib/oso/polar/polar.rb +171 -305
- data/lib/oso/polar/query.rb +99 -39
- data/lib/oso/version.rb +1 -1
- data/oso-oso.gemspec +10 -7
- metadata +30 -11
- data/bin/console +0 -33
- data/bin/setup +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09ee09c3366406a34633c870ddfe4ff271971529'
|
4
|
+
data.tar.gz: 764b0f3b440d567e19e45b71eff823b9000f35ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdae19c2528c46a85004241f0a8e59639123e416724239670cb9ff2ffc1172a87bb116b481d575e466c74f634213abdced263211f991721f1ff990e1e085d884
|
7
|
+
data.tar.gz: 309a196e2a0a3725e3bc1f45be8382ed85026dc7fc271662efaef8e207b0df2891f26da48ece702f1d9ce3e3731ea52c0a6cea30bf50af6acde54f08b44d79ec
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
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.5.0)
|
5
5
|
ffi (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -12,18 +12,18 @@ GEM
|
|
12
12
|
benchmark (0.1.0)
|
13
13
|
byebug (11.1.3)
|
14
14
|
coderay (1.1.3)
|
15
|
-
diff-lcs (1.
|
15
|
+
diff-lcs (1.4.4)
|
16
16
|
e2mmap (0.1.0)
|
17
17
|
ffi (1.13.1)
|
18
18
|
jaro_winkler (1.5.4)
|
19
19
|
maruku (0.7.3)
|
20
20
|
method_source (1.0.0)
|
21
21
|
mini_portile2 (2.4.0)
|
22
|
-
nokogiri (1.10.
|
22
|
+
nokogiri (1.10.10)
|
23
23
|
mini_portile2 (~> 2.4.0)
|
24
|
-
parallel (1.19.
|
25
|
-
parser (2.7.1.
|
26
|
-
ast (~> 2.4.
|
24
|
+
parallel (1.19.2)
|
25
|
+
parser (2.7.1.4)
|
26
|
+
ast (~> 2.4.1)
|
27
27
|
pry (0.13.1)
|
28
28
|
coderay (~> 1.1)
|
29
29
|
method_source (~> 1.0)
|
@@ -49,19 +49,19 @@ GEM
|
|
49
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
50
50
|
rspec-support (~> 3.9.0)
|
51
51
|
rspec-support (3.9.3)
|
52
|
-
rubocop (0.
|
52
|
+
rubocop (0.89.1)
|
53
53
|
parallel (~> 1.10)
|
54
|
-
parser (>= 2.7.
|
54
|
+
parser (>= 2.7.1.1)
|
55
55
|
rainbow (>= 2.2.2, < 4.0)
|
56
56
|
regexp_parser (>= 1.7)
|
57
57
|
rexml
|
58
|
-
rubocop-ast (>= 0.0.
|
58
|
+
rubocop-ast (>= 0.3.0, < 1.0)
|
59
59
|
ruby-progressbar (~> 1.7)
|
60
60
|
unicode-display_width (>= 1.4.0, < 2.0)
|
61
|
-
rubocop-ast (0.0
|
62
|
-
parser (>= 2.7.
|
61
|
+
rubocop-ast (0.3.0)
|
62
|
+
parser (>= 2.7.1.4)
|
63
63
|
ruby-progressbar (1.10.1)
|
64
|
-
solargraph (0.39.
|
64
|
+
solargraph (0.39.14)
|
65
65
|
backport (~> 1.1)
|
66
66
|
benchmark
|
67
67
|
bundler (>= 1.17.2)
|
@@ -88,7 +88,8 @@ DEPENDENCIES
|
|
88
88
|
pry-byebug (~> 3.9.0)
|
89
89
|
rake (~> 12.0)
|
90
90
|
rspec (~> 3.0)
|
91
|
-
|
91
|
+
rubocop (~> 0.89.1)
|
92
|
+
solargraph (~> 0.39.14)
|
92
93
|
yard (~> 0.9.25)
|
93
94
|
|
94
95
|
BUNDLED WITH
|
data/Makefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
.PHONY: install
|
1
|
+
.PHONY: rust install test lint typecheck repl
|
2
2
|
|
3
3
|
rust:
|
4
4
|
$(MAKE) -C ../.. rust-build
|
@@ -8,3 +8,12 @@ install: rust
|
|
8
8
|
|
9
9
|
test: install
|
10
10
|
bundle exec rake spec
|
11
|
+
|
12
|
+
lint:
|
13
|
+
bundle exec rubocop
|
14
|
+
|
15
|
+
typecheck:
|
16
|
+
bundle exec solargraph typecheck
|
17
|
+
|
18
|
+
repl: install
|
19
|
+
bundle exec oso
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# oso-oso
|
2
2
|
|
3
3
|
## Installation
|
4
4
|
|
@@ -18,13 +18,10 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Development
|
20
20
|
|
21
|
-
After checking out the repo, run `
|
22
|
-
`rake spec` to run the tests. You can also run `
|
23
|
-
|
21
|
+
After checking out the repo, run `bundle install` to install dependencies.
|
22
|
+
Then, run `bundle exec rake spec` to run the tests. You can also run `bundle
|
23
|
+
exec oso` for an interactive REPL that will allow you to experiment.
|
24
24
|
|
25
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
26
|
-
release a new version, update the version number in `version.rb`, and then run
|
27
|
-
`bundle exec rake release`, which will create a git tag for the version, push
|
28
|
-
git commits and tags, and push the `.gem` file to
|
29
|
-
[rubygems.org](https://rubygems.org).
|
25
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
30
26
|
|
27
|
+
New releases are minted and pushed to RubyGems via GitHub Actions workflows.
|
data/Rakefile
CHANGED
data/bin/oso
ADDED
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/http.rb
CHANGED
data/lib/oso/oso.rb
CHANGED
@@ -1,47 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'polar/polar'
|
4
|
+
|
3
5
|
module Oso
|
4
|
-
#
|
5
|
-
class Oso
|
6
|
+
# oso authorization API.
|
7
|
+
class Oso < Polar::Polar
|
6
8
|
def initialize
|
7
|
-
|
9
|
+
super
|
8
10
|
register_class(Http, name: 'Http')
|
9
11
|
register_class(PathMapper, name: 'PathMapper')
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if block_given?
|
22
|
-
polar.register_class(cls, name: name, from_polar: Proc.new)
|
23
|
-
else
|
24
|
-
polar.register_class(cls, name: name)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def allow(actor:, action:, resource:)
|
29
|
-
polar.query_pred('allow', args: [actor, action, resource]).next
|
14
|
+
# Query the knowledge base to determine whether an actor is allowed to
|
15
|
+
# perform an action upon a resource.
|
16
|
+
#
|
17
|
+
# @param actor [Object] Subject.
|
18
|
+
# @param action [Object] Verb.
|
19
|
+
# @param resource [Object] Object.
|
20
|
+
# @return [Boolean] An access control decision.
|
21
|
+
def allowed?(actor:, action:, resource:)
|
22
|
+
query_rule('allow', actor, action, resource).next
|
30
23
|
true
|
31
24
|
rescue StopIteration
|
32
25
|
false
|
33
26
|
end
|
34
|
-
|
35
|
-
def query_predicate(name, *args)
|
36
|
-
polar.query_pred(name, args: args)
|
37
|
-
end
|
38
|
-
|
39
|
-
def load_queued_files
|
40
|
-
polar.load_queued_files
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
attr_reader :polar
|
46
27
|
end
|
47
28
|
end
|
data/lib/oso/path_mapper.rb
CHANGED
@@ -4,7 +4,7 @@ module Oso
|
|
4
4
|
# Map from a template string with capture groups of the form
|
5
5
|
# `{name}` to a dictionary of the form `{name: captured_value}`
|
6
6
|
class PathMapper
|
7
|
-
def initialize(template
|
7
|
+
def initialize(template)
|
8
8
|
capture_group = /({([^}]+)})/
|
9
9
|
|
10
10
|
template = template.dup
|
data/lib/oso/polar.rb
CHANGED
data/lib/oso/polar/errors.rb
CHANGED
@@ -5,16 +5,12 @@ module Oso
|
|
5
5
|
# Base error type for Oso::Polar.
|
6
6
|
class Error < ::RuntimeError
|
7
7
|
attr_reader :stack_trace
|
8
|
-
|
8
|
+
|
9
9
|
# @param message [String]
|
10
10
|
# @param details [Hash]
|
11
11
|
def initialize(message = nil, details: nil)
|
12
12
|
@details = details
|
13
|
-
|
14
|
-
@stack_trace = details['stack_trace']
|
15
|
-
else
|
16
|
-
@stack_trace = nil
|
17
|
-
end
|
13
|
+
@stack_trace = details&.fetch('stack_trace', nil)
|
18
14
|
super(message)
|
19
15
|
end
|
20
16
|
end
|
@@ -40,12 +36,32 @@ module Oso
|
|
40
36
|
class DuplicateInstanceRegistrationError < PolarRuntimeError; end
|
41
37
|
class InvalidCallError < PolarRuntimeError; end
|
42
38
|
class InvalidConstructorError < PolarRuntimeError; end
|
39
|
+
class InvalidQueryTypeError < PolarRuntimeError; end
|
43
40
|
class InlineQueryFailedError < PolarRuntimeError; end
|
44
41
|
class NullByteInPolarFileError < PolarRuntimeError; end
|
45
42
|
class UnexpectedPolarTypeError < PolarRuntimeError; end
|
43
|
+
class PolarFileAlreadyLoadedError < PolarRuntimeError # rubocop:disable Style/Documentation
|
44
|
+
# @param file [String]
|
45
|
+
def initialize(file)
|
46
|
+
super("File #{file} has already been loaded.")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
class PolarFileContentsChangedError < PolarRuntimeError # rubocop:disable Style/Documentation
|
50
|
+
# @param file [String]
|
51
|
+
def initialize(file)
|
52
|
+
super("A file with the name #{file}, but different contents, has already been loaded.")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
class PolarFileNameChangedError < PolarRuntimeError # rubocop:disable Style/Documentation
|
56
|
+
# @param file [String]
|
57
|
+
# @param existing [String]
|
58
|
+
def initialize(file, existing)
|
59
|
+
super("A file with the same contents as #{file} named #{existing} has already been loaded.")
|
60
|
+
end
|
61
|
+
end
|
46
62
|
class PolarFileExtensionError < PolarRuntimeError # rubocop:disable Style/Documentation
|
47
|
-
def initialize
|
48
|
-
super(
|
63
|
+
def initialize(file)
|
64
|
+
super("Polar files must have .polar extension. Offending file: #{file}")
|
49
65
|
end
|
50
66
|
end
|
51
67
|
class PolarFileNotFoundError < PolarRuntimeError # rubocop:disable Style/Documentation
|
@@ -58,7 +74,7 @@ module Oso
|
|
58
74
|
# @param as [String]
|
59
75
|
# @param old [Class]
|
60
76
|
# @param new [Class]
|
61
|
-
def initialize(name:, old:, new:)
|
77
|
+
def initialize(name:, old:, new:)
|
62
78
|
super("Attempted to alias #{new} as '#{name}', but #{old} already has that alias.")
|
63
79
|
end
|
64
80
|
end
|
data/lib/oso/polar/ffi.rb
CHANGED
@@ -5,10 +5,12 @@ require 'ffi'
|
|
5
5
|
module Oso
|
6
6
|
module Polar
|
7
7
|
module FFI
|
8
|
-
LIB = ::FFI::Platform::LIBPREFIX
|
9
|
-
|
10
|
-
|
11
|
-
#
|
8
|
+
LIB = "#{::FFI::Platform::LIBPREFIX}polar.#{::FFI::Platform::LIBSUFFIX}"
|
9
|
+
RELEASE_PATH = File.expand_path(File.join(__dir__, "../../../ext/oso-oso/lib/#{LIB}"))
|
10
|
+
DEV_PATH = File.expand_path(File.join(__dir__, "../../../../../target/debug/#{LIB}"))
|
11
|
+
# If the lib exists in the ext/ dir, use it. Otherwise, fall back to
|
12
|
+
# checking the local Rust target dir.
|
13
|
+
LIB_PATH = File.file?(RELEASE_PATH) ? RELEASE_PATH : DEV_PATH
|
12
14
|
|
13
15
|
# Wrapper classes defined upfront to fix Ruby loading issues. Actual
|
14
16
|
# implementations live in the sibling `ffi/` directory and are `require`d
|
@@ -35,7 +37,13 @@ module Oso
|
|
35
37
|
# Wrapper class for Error FFI pointer + operations.
|
36
38
|
class Error < ::FFI::AutoPointer
|
37
39
|
def self.release(ptr)
|
38
|
-
Rust.free(ptr)
|
40
|
+
Rust.free(ptr) unless ptr.null?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# Wrapper class for Message FFI pointer + operations.
|
44
|
+
class Message < ::FFI::AutoPointer
|
45
|
+
def self.release(ptr)
|
46
|
+
Rust.free(ptr) unless ptr.null?
|
39
47
|
end
|
40
48
|
end
|
41
49
|
end
|
@@ -47,3 +55,4 @@ require 'oso/polar/ffi/polar'
|
|
47
55
|
require 'oso/polar/ffi/query'
|
48
56
|
require 'oso/polar/ffi/query_event'
|
49
57
|
require 'oso/polar/ffi/error'
|
58
|
+
require 'oso/polar/ffi/message'
|
data/lib/oso/polar/ffi/error.rb
CHANGED
@@ -22,7 +22,7 @@ module Oso
|
|
22
22
|
#
|
23
23
|
# @return [::Oso::Polar::Error] if there's an FFI error.
|
24
24
|
# @return [::Oso::Polar::FFIErrorNotFound] if there isn't one.
|
25
|
-
def self.get # rubocop:disable Metrics/
|
25
|
+
def self.get # rubocop:disable Metrics/MethodLength
|
26
26
|
error = Rust.get
|
27
27
|
return ::Oso::Polar::FFIErrorNotFound if error.null?
|
28
28
|
|
@@ -48,7 +48,7 @@ module Oso
|
|
48
48
|
# @param msg [String]
|
49
49
|
# @param details [Hash<String, Object>]
|
50
50
|
# @return [::Oso::Polar::ParseError] the object converted into the expected format.
|
51
|
-
private_class_method def self.parse_error(kind, msg:, details:) # rubocop:disable Metrics/
|
51
|
+
private_class_method def self.parse_error(kind, msg:, details:) # rubocop:disable Metrics/MethodLength
|
52
52
|
case kind
|
53
53
|
when 'ExtraToken'
|
54
54
|
::Oso::Polar::ParseError::ExtraToken.new(msg, details: details)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Oso
|
4
|
+
module Polar
|
5
|
+
module FFI
|
6
|
+
# Wrapper class for Message FFI pointer + operations.
|
7
|
+
class Message < ::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, [Message], :int32
|
18
|
+
end
|
19
|
+
|
20
|
+
def process
|
21
|
+
message = JSON.parse(to_s)
|
22
|
+
kind = message['kind']
|
23
|
+
msg = message['msg']
|
24
|
+
|
25
|
+
case kind
|
26
|
+
when 'Print'
|
27
|
+
puts(msg)
|
28
|
+
when 'Warning'
|
29
|
+
warn('[warning] %<msg>s')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private_constant :Rust
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/oso/polar/ffi/polar.rb
CHANGED
@@ -11,12 +11,12 @@ module Oso
|
|
11
11
|
|
12
12
|
attach_function :new, :polar_new, [], FFI::Polar
|
13
13
|
attach_function :load_str, :polar_load, [FFI::Polar, :string, :string], :int32
|
14
|
-
attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar], FFI::Query
|
14
|
+
attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
|
15
15
|
attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
|
16
|
-
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string], FFI::Query
|
17
|
-
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string], FFI::Query
|
18
|
-
attach_function :new_query_from_repl, :polar_query_from_repl, [FFI::Polar], FFI::Query
|
16
|
+
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], FFI::Query
|
17
|
+
attach_function :new_query_from_term, :polar_new_query_from_term, [FFI::Polar, :string, :uint32], FFI::Query
|
19
18
|
attach_function :register_constant, :polar_register_constant, [FFI::Polar, :string, :string], :int32
|
19
|
+
attach_function :next_message, :polar_next_polar_message, [FFI::Polar], FFI::Message
|
20
20
|
attach_function :free, :polar_free, [FFI::Polar], :int32
|
21
21
|
end
|
22
22
|
private_constant :Rust
|
@@ -34,14 +34,17 @@ module Oso
|
|
34
34
|
# @param filename [String]
|
35
35
|
# @raise [FFI::Error] if the FFI call returns an error.
|
36
36
|
def load_str(src, filename: nil)
|
37
|
-
|
37
|
+
loaded = Rust.load_str(self, src, filename)
|
38
|
+
process_messages
|
39
|
+
raise FFI::Error.get if loaded.zero?
|
38
40
|
end
|
39
41
|
|
40
42
|
# @return [FFI::Query] if there are remaining inline queries.
|
41
43
|
# @return [nil] if there are no remaining inline queries.
|
42
44
|
# @raise [FFI::Error] if the FFI call returns an error.
|
43
45
|
def next_inline_query
|
44
|
-
query = Rust.next_inline_query(self)
|
46
|
+
query = Rust.next_inline_query(self, 0)
|
47
|
+
process_messages
|
45
48
|
query.null? ? nil : query
|
46
49
|
end
|
47
50
|
|
@@ -60,7 +63,8 @@ module Oso
|
|
60
63
|
# @return [FFI::Query]
|
61
64
|
# @raise [FFI::Error] if the FFI call returns an error.
|
62
65
|
def new_query_from_str(str)
|
63
|
-
query = Rust.new_query_from_str(self, str)
|
66
|
+
query = Rust.new_query_from_str(self, str, 0)
|
67
|
+
process_messages
|
64
68
|
raise FFI::Error.get if query.null?
|
65
69
|
|
66
70
|
query
|
@@ -70,16 +74,8 @@ module Oso
|
|
70
74
|
# @return [FFI::Query]
|
71
75
|
# @raise [FFI::Error] if the FFI call returns an error.
|
72
76
|
def new_query_from_term(term)
|
73
|
-
query = Rust.new_query_from_term(self, JSON.dump(term))
|
74
|
-
|
75
|
-
|
76
|
-
query
|
77
|
-
end
|
78
|
-
|
79
|
-
# @return [FFI::Query]
|
80
|
-
# @raise [FFI::Error] if the FFI call returns an error.
|
81
|
-
def new_query_from_repl
|
82
|
-
query = Rust.new_query_from_repl(self)
|
77
|
+
query = Rust.new_query_from_term(self, JSON.dump(term), 0)
|
78
|
+
process_messages
|
83
79
|
raise FFI::Error.get if query.null?
|
84
80
|
|
85
81
|
query
|
@@ -89,7 +85,21 @@ module Oso
|
|
89
85
|
# @param value [Hash<String, Object>]
|
90
86
|
# @raise [FFI::Error] if the FFI call returns an error.
|
91
87
|
def register_constant(name, value:)
|
92
|
-
|
88
|
+
registered = Rust.register_constant(self, name, JSON.dump(value))
|
89
|
+
raise FFI::Error.get if registered.zero?
|
90
|
+
end
|
91
|
+
|
92
|
+
def next_message
|
93
|
+
Rust.next_message(self)
|
94
|
+
end
|
95
|
+
|
96
|
+
def process_messages
|
97
|
+
loop do
|
98
|
+
message = next_message
|
99
|
+
break if message.null?
|
100
|
+
|
101
|
+
message.process
|
102
|
+
end
|
93
103
|
end
|
94
104
|
end
|
95
105
|
end
|