oso-oso 0.2.6 → 0.5.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: 8c00a51f40789d269beb2d63e29ad12b76565d94
4
- data.tar.gz: 8f66dfdb31532125fd3593ddcfb7c3e76fd81df5
3
+ metadata.gz: cf2c949f57a56b4849f24ed9593c78da58baa5a0
4
+ data.tar.gz: 31ac99dcfa3bba1bbbee68474a3fe3702fb7484d
5
5
  SHA512:
6
- metadata.gz: 6ac0a230d40ecab86a7896249eb875fa43a16705a0594c9b55acd9d21215bff572ce1abb18da0593455f1a3c749354453808b779bed3dbf14e171f8b26b7511c
7
- data.tar.gz: 910cccfb441f515b20bcd1c21fc02a3f293b4445ed4526e2b8a2a921d97062df23156bc2726587269139a79edd687bd8d5b9fa79776bbc5910045f2205e58923
6
+ metadata.gz: f1708d82f2531d50456ddcadb967bd024414700808e81b440836eb344f542cfda46af651382a6445a098dce9cf1e1c63c6802df73e3f97713a819d0b07ef5aae
7
+ data.tar.gz: d96b91d530555ed98d040c2d3c8e8e0adbdffa352cef6bd49c22e6f225c16bfa9f4c488d37468201e16e0950791581621737b94f1b9d5ffc8b8c4dffeaa4e656
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - '**/*~'
5
+ - 'bin/oso'
6
+ - 'vendor/**/*'
7
+ NewCops: enable
data/Gemfile CHANGED
@@ -3,4 +3,3 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
-
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oso-oso (0.2.6)
4
+ oso-oso (0.5.1)
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.3)
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.9)
22
+ nokogiri (1.10.10)
23
23
  mini_portile2 (~> 2.4.0)
24
- parallel (1.19.1)
25
- parser (2.7.1.3)
26
- ast (~> 2.4.0)
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.85.1)
52
+ rubocop (0.89.1)
53
53
  parallel (~> 1.10)
54
- parser (>= 2.7.0.1)
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.3)
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.3)
62
- parser (>= 2.7.0.1)
61
+ rubocop-ast (0.3.0)
62
+ parser (>= 2.7.1.4)
63
63
  ruby-progressbar (1.10.1)
64
- solargraph (0.39.8)
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
- solargraph (~> 0.39.8)
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,13 +1,19 @@
1
- .PHONY: install rust test
1
+ .PHONY: rust install test lint typecheck repl
2
2
 
3
3
  rust:
4
4
  $(MAKE) -C ../.. rust-build
5
5
 
6
- install: rust
6
+ install:
7
7
  bundle install
8
8
 
9
- test: install
9
+ test: install rust
10
10
  bundle exec rake spec
11
11
 
12
- repl:
12
+ lint: install
13
+ bundle exec rubocop
14
+
15
+ typecheck: install
16
+ bundle exec solargraph typecheck
17
+
18
+ repl: install rust
13
19
  bundle exec oso
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Oso::Oso
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 `bin/setup` to install dependencies. Then, run
22
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
23
- prompt that will allow you to experiment.
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`. To
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
@@ -6,4 +6,3 @@ require 'rspec/core/rake_task'
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  task default: :spec
9
-
data/bin/oso CHANGED
@@ -4,4 +4,4 @@
4
4
  require 'bundler/setup'
5
5
  require 'oso'
6
6
 
7
- Oso.new.repl(load: true)
7
+ Oso.new.repl(ARGV)
Binary file
Binary file
@@ -3,7 +3,7 @@
3
3
  module Oso
4
4
  # An HTTP resource.
5
5
  class Http
6
- def initialize(hostname: nil, path: nil, query: nil)
6
+ def initialize(hostname, path, query)
7
7
  @hostname = hostname
8
8
  @path = path
9
9
  @query = query
@@ -3,7 +3,7 @@
3
3
  require_relative 'polar/polar'
4
4
 
5
5
  module Oso
6
- # Oso authorization API.
6
+ # oso authorization API.
7
7
  class Oso < Polar::Polar
8
8
  def initialize
9
9
  super
@@ -11,8 +11,15 @@ module Oso
11
11
  register_class(PathMapper, name: 'PathMapper')
12
12
  end
13
13
 
14
- def allow(actor:, action:, resource:)
15
- query_predicate('allow', 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
16
23
  true
17
24
  rescue StopIteration
18
25
  false
@@ -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
@@ -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
- if details and details.key?("stack_trace")
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
@@ -31,6 +27,7 @@ module Oso
31
27
  class UnsupportedError < PolarRuntimeError; end
32
28
  class PolarTypeError < PolarRuntimeError; end
33
29
  class StackOverflowError < PolarRuntimeError; end
30
+ class FileLoadingError < PolarRuntimeError; end
34
31
 
35
32
  # Errors originating from this side of the FFI boundary.
36
33
 
@@ -41,12 +38,17 @@ module Oso
41
38
  class InvalidCallError < PolarRuntimeError; end
42
39
  class InvalidConstructorError < PolarRuntimeError; end
43
40
  class InvalidQueryTypeError < PolarRuntimeError; end
44
- class InlineQueryFailedError < PolarRuntimeError; end
45
41
  class NullByteInPolarFileError < PolarRuntimeError; end
46
42
  class UnexpectedPolarTypeError < PolarRuntimeError; end
43
+ class InlineQueryFailedError < PolarRuntimeError; # rubocop:disable Style/Documentation
44
+ # @param source [String]
45
+ def initialize(source)
46
+ super("Inline query failed: #{source}")
47
+ end
48
+ end
47
49
  class PolarFileExtensionError < PolarRuntimeError # rubocop:disable Style/Documentation
48
- def initialize
49
- super('Polar files must have .pol or .polar extension.')
50
+ def initialize(file)
51
+ super("Polar files must have .polar extension. Offending file: #{file}")
50
52
  end
51
53
  end
52
54
  class PolarFileNotFoundError < PolarRuntimeError # rubocop:disable Style/Documentation
@@ -59,7 +61,7 @@ module Oso
59
61
  # @param as [String]
60
62
  # @param old [Class]
61
63
  # @param new [Class]
62
- def initialize(name:, old:, new:) # rubocop:disable Naming/MethodParameterName
64
+ def initialize(name:, old:, new:)
63
65
  super("Attempted to alias #{new} as '#{name}', but #{old} already has that alias.")
64
66
  end
65
67
  end
@@ -5,7 +5,7 @@ require 'ffi'
5
5
  module Oso
6
6
  module Polar
7
7
  module FFI
8
- LIB = ::FFI::Platform::LIBPREFIX + 'polar.' + ::FFI::Platform::LIBSUFFIX
8
+ LIB = "#{::FFI::Platform::LIBPREFIX}polar.#{::FFI::Platform::LIBSUFFIX}"
9
9
  RELEASE_PATH = File.expand_path(File.join(__dir__, "../../../ext/oso-oso/lib/#{LIB}"))
10
10
  DEV_PATH = File.expand_path(File.join(__dir__, "../../../../../target/debug/#{LIB}"))
11
11
  # If the lib exists in the ext/ dir, use it. Otherwise, fall back to
@@ -37,7 +37,20 @@ module Oso
37
37
  # Wrapper class for Error FFI pointer + operations.
38
38
  class Error < ::FFI::AutoPointer
39
39
  def self.release(ptr)
40
- 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?
47
+ end
48
+ end
49
+
50
+ # Wrapper class for Source FFI pointer.
51
+ class Source < ::FFI::AutoPointer
52
+ def self.release(ptr)
53
+ Rust.free(ptr) unless ptr.null?
41
54
  end
42
55
  end
43
56
  end
@@ -49,3 +62,5 @@ require 'oso/polar/ffi/polar'
49
62
  require 'oso/polar/ffi/query'
50
63
  require 'oso/polar/ffi/query_event'
51
64
  require 'oso/polar/ffi/error'
65
+ require 'oso/polar/ffi/message'
66
+ require 'oso/polar/ffi/source'
@@ -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/AbcSize, Metrics/MethodLength
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/CyclomaticComplexity, Metrics/MethodLength
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)
@@ -83,6 +83,8 @@ module Oso
83
83
  ::Oso::Polar::PolarTypeError.new(msg, details: details)
84
84
  when 'StackOverflow'
85
85
  ::Oso::Polar::StackOverflowError.new(msg, details: details)
86
+ when 'FileLoading'
87
+ ::Oso::Polar::FileLoadingError.new(msg, details: details)
86
88
  else
87
89
  ::Oso::Polar::PolarRuntimeError.new(msg, details: details)
88
90
  end
@@ -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(format('[warning] %<msg>s', msg: msg))
30
+ end
31
+ end
32
+
33
+ private_constant :Rust
34
+ end
35
+ end
36
+ end
37
+ end
@@ -10,12 +10,13 @@ module Oso
10
10
  ffi_lib FFI::LIB_PATH
11
11
 
12
12
  attach_function :new, :polar_new, [], FFI::Polar
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
13
+ attach_function :load, :polar_load, [FFI::Polar, :string, :string], :int32
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
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
18
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
19
20
  attach_function :free, :polar_free, [FFI::Polar], :int32
20
21
  end
21
22
  private_constant :Rust
@@ -32,15 +33,18 @@ module Oso
32
33
  # @param src [String]
33
34
  # @param filename [String]
34
35
  # @raise [FFI::Error] if the FFI call returns an error.
35
- def load_str(src, filename: nil)
36
- raise FFI::Error.get if Rust.load_str(self, src, filename).zero?
36
+ def load(src, filename: nil)
37
+ loaded = Rust.load(self, src, filename)
38
+ process_messages
39
+ raise FFI::Error.get if loaded.zero?
37
40
  end
38
41
 
39
42
  # @return [FFI::Query] if there are remaining inline queries.
40
43
  # @return [nil] if there are no remaining inline queries.
41
44
  # @raise [FFI::Error] if the FFI call returns an error.
42
45
  def next_inline_query
43
- query = Rust.next_inline_query(self)
46
+ query = Rust.next_inline_query(self, 0)
47
+ process_messages
44
48
  query.null? ? nil : query
45
49
  end
46
50
 
@@ -59,7 +63,8 @@ module Oso
59
63
  # @return [FFI::Query]
60
64
  # @raise [FFI::Error] if the FFI call returns an error.
61
65
  def new_query_from_str(str)
62
- query = Rust.new_query_from_str(self, str)
66
+ query = Rust.new_query_from_str(self, str, 0)
67
+ process_messages
63
68
  raise FFI::Error.get if query.null?
64
69
 
65
70
  query
@@ -69,7 +74,8 @@ module Oso
69
74
  # @return [FFI::Query]
70
75
  # @raise [FFI::Error] if the FFI call returns an error.
71
76
  def new_query_from_term(term)
72
- query = Rust.new_query_from_term(self, JSON.dump(term))
77
+ query = Rust.new_query_from_term(self, JSON.dump(term), 0)
78
+ process_messages
73
79
  raise FFI::Error.get if query.null?
74
80
 
75
81
  query
@@ -79,7 +85,21 @@ module Oso
79
85
  # @param value [Hash<String, Object>]
80
86
  # @raise [FFI::Error] if the FFI call returns an error.
81
87
  def register_constant(name, value:)
82
- raise FFI::Error.get if Rust.register_constant(self, name, JSON.dump(value)).zero?
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
83
103
  end
84
104
  end
85
105
  end
@@ -12,7 +12,10 @@ module Oso
12
12
  attach_function :debug_command, :polar_debug_command, [FFI::Query, :string], :int32
13
13
  attach_function :call_result, :polar_call_result, [FFI::Query, :uint64, :string], :int32
14
14
  attach_function :question_result, :polar_question_result, [FFI::Query, :uint64, :int32], :int32
15
+ attach_function :application_error, :polar_application_error, [FFI::Query, :string], :int32
15
16
  attach_function :next_event, :polar_next_query_event, [FFI::Query], FFI::QueryEvent
17
+ attach_function :next_message, :polar_next_query_message, [FFI::Query], FFI::Message
18
+ attach_function :source, :polar_query_source_info, [FFI::Query], FFI::Source
16
19
  attach_function :free, :query_free, [FFI::Query], :int32
17
20
  end
18
21
  private_constant :Rust
@@ -21,6 +24,7 @@ module Oso
21
24
  # @raise [FFI::Error] if the FFI call returns an error.
22
25
  def debug_command(cmd)
23
26
  res = Rust.debug_command(self, cmd)
27
+ process_messages
24
28
  raise FFI::Error.get if res.zero?
25
29
  end
26
30
 
@@ -41,14 +45,45 @@ module Oso
41
45
  raise FFI::Error.get if res.zero?
42
46
  end
43
47
 
48
+ # @param result [Boolean]
49
+ # @param call_id [Integer]
50
+ # @raise [FFI::Error] if the FFI call returns an error.
51
+ def application_error(message)
52
+ res = Rust.application_error(self, message)
53
+ raise FFI::Error.get if res.zero?
54
+ end
55
+
44
56
  # @return [::Oso::Polar::QueryEvent]
45
57
  # @raise [FFI::Error] if the FFI call returns an error.
46
58
  def next_event
47
59
  event = Rust.next_event(self)
60
+ process_messages
48
61
  raise FFI::Error.get if event.null?
49
62
 
50
63
  ::Oso::Polar::QueryEvent.new(JSON.parse(event.to_s))
51
64
  end
65
+
66
+ def next_message
67
+ Rust.next_message(self)
68
+ end
69
+
70
+ def process_messages
71
+ loop do
72
+ message = next_message
73
+ break if message.null?
74
+
75
+ message.process
76
+ end
77
+ end
78
+
79
+ # @return [String]
80
+ # @raise [FFI::Error] if the FFI call returns an error.
81
+ def source
82
+ res = Rust.source(self)
83
+ raise FFI::Error.get if res.null?
84
+
85
+ res.to_s
86
+ end
52
87
  end
53
88
  end
54
89
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Oso
4
+ module Polar
5
+ module FFI
6
+ # Wrapper class for Source FFI pointer.
7
+ class Source < ::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
+ private_constant :Rust
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,8 +3,9 @@
3
3
  module Oso
4
4
  module Polar
5
5
  # Translate between Polar and the host language (Ruby).
6
- class Host
6
+ class Host # rubocop:disable Metrics/ClassLength
7
7
  protected
8
+
8
9
  # @return [FFI::Polar]
9
10
  attr_reader :ffi_polar
10
11
  # @return [Hash<String, Class>]
@@ -15,6 +16,7 @@ module Oso
15
16
  attr_reader :instances
16
17
 
17
18
  public
19
+
18
20
  def initialize(ffi_polar)
19
21
  @ffi_polar = ffi_polar
20
22
  @classes = {}
@@ -44,9 +46,10 @@ module Oso
44
46
  #
45
47
  # @param cls [Class] the class to cache
46
48
  # @param name [String] the name to cache the class as. Defaults to the name of the class.
49
+ # @param constructor [Proc] optional custom constructor function. Defaults to the :new method.
47
50
  # @return [String] the name the class is cached as.
48
51
  # @raise [UnregisteredClassError] if the class has not been registered.
49
- def cache_class(cls, name:, constructor:)
52
+ def cache_class(cls, name:, constructor:) # rubocop:disable Metrics/MethodLength
50
53
  name = cls.name if name.nil?
51
54
  raise DuplicateClassAliasError, name: name, old: get_class(name), new: cls if classes.key? name
52
55
 
@@ -73,7 +76,7 @@ module Oso
73
76
  constructors[name]
74
77
  end
75
78
 
76
- # Check if an instance has been cached.
79
+ # Check if an instance exists in the {#instances} cache.
77
80
  #
78
81
  # @param id [Integer]
79
82
  # @return [Boolean]
@@ -97,7 +100,8 @@ module Oso
97
100
  instances[id]
98
101
  end
99
102
 
100
- # Cache a Ruby instance, fetching a {#new_id} if one isn't provided.
103
+ # Cache a Ruby instance in the {#instances} cache, fetching a {#new_id}
104
+ # if one isn't provided.
101
105
  #
102
106
  # @param instance [Object]
103
107
  # @param id [Integer]
@@ -111,22 +115,29 @@ module Oso
111
115
  # Construct and cache a Ruby instance.
112
116
  #
113
117
  # @param cls_name [String]
114
- # @param fields [Hash<String, Hash>]
118
+ # @param initargs [Hash<String, Hash>]
115
119
  # @param id [Integer]
116
120
  # @raise [PolarRuntimeError] if instance construction fails.
117
- def make_instance(cls_name, fields:, id:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
121
+ def make_instance(cls_name, initargs:, id:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
118
122
  constructor = get_constructor(cls_name)
119
- fields = Hash[fields.map { |k, v| [k.to_sym, to_ruby(v)] }]
120
123
  instance = if constructor == :new
121
- if fields.empty?
124
+ if initargs.empty?
122
125
  get_class(cls_name).__send__(:new)
126
+ elsif initargs.is_a? Array
127
+ get_class(cls_name).__send__(:new, *initargs)
128
+ elsif initargs.is_a? Hash
129
+ get_class(cls_name).__send__(:new, **initargs)
123
130
  else
124
- get_class(cls_name).__send__(:new, **fields)
131
+ raise PolarRuntimeError, "Bad initargs: #{initargs}"
125
132
  end
126
- elsif fields.empty?
133
+ elsif initargs.empty?
127
134
  constructor.call
135
+ elsif initargs.is_a? Array
136
+ constructor.call(*initargs)
137
+ elsif initargs.is_a? Hash
138
+ constructor.call(**initargs)
128
139
  else
129
- constructor.call(**fields)
140
+ raise PolarRuntimeError, "Bad initargs: #{initargs}"
130
141
  end
131
142
  cache_instance(instance, id: id)
132
143
  rescue StandardError => e
@@ -149,11 +160,11 @@ module Oso
149
160
 
150
161
  # Check if instance is an instance of class.
151
162
  #
152
- # @param instance_id [Integer]
163
+ # @param instance [Hash<String, Object>]
153
164
  # @param class_tag [String]
154
165
  # @return [Boolean]
155
- def isa?(instance_id, class_tag:)
156
- instance = get_instance(instance_id)
166
+ def isa?(instance, class_tag:)
167
+ instance = to_ruby(instance)
157
168
  cls = get_class(class_tag)
158
169
  instance.is_a? cls
159
170
  rescue PolarRuntimeError
@@ -198,7 +209,7 @@ module Oso
198
209
  # This is supported so that we can query for unbound variables
199
210
  { 'Variable' => value }
200
211
  else
201
- { 'ExternalInstance' => { 'instance_id' => cache_instance(value) } }
212
+ { 'ExternalInstance' => { 'instance_id' => cache_instance(value), 'repr' => value.to_s } }
202
213
  end
203
214
  { 'value' => value }
204
215
  end
@@ -226,6 +237,8 @@ module Oso
226
237
  get_instance(value['instance_id'])
227
238
  when 'Call'
228
239
  Predicate.new(value['name'], args: value['args'].map { |a| to_ruby(a) })
240
+ when 'Variable'
241
+ Variable.new(value['name'])
229
242
  else
230
243
  raise UnexpectedPolarTypeError, tag
231
244
  end
@@ -3,36 +3,72 @@
3
3
  require 'json'
4
4
  require 'pp'
5
5
  require 'set'
6
+ require 'digest/md5'
7
+
8
+ # Missing Ruby type.
9
+ module PolarBoolean; end
10
+ # Monkey-patch Ruby true type.
11
+ class TrueClass; include PolarBoolean; end
12
+ # Monkey-patch Ruby false type.
13
+ class FalseClass; include PolarBoolean; end
14
+
15
+ # https://github.com/ruby/ruby/blob/bb9ecd026a6cadd5d0f85ac061649216806ed935/lib/bundler/vendor/thor/lib/thor/shell/color.rb#L99-L105
16
+ def supports_color
17
+ $stdout.tty? && $stderr.tty? && ENV['NO_COLOR'].nil?
18
+ end
19
+
20
+ if supports_color
21
+ RESET = "\x1b[0m"
22
+ FG_BLUE = "\x1b[34m"
23
+ FG_RED = "\x1b[31m"
24
+ else
25
+ RESET = ''
26
+ FG_BLUE = ''
27
+ FG_RED = ''
28
+ end
29
+
30
+ def print_error(error)
31
+ warn FG_RED + error.class.name.split('::').last + RESET
32
+ warn error.message
33
+ end
6
34
 
7
35
  module Oso
8
36
  module Polar
9
37
  # Create and manage an instance of the Polar runtime.
10
- class Polar
38
+ class Polar # rubocop:disable Metrics/ClassLength
11
39
  # @return [Host]
12
40
  attr_reader :host
13
41
 
14
42
  def initialize
15
43
  @ffi_polar = FFI::Polar.create
16
44
  @host = Host.new(ffi_polar)
17
- @load_queue = Set.new
45
+
46
+ # Register built-in classes.
47
+ register_class PolarBoolean, name: 'Boolean'
48
+ register_class Integer
49
+ register_class Float
50
+ register_class Array, name: 'List'
51
+ register_class Hash, name: 'Dictionary'
52
+ register_class String
18
53
  end
19
54
 
20
55
  # Replace the current Polar instance but retain all registered classes and constructors.
21
56
  def clear
22
- load_queue.clear
23
57
  @ffi_polar = FFI::Polar.create
24
58
  end
25
59
 
26
- # Enqueue a Polar policy file for loading into the KB.
60
+ # Load a Polar policy file.
27
61
  #
28
62
  # @param name [String]
29
63
  # @raise [PolarFileExtensionError] if provided filename has invalid extension.
30
64
  # @raise [PolarFileNotFoundError] if provided filename does not exist.
31
65
  def load_file(name)
32
- raise PolarFileExtensionError unless ['.pol', '.polar'].include? File.extname(name)
33
- raise PolarFileNotFoundError, name unless File.file?(name)
66
+ raise PolarFileExtensionError, name unless File.extname(name) == '.polar'
34
67
 
35
- load_queue << name
68
+ file_data = File.open(name, &:read)
69
+ load_str(file_data, filename: name)
70
+ rescue Errno::ENOENT
71
+ raise PolarFileNotFoundError, name
36
72
  end
37
73
 
38
74
  # Load a Polar string into the KB.
@@ -45,7 +81,7 @@ module Oso
45
81
  def load_str(str, filename: nil) # rubocop:disable Metrics/MethodLength
46
82
  raise NullByteInPolarFileError if str.chomp("\0").include?("\0")
47
83
 
48
- ffi_polar.load_str(str, filename: filename)
84
+ ffi_polar.load(str, filename: filename)
49
85
  loop do
50
86
  next_query = ffi_polar.next_inline_query
51
87
  break if next_query.nil?
@@ -53,14 +89,22 @@ module Oso
53
89
  begin
54
90
  Query.new(next_query, host: host).results.next
55
91
  rescue StopIteration
56
- raise InlineQueryFailedError
92
+ raise InlineQueryFailedError, next_query.source
57
93
  end
58
94
  end
59
95
  end
60
96
 
61
- # Query for a predicate, parsing it if necessary.
97
+ # Query for a Polar predicate or string.
98
+ #
99
+ # @overload query(query)
100
+ # @param query [String]
101
+ # @return [Enumerator] of resulting bindings
102
+ # @raise [Error] if the FFI call raises one.
103
+ # @overload query(query)
104
+ # @param query [Predicate]
105
+ # @return [Enumerator] of resulting bindings
106
+ # @raise [Error] if the FFI call raises one.
62
107
  def query(query)
63
- load_queued_files
64
108
  new_host = host.dup
65
109
  case query
66
110
  when String
@@ -73,58 +117,16 @@ module Oso
73
117
  Query.new(ffi_query, host: new_host).results
74
118
  end
75
119
 
76
- # Query for a predicate.
120
+ # Query for a rule.
77
121
  #
78
122
  # @param name [String]
79
123
  # @param args [Array<Object>]
124
+ # @return [Enumerator] of resulting bindings
80
125
  # @raise [Error] if the FFI call raises one.
81
- def query_predicate(name, *args)
126
+ def query_rule(name, *args)
82
127
  query(Predicate.new(name, args: args))
83
128
  end
84
129
 
85
- # Start a REPL session.
86
- #
87
- # @raise [Error] if the FFI call raises one.
88
- def repl(load: false) # rubocop:disable Metrics/MethodLength
89
- ARGV.map { |f| load_file(f) } if load
90
- load_queued_files
91
-
92
- loop do
93
- print('> ')
94
- begin
95
- query = STDIN.readline.chomp.chomp(';')
96
- rescue EOFError
97
- return
98
- end
99
-
100
- begin
101
- ffi_query = ffi_polar.new_query_from_str(query)
102
- rescue ParseError => e
103
- puts("Parse error: " + e.to_s)
104
- next
105
- end
106
-
107
- begin
108
- results = Query.new(ffi_query, host: host).results.to_a
109
- rescue PolarRuntimeError => e
110
- puts(e.to_s)
111
- next
112
- end
113
-
114
- if results.empty?
115
- pp false
116
- else
117
- results.each do |result|
118
- if result.empty?
119
- pp true
120
- else
121
- pp result
122
- end
123
- end
124
- end
125
- end
126
- end
127
-
128
130
  # Register a Ruby class with Polar.
129
131
  #
130
132
  # @param cls [Class]
@@ -132,9 +134,7 @@ module Oso
132
134
  # @param from_polar [Proc]
133
135
  # @raise [InvalidConstructorError] if provided an invalid 'from_polar' constructor.
134
136
  def register_class(cls, name: nil, from_polar: nil)
135
- if block_given?
136
- from_polar = Proc.new
137
- end
137
+ from_polar = Proc.new if block_given?
138
138
  name = host.cache_class(cls, name: name, constructor: from_polar)
139
139
  register_constant(name, value: cls)
140
140
  end
@@ -143,20 +143,89 @@ module Oso
143
143
  ffi_polar.register_constant(name, value: host.to_polar_term(value))
144
144
  end
145
145
 
146
- # Load all queued files, flushing the {#load_queue}.
147
- def load_queued_files
148
- load_queue.reject! do |filename|
149
- File.open(filename) { |file| load_str(file.read, filename: filename) }
150
- true
151
- end
146
+ # Start a REPL session.
147
+ #
148
+ # @param files [Array<String>]
149
+ # @raise [Error] if the FFI call raises one.
150
+ def repl(files = [])
151
+ files.map { |f| load_file(f) }
152
+ prompt = "#{FG_BLUE}query>#{RESET} "
153
+ # Try loading the readline module from the Ruby stdlib. If we get a
154
+ # LoadError, fall back to the standard REPL with no readline support.
155
+ require 'readline'
156
+ repl_readline(prompt)
157
+ rescue LoadError
158
+ repl_standard(prompt)
152
159
  end
153
160
 
154
161
  private
155
162
 
156
163
  # @return [FFI::Polar]
157
164
  attr_reader :ffi_polar
158
- # @return [Array<String>]
159
- attr_reader :load_queue
165
+ # @return [Hash<String, String>]
166
+ attr_reader :loaded_names
167
+ # @return [Hash<String, String>]
168
+ attr_reader :loaded_contents
169
+
170
+ # The R and L in REPL for systems where readline is available.
171
+ def repl_readline(prompt)
172
+ while (buf = Readline.readline(prompt, true))
173
+ if /^\s*$/ =~ buf # Don't add empty entries to history.
174
+ Readline::HISTORY.pop
175
+ next
176
+ end
177
+ process_line(buf)
178
+ end
179
+ rescue Interrupt # rubocop:disable Lint/SuppressedException
180
+ end
181
+
182
+ # The R and L in REPL for systems where readline is not available.
183
+ def repl_standard(prompt)
184
+ loop do
185
+ puts prompt
186
+ begin
187
+ buf = $stdin.readline
188
+ rescue EOFError
189
+ return
190
+ end
191
+ process_line(buf)
192
+ end
193
+ rescue Interrupt # rubocop:disable Lint/SuppressedException
194
+ end
195
+
196
+ # Process a line of user input in the REPL.
197
+ #
198
+ # @param buf [String]
199
+ def process_line(buf) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/AbcSize
200
+ query = buf.chomp.chomp(';')
201
+ begin
202
+ ffi_query = ffi_polar.new_query_from_str(query)
203
+ rescue ParseError => e
204
+ print_error(e)
205
+ return
206
+ end
207
+
208
+ begin
209
+ results = Query.new(ffi_query, host: host).results.to_a
210
+ rescue PolarRuntimeError => e
211
+ print_error(e)
212
+ return
213
+ end
214
+
215
+ if results.empty?
216
+ puts false
217
+ else
218
+ results.each do |result|
219
+ if result.empty?
220
+ puts true
221
+ else
222
+ result.each do |variable, value|
223
+ puts "#{variable} => #{value.inspect}"
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
160
229
  end
161
230
  end
162
231
  end
@@ -3,11 +3,12 @@
3
3
  module Oso
4
4
  module Polar
5
5
  # A single Polar query.
6
- class Query
6
+ class Query # rubocop:disable Metrics/ClassLength
7
+ # @return [Enumerator]
7
8
  attr_reader :results
8
9
 
9
10
  # @param ffi_query [FFI::Query]
10
- # @param ffi_polar [FFI::Polar]
11
+ # @param host [Oso::Polar::Host]
11
12
  def initialize(ffi_query, host:)
12
13
  @calls = {}
13
14
  @ffi_query = ffi_query
@@ -38,21 +39,20 @@ module Oso
38
39
  #
39
40
  # @param method [#to_sym]
40
41
  # @param call_id [Integer]
41
- # @param instance [Hash]
42
+ # @param instance [Hash<String, Object>]
42
43
  # @param args [Array<Hash>]
43
44
  # @raise [InvalidCallError] if the method doesn't exist on the instance or
44
45
  # the args passed to the method are invalid.
45
- def register_call(method, call_id:, instance:, args:)
46
+ def register_call(attribute, call_id:, instance:, args:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
46
47
  return if calls.key?(call_id)
47
48
 
48
- args = args.map { |a| host.to_ruby(a) }
49
- if instance['value'].key? 'ExternalInstance'
50
- instance_id = instance['value']['ExternalInstance']['instance_id']
51
- instance = host.get_instance(instance_id)
49
+ instance = host.to_ruby(instance)
50
+ if args.nil?
51
+ result = instance.__send__(attribute)
52
52
  else
53
- instance = host.to_ruby(instance)
53
+ args = args.map { |a| host.to_ruby(a) }
54
+ result = instance.__send__(attribute, *args)
54
55
  end
55
- result = instance.__send__(method, *args)
56
56
  result = [result].to_enum unless result.is_a? Enumerator # Call must be a generator.
57
57
  calls[call_id] = result.lazy
58
58
  rescue ArgumentError, NoMethodError
@@ -77,22 +77,31 @@ module Oso
77
77
  host.to_polar_term(calls[id].next)
78
78
  end
79
79
 
80
+ # Send application error across FFI boundary.
81
+ #
82
+ # @param message [String]
83
+ # @raise [Error] if the FFI call raises one.
84
+ def application_error(message)
85
+ ffi_query.application_error(message)
86
+ end
87
+
80
88
  # Fetch the next result from calling a Ruby method and prepare it for
81
89
  # transmission across the FFI boundary.
82
90
  #
83
91
  # @param method [#to_sym]
84
92
  # @param args [Array<Hash>]
85
93
  # @param call_id [Integer]
86
- # @param instance_id [Integer]
94
+ # @param instance [Hash<String, Object>]
87
95
  # @raise [Error] if the FFI call raises one.
88
- def handle_call(method, call_id:, instance:, args:)
89
- register_call(method, call_id: call_id, instance: instance, args: args)
96
+ def handle_call(attribute, call_id:, instance:, args:)
97
+ register_call(attribute, call_id: call_id, instance: instance, args: args)
90
98
  result = JSON.dump(next_call_result(call_id))
91
99
  call_result(result, call_id: call_id)
92
- rescue InvalidCallError, StopIteration
100
+ rescue InvalidCallError => e
101
+ application_error(e.message)
102
+ call_result(nil, call_id: call_id)
103
+ rescue StopIteration
93
104
  call_result(nil, call_id: call_id)
94
- # @TODO: polar line numbers in errors once polar errors are better.
95
- # raise PolarRuntimeError(f"Error calling {attribute}")
96
105
  end
97
106
 
98
107
  # Create a generator that can be polled to advance the query loop.
@@ -100,7 +109,7 @@ module Oso
100
109
  # @yieldparam [Hash<String, Object>]
101
110
  # @return [Enumerator]
102
111
  # @raise [Error] if any of the FFI calls raise one.
103
- def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
112
+ def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
104
113
  Enumerator.new do |yielder| # rubocop:disable Metrics/BlockLength
105
114
  loop do # rubocop:disable Metrics/BlockLength
106
115
  event = ffi_query.next_event
@@ -113,15 +122,24 @@ module Oso
113
122
  id = event.data['instance_id']
114
123
  raise DuplicateInstanceRegistrationError, id if host.instance? id
115
124
 
116
- cls_name = event.data['instance']['tag']
117
- fields = event.data['instance']['fields']['fields']
118
- host.make_instance(cls_name, fields: fields, id: id)
125
+ constructor = event.data['constructor']['value']
126
+ if constructor.key? 'InstanceLiteral'
127
+ cls_name = constructor['InstanceLiteral']['tag']
128
+ fields = constructor['InstanceLiteral']['fields']['fields']
129
+ initargs = Hash[fields.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
130
+ elsif constructor.key? 'Call'
131
+ cls_name = constructor['Call']['name']
132
+ initargs = constructor['Call']['args'].map { |arg| host.to_ruby(arg) }
133
+ else
134
+ raise InvalidConstructorError
135
+ end
136
+ host.make_instance(cls_name, initargs: initargs, id: id)
119
137
  when 'ExternalCall'
120
138
  call_id = event.data['call_id']
121
139
  instance = event.data['instance']
122
- method = event.data['attribute']
140
+ attribute = event.data['attribute']
123
141
  args = event.data['args']
124
- handle_call(method, call_id: call_id, instance: instance, args: args)
142
+ handle_call(attribute, call_id: call_id, instance: instance, args: args)
125
143
  when 'ExternalIsSubSpecializer'
126
144
  instance_id = event.data['instance_id']
127
145
  left_tag = event.data['left_class_tag']
@@ -129,9 +147,9 @@ module Oso
129
147
  answer = host.subspecializer?(instance_id, left_tag: left_tag, right_tag: right_tag)
130
148
  question_result(answer, call_id: event.data['call_id'])
131
149
  when 'ExternalIsa'
132
- instance_id = event.data['instance_id']
150
+ instance = event.data['instance']
133
151
  class_tag = event.data['class_tag']
134
- answer = host.isa?(instance_id, class_tag: class_tag)
152
+ answer = host.isa?(instance, class_tag: class_tag)
135
153
  question_result(answer, call_id: event.data['call_id'])
136
154
  when 'ExternalUnify'
137
155
  left_instance_id = event.data['left_instance_id']
@@ -140,8 +158,12 @@ module Oso
140
158
  question_result(answer, call_id: event.data['call_id'])
141
159
  when 'Debug'
142
160
  puts event.data['message'] if event.data['message']
143
- print '> '
144
- input = $stdin.gets.chomp!
161
+ print 'debug> '
162
+ begin
163
+ input = $stdin.readline.chomp.chomp(';')
164
+ rescue EOFError
165
+ next
166
+ end
145
167
  command = JSON.dump(host.to_polar_term(input))
146
168
  ffi_query.debug_command(command)
147
169
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Oso
4
- VERSION = '0.2.6'
4
+ VERSION = '0.5.1'
5
5
  end
@@ -5,14 +5,16 @@ require_relative 'lib/oso/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'oso-oso'
7
7
  spec.version = Oso::VERSION
8
- spec.authors = ['Oso Security']
8
+ spec.authors = ['Oso Security, Inc.']
9
9
  spec.email = ['support@osohq.com']
10
-
11
- spec.summary = 'Oso authorization API.'
10
+ spec.licenses = ['Apache-2.0']
11
+ spec.summary = 'oso authorization library.'
12
12
  spec.homepage = 'https://www.osohq.com/'
13
+
13
14
  spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
14
15
 
15
16
  spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/osohq/oso'
16
18
 
17
19
  # Specify which files should be added to the gem when it is released.
18
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -32,6 +34,7 @@ Gem::Specification.new do |spec|
32
34
  spec.add_development_dependency 'pry-byebug', '~> 3.9.0'
33
35
  spec.add_development_dependency 'rake', '~> 12.0'
34
36
  spec.add_development_dependency 'rspec', '~> 3.0'
35
- spec.add_development_dependency 'solargraph', '~> 0.39.8'
37
+ spec.add_development_dependency 'rubocop', '~> 0.89.1'
38
+ spec.add_development_dependency 'solargraph', '~> 0.39.14'
36
39
  spec.add_development_dependency 'yard', '~> 0.9.25'
37
40
  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.2.6
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
- - Oso Security
7
+ - Oso Security, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-25 00:00:00.000000000 Z
11
+ date: 2020-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -66,20 +66,34 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.89.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.89.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: solargraph
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 0.39.8
89
+ version: 0.39.14
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 0.39.8
96
+ version: 0.39.14
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,7 @@ extra_rdoc_files: []
104
118
  files:
105
119
  - ".gitignore"
106
120
  - ".rspec"
121
+ - ".rubocop.yml"
107
122
  - ".solargraph.yml"
108
123
  - Gemfile
109
124
  - Gemfile.lock
@@ -122,9 +137,11 @@ files:
122
137
  - lib/oso/polar/errors.rb
123
138
  - lib/oso/polar/ffi.rb
124
139
  - lib/oso/polar/ffi/error.rb
140
+ - lib/oso/polar/ffi/message.rb
125
141
  - lib/oso/polar/ffi/polar.rb
126
142
  - lib/oso/polar/ffi/query.rb
127
143
  - lib/oso/polar/ffi/query_event.rb
144
+ - lib/oso/polar/ffi/source.rb
128
145
  - lib/oso/polar/host.rb
129
146
  - lib/oso/polar/polar.rb
130
147
  - lib/oso/polar/predicate.rb
@@ -134,9 +151,11 @@ files:
134
151
  - lib/oso/version.rb
135
152
  - oso-oso.gemspec
136
153
  homepage: https://www.osohq.com/
137
- licenses: []
154
+ licenses:
155
+ - Apache-2.0
138
156
  metadata:
139
157
  homepage_uri: https://www.osohq.com/
158
+ source_code_uri: https://github.com/osohq/oso
140
159
  post_install_message:
141
160
  rdoc_options: []
142
161
  require_paths:
@@ -156,5 +175,5 @@ rubyforge_project:
156
175
  rubygems_version: 2.6.14.4
157
176
  signing_key:
158
177
  specification_version: 4
159
- summary: Oso authorization API.
178
+ summary: oso authorization library.
160
179
  test_files: []