require 'json' require 'net/http' require 'uri' require 'oso/version' module Oso class Client def initialize(url: 'https://cloud.osohq.com', api_key: nil) @url = url @api_key = api_key end def policy(policy) POST('policy', { src: policy }) end def authorize(actor, action, resource) actor_typed_id = extract_typed_id actor resource_typed_id = extract_typed_id resource result = POST('authorize', { actor_type: actor_typed_id.type, actor_id: actor_typed_id.id, action: action, resource_type: resource_typed_id.type, resource_id: resource_typed_id.id }) allowed = result['allowed'] allowed end def list(actor, action, resource_type) actor_typed_id = extract_typed_id actor result = POST('list', { actor_type: actor_typed_id.type, actor_id: actor_typed_id.id, action: action, resource_type: resource_type, }) results = result['results'] results end def tell(predicate, *args) typed_args = args.map { |a| extract_typed_id a} POST('facts', { predicate: predicate, args: typed_args }) end def bulk_tell(facts) params = facts.map { |predicate, *args| typed_args = args.map { |a| extract_typed_id a} { predicate: predicate, args: typed_args } } POST('bulk_load', params) end def delete(predicate, *args) typed_args = args.map { |a| extract_typed_id a} DELETE('facts', { predicate: predicate, args: typed_args }) end def bulk_delete(facts) params = facts.map { |predicate, *args| typed_args = args.map { |a| extract_typed_id a} { predicate: predicate, args: typed_args } } POST('bulk_delete', params) end def get(predicate, *args) params = {predicate: predicate} args.each_with_index do |arg, i| typed_id = extract_arg_query(arg) if typed_id params["args.#{i}.type"] = typed_id.type params["args.#{i}.id"] = typed_id.id end end GET('facts', params) end private def headers() { "Authorization" => "Basic %s" % @api_key, "User-Agent" => "Oso Cloud (ruby)", "Accept": "application/json", "Content-Type": "application/json" } end def GET(path, params) uri = URI("#{@url}/api/#{path}") uri.query = URI::encode_www_form(params) use_ssl = (uri.scheme == 'https') result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl ) { |http| http.request(Net::HTTP::Get.new(uri, headers)) {|r| r.read_body } } handle_result result end def POST(path, params) result = Net::HTTP.post(URI("#{@url}/api/#{path}"), params.to_json, headers) handle_result result end def DELETE(path, params) uri = URI("#{@url}/api/#{path}") use_ssl = (uri.scheme == 'https') result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: use_ssl ) { |http| http.request(Net::HTTP::Delete.new(uri, headers), params.to_json) {|r| r.read_body } } handle_result result end def handle_result(result) unless result.is_a?(Net::HTTPSuccess) raise "Got an unexpected error from Oso Service: #{result.code}\n#{result.body}" end JSON.parse(result.body) end def extract_typed_id(x) return TypedId.new(type: "String", id: x) if x.is_a? String raise "#{x} does not have an 'id' field" unless x.respond_to? :id raise "Invalid 'id' field on #{x}: #{x.id}" if x.id.nil? TypedId.new(type: x.class.name, id: x.id.to_s) end def extract_arg_query(x) return nil if x.nil? extract_typed_id(x) end TypedId = Struct.new(:type, :id, keyword_init: true) do def to_json(*args) to_h.to_json(*args) end end end end