oso-cloud 0.2.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f79239f03cdcaddbb6a8c9800b4c83a5d28b4acccd3cae2875a9585306c1531e
4
- data.tar.gz: 59764d372207e4beda482bddf6f4489573a640d61ba9d064d2644931ffe6dde0
3
+ metadata.gz: ae0bc3e70a6d9ea8aeb9f6f02af64afa1ca65099ea036d75d2a611bed86e3f83
4
+ data.tar.gz: d46f670990a2ffff708c3a72d54f9bed2da6602e8699d3ffab3108d18750851a
5
5
  SHA512:
6
- metadata.gz: f68bf249055cf164b296a945a3149e0b4328f6da4843105b9455c9e864dd87d7b25374cb8cc2d3c76f971368b9944bd72784e15f36564ba5f9ed89ca538f955e
7
- data.tar.gz: be9850f671f35bc24f64061b9975e92aba2c66d5c6ce9c4b33bbf2027c4e4cba7b7e62bef2f82d1057b1ed15763022d51d32d8200397629050145e271520bc45
6
+ metadata.gz: 118581772825e045e4f1237a43d8d8077fef30c2f8118d9886ab5d3bbfdbdfa1de383216b2426520a0aee0e0b61239a0153c177cac48de874b4832fac87b50a3
7
+ data.tar.gz: 6a66ab888f95a43e64ae69e3d8e9c378077da17d5dd580185b25b77371c532bb2ec169df9cdd6781cf0f43b11a68ded664d26ed9efff0d877f9019e75967a2aa
data/.gitignore CHANGED
@@ -1,3 +1,2 @@
1
- Gemfile.lock
2
1
  .bundle
3
2
  vendor
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ oso-cloud (0.4.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.15.0)
10
+ rake (12.3.3)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ minitest (~> 5.15)
17
+ oso-cloud!
18
+ rake (~> 12.0)
19
+
20
+ BUNDLED WITH
21
+ 2.3.13
data/Rakefile CHANGED
@@ -1,2 +1,9 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
data/lib/oso/client.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'json'
2
- require 'logger'
3
2
  require 'net/http'
4
3
  require 'uri'
5
4
 
@@ -7,103 +6,117 @@ require 'oso/version'
7
6
 
8
7
  module Oso
9
8
  class Client
10
- def initialize(url: 'https://cloud.osohq.com', api_key: nil, logger: nil)
9
+ def initialize(url: 'https://cloud.osohq.com', api_key: nil)
11
10
  @url = url
12
11
  @api_key = api_key
13
- @logger = logger || Logger.new(STDOUT)
14
- # TODO: why does this need to be configurable?
15
- # @to_type_and_id = method(default_to_type_and_id)
12
+ end
13
+
14
+ def policy(policy)
15
+ POST('policy', { src: policy })
16
16
  end
17
17
 
18
18
  def authorize(actor, action, resource)
19
- actor_type, actor_id = default_to_type_and_id actor
20
- resource_type, resource_id = default_to_type_and_id resource
21
- result = post('authorize', {
22
- actor_type: actor_type, actor_id: actor_id,
23
- action: action,
24
- resource_type: resource_type, resource_id: resource_id
25
- })
19
+ actor_typed_id = extract_typed_id actor
20
+ resource_typed_id = extract_typed_id resource
21
+ result = POST('authorize', {
22
+ actor_type: actor_typed_id.type, actor_id: actor_typed_id.id,
23
+ action: action,
24
+ resource_type: resource_typed_id.type, resource_id: resource_typed_id.id
25
+ })
26
26
  allowed = result['allowed']
27
- @logger.debug { "AUTHORIZING (#{actor}, #{action}, #{resource}) => ALLOWED? = #{allowed ? 'true' : 'false'} " }
28
-
29
27
  allowed
30
28
  end
31
29
 
32
30
  def list(actor, action, resource_type)
33
- actor_type, actor_id = default_to_type_and_id actor
34
- result = post('list',
35
- {
36
- actor_type: actor_type, actor_id: actor_id,
37
- action: action,
38
- resource_type: resource_type
39
- })
31
+ actor_typed_id = extract_typed_id actor
32
+ result = POST('list', {
33
+ actor_type: actor_typed_id.type, actor_id: actor_typed_id.id,
34
+ action: action,
35
+ resource_type: resource_type,
36
+ })
40
37
  results = result['results']
41
- @logger.debug { "AUTHORIZING (#{actor}, #{action}, #{resource_type}). RESULTS: #{results}" }
42
-
43
38
  results
44
39
  end
45
40
 
46
- def add_role(actor, role_name, resource)
47
- add_role_or_relation('role', resource, role_name, actor)
41
+ def tell(predicate, *args)
42
+ typed_args = args.map { |a| extract_typed_id a}
43
+ POST('facts', { predicate: predicate, args: typed_args })
48
44
  end
49
45
 
50
- def delete_role(actor, role_name, resource)
51
- delete_role_or_relation('role', resource, role_name, actor)
46
+ def bulk_tell(facts)
47
+ params = facts.map { |predicate, *args|
48
+ typed_args = args.map { |a| extract_typed_id a}
49
+ { predicate: predicate, args: typed_args }
50
+ }
51
+ POST('bulk_load', params)
52
52
  end
53
53
 
54
- def add_relation(subject, name, object)
55
- add_role_or_relation('relation', subject, name, object)
54
+ def delete(predicate, *args)
55
+ typed_args = args.map { |a| extract_typed_id a}
56
+ DELETE('facts', { predicate: predicate, args: typed_args })
56
57
  end
57
58
 
58
- def delete_relation(subject, name, object)
59
- delete_role_or_relation('relation', subject, name, object)
59
+ def bulk_delete(facts)
60
+ params = facts.map { |predicate, *args|
61
+ typed_args = args.map { |a| extract_typed_id a}
62
+ { predicate: predicate, args: typed_args }
63
+ }
64
+ POST('bulk_delete', params)
60
65
  end
61
66
 
62
- def get_roles(resource: nil, role: nil, actor: nil)
63
- params = {}
64
- unless actor.nil?
65
- actor_type, actor_id = default_to_type_and_id actor
66
- params[:actor_type] = actor_type
67
- params[:actor_id] = actor_id
68
- end
69
- unless resource.nil?
70
- resource_type, resource_id = default_to_type_and_id resource
71
- params[:resource_type] = resource_type
72
- params[:resource_id] = resource_id
67
+ def get(predicate, *args)
68
+ params = {predicate: predicate}
69
+ args.each_with_index do |arg, i|
70
+ typed_id = extract_arg_query(arg)
71
+ if typed_id
72
+ params["args.#{i}.type"] = typed_id.type
73
+ params["args.#{i}.id"] = typed_id.id
74
+ end
73
75
  end
74
- params[:role] = role unless role.nil?
75
76
 
76
- get('roles')
77
+ GET('facts', params)
77
78
  end
78
79
 
79
80
  private
80
81
 
81
- def auth()
82
- {"Authorization" => "Basic %s" % @api_key}
82
+ def headers()
83
+ {
84
+ "Authorization" => "Basic %s" % @api_key,
85
+ "User-Agent" => "Oso Cloud (ruby)",
86
+ "Accept": "application/json",
87
+ "Content-Type": "application/json"
88
+ }
83
89
  end
84
90
 
85
- def get(path)
86
- result = Net::HTTP.get(URI("#{@url}/api/#{path}"), auth)
87
- handle_result result
88
- end
89
91
 
90
- def post(path, params)
91
- result = Net::HTTP.post(URI("#{@url}/api/#{path}"), params.to_json, auth)
92
+ def GET(path, params)
93
+ uri = URI("#{@url}/api/#{path}")
94
+ uri.query = URI::encode_www_form(params)
95
+ use_ssl = (uri.scheme == 'https')
96
+
97
+ result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl ) { |http|
98
+ http.request(Net::HTTP::Get.new(uri, headers)) {|r|
99
+ r.read_body
100
+ }
101
+ }
92
102
  handle_result result
103
+
93
104
  end
94
105
 
95
- def delete(path, params)
96
- result = Net::HTTP.delete(URI("#{@url}/api/#{path}"), params.to_json, auth)
106
+ def POST(path, params)
107
+ result = Net::HTTP.post(URI("#{@url}/api/#{path}"), params.to_json, headers)
97
108
  handle_result result
98
109
  end
99
110
 
100
- # TODO: why does this need to be configurable?
101
- def default_to_type_and_id(obj)
102
- if obj.nil?
103
- %w[null null]
104
- else
105
- [obj.class.to_s, obj.id.to_s]
106
- end
111
+ def DELETE(path, params)
112
+ uri = URI("#{@url}/api/#{path}")
113
+ use_ssl = (uri.scheme == 'https')
114
+ result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl ) { |http|
115
+ http.request(Net::HTTP::Delete.new(uri, headers), params.to_json) {|r|
116
+ r.read_body
117
+ }
118
+ }
119
+ handle_result result
107
120
  end
108
121
 
109
122
  def handle_result(result)
@@ -111,32 +124,27 @@ module Oso
111
124
  raise "Got an unexpected error from Oso Service: #{result.code}\n#{result.body}"
112
125
  end
113
126
 
114
- # TODO: Always JSON?
115
127
  JSON.parse(result.body)
116
128
  end
117
129
 
118
- def to_params(role_or_relation, from, name, to)
119
- from_type, from_id = default_to_type_and_id from
120
- to_type, to_id = default_to_type_and_id to
130
+ def extract_typed_id(x)
131
+ return TypedId.new(type: "String", id: x) if x.is_a? String
121
132
 
122
- from_name = role_or_relation == 'role' ? 'resource' : 'from'
123
- to_name = role_or_relation == 'role' ? 'actor' : 'to'
133
+ raise "#{x} does not have an 'id' field" unless x.respond_to? :id
134
+ raise "Invalid 'id' field on #{x}: #{x.id}" if x.id.nil?
124
135
 
125
- {
126
- "#{from_name}_id" => from_id,
127
- "#{from_name}_type" => from_type,
128
- role_or_relation.to_s => name,
129
- "#{to_name}_id" => to_id,
130
- "#{to_name}_type" => to_type
131
- }
136
+ TypedId.new(type: x.class.name, id: x.id.to_s)
132
137
  end
133
138
 
134
- def add_role_or_relation(role_or_relation, from, name, to)
135
- post("#{role_or_relation}s", to_params(role_or_relation, from, name, to))
139
+ def extract_arg_query(x)
140
+ return nil if x.nil?
141
+ extract_typed_id(x)
136
142
  end
137
143
 
138
- def delete_role_or_relation(role_or_relation, from, name, to)
139
- delete("#{role_or_relation}s", to_params(role_or_relation, from, name, to))
144
+ TypedId = Struct.new(:type, :id, keyword_init: true) do
145
+ def to_json(*args)
146
+ to_h.to_json(*args)
147
+ end
140
148
  end
141
149
  end
142
150
  end
data/lib/oso/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Oso
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/oso-cloud.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.summary = 'Oso authorization library.'
9
9
  spec.homepage = 'https://www.osohq.com/'
10
10
 
11
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
11
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
12
12
 
13
13
  # Specify which files should be added to the gem when it is released.
14
14
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -18,4 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.bindir = 'exe'
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'minitest', '~> 5.15'
21
23
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oso-cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oso Security, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-01 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.15'
13
27
  description:
14
28
  email:
15
29
  - support@osohq.com
@@ -19,6 +33,7 @@ extra_rdoc_files: []
19
33
  files:
20
34
  - ".gitignore"
21
35
  - Gemfile
36
+ - Gemfile.lock
22
37
  - README.md
23
38
  - Rakefile
24
39
  - bin/console
@@ -37,14 +52,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
37
52
  requirements:
38
53
  - - ">="
39
54
  - !ruby/object:Gem::Version
40
- version: 2.3.0
55
+ version: 2.7.0
41
56
  required_rubygems_version: !ruby/object:Gem::Requirement
42
57
  requirements:
43
58
  - - ">="
44
59
  - !ruby/object:Gem::Version
45
60
  version: '0'
46
61
  requirements: []
47
- rubygems_version: 3.1.2
62
+ rubygems_version: 3.1.6
48
63
  signing_key:
49
64
  specification_version: 4
50
65
  summary: Oso authorization library.