Class: ActionPolicy::AuthZEN::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/action_policy/authzen/client.rb

Constant Summary collapse

DEFAULT_PATHS =
{
  evaluation: "/access/v1/evaluation",
  evaluations: "/access/v1/evaluations",
  search_subject: "/access/v1/search/subject",
  search_resource: "/access/v1/search/resource",
  search_action: "/access/v1/search/action"
}.freeze
METADATA_ENDPOINTS =
{
  evaluation: "access_evaluation_endpoint",
  evaluations: "access_evaluations_endpoint",
  search_subject: "search_subject_endpoint",
  search_resource: "search_resource_endpoint",
  search_action: "search_action_endpoint"
}.freeze
EVALUATION_SEMANTICS =
%w[execute_all deny_on_first_deny permit_on_first_permit].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(endpoint: nil, base_url: nil, endpoints: {}, metadata_url: nil, headers: {}, open_timeout: 2, read_timeout: 5, ca_file: nil, trusted_origins: [], allow_http: false) ⇒ Client

Returns a new instance of Client.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/action_policy/authzen/client.rb', line 50

def initialize(endpoint: nil, base_url: nil, endpoints: {}, metadata_url: nil,
  headers: {}, open_timeout: 2, read_timeout: 5, ca_file: nil, trusted_origins: [], allow_http: false)
  raise ArgumentError, "base_url or endpoint is required" if endpoint.nil? && base_url.nil?

  @allow_http = allow_http == true
  @base_url = base_url.nil? ? nil : parse_base_url(base_url, "base_url")
  @pdp_identifier = base_url&.dup&.freeze
  @metadata_url = .nil? ? nil : parse_endpoint(, "metadata_url")
  @endpoints = {}
  @endpoints[:evaluation] = parse_endpoint(endpoint, "endpoint") unless endpoint.nil?
  endpoints.each do |operation, value|
    key = operation.to_sym
    raise ArgumentError, "unknown AuthZEN endpoint #{operation.inspect}" unless DEFAULT_PATHS.key?(key)

    @endpoints[key] = parse_endpoint(value, "endpoints[#{operation.inspect}]")
  end

  @headers = headers.dup.freeze
  @open_timeout = positive_timeout(open_timeout)
  @read_timeout = positive_timeout(read_timeout)
  @ca_file = ca_file
  @metadata = nil
  @trusted_origins = trusted_origins.map { |origin| normalize_origin(origin) }.freeze
  @discovered = false
rescue URI::InvalidURIError
  raise ArgumentError, "AuthZEN URLs must be absolute HTTP(S) URLs"
end

Instance Attribute Details

#metadata ⇒ Object (readonly)

Returns the value of attribute metadata.



41
42
43
# File 'lib/action_policy/authzen/client.rb', line 41

def 
  @metadata
end

Class Method Details

.discover(base_url:, metadata_url: nil, trusted_origins: [], **options) ⇒ Object



43
44
45
46
47
48
# File 'lib/action_policy/authzen/client.rb', line 43

def self.discover(base_url:, metadata_url: nil, trusted_origins: [], **options)
  unless (options.keys & %i[endpoint endpoints metadata discovered]).empty?
    raise ArgumentError, "discovery endpoints must come from PDP metadata"
  end
  new(base_url: base_url, metadata_url: , trusted_origins: trusted_origins, **options).tap(&:configuration)
end

Instance Method Details

#allowed?(**request) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/action_policy/authzen/client.rb', line 84

def allowed?(**request)
  evaluate(**request).fetch("decision")
end

#configuration ⇒ Object

Raises:

  • (ArgumentError)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/action_policy/authzen/client.rb', line 131

def configuration
  base = base_url
  raise ArgumentError, "configuration discovery requires base_url" if base.nil?

  response = get((base))
  raise HTTPError, response.code.to_i unless response.code == "200"
  validate_json_content_type(response, "AuthZEN metadata response")

   = parse_json_object(response.body, "Invalid AuthZEN metadata JSON response")
  (, base)
  endpoints = METADATA_ENDPOINTS.each_with_object({}) do |(operation, key), configured|
    configured[operation] = parse_endpoint(.fetch(key), key) if .key?(key)
  end
  @endpoints = endpoints
  @discovered = true
  @metadata = 
end

#each_action(**request) ⇒ Object



125
126
127
128
129
# File 'lib/action_policy/authzen/client.rb', line 125

def each_action(**request)
  return enum_for(:each_action, **request) unless block_given?

  each_search(:search_actions, request) { |entity| yield entity }
end

#each_resource(**request) ⇒ Object



119
120
121
122
123
# File 'lib/action_policy/authzen/client.rb', line 119

def each_resource(**request)
  return enum_for(:each_resource, **request) unless block_given?

  each_search(:search_resources, request) { |entity| yield entity }
end

#each_subject(**request) ⇒ Object



113
114
115
116
117
# File 'lib/action_policy/authzen/client.rb', line 113

def each_subject(**request)
  return enum_for(:each_subject, **request) unless block_given?

  each_search(:search_subjects, request) { |entity| yield entity }
end

#evaluate(subject:, action:, resource:, context: nil) ⇒ Object



78
79
80
81
82
# File 'lib/action_policy/authzen/client.rb', line 78

def evaluate(subject:, action:, resource:, context: nil)
  result = post(:evaluation, evaluation_payload(subject: subject, action: action, resource: resource, context: context))
  validate_decision(result, "AuthZEN response")
  result
end

#evaluations(evaluations: nil, subject: nil, action: nil, resource: nil, context: nil, options: nil) ⇒ Object



88
89
90
91
92
93
94
95
96
97
# File 'lib/action_policy/authzen/client.rb', line 88

def evaluations(evaluations: nil, subject: nil, action: nil, resource: nil, context: nil, options: nil)
  payload = evaluations_payload(evaluations: evaluations, subject: subject, action: action,
    resource: resource, context: context, options: options)
  result = post(:evaluations, payload)
  single_request = evaluations.nil? || evaluations.empty?
  count = single_request ? 1 : evaluations.length
  semantic = payload.dig("options", "evaluations_semantic") || "execute_all"
  validate_evaluations_response(result, count, semantic, single_request: single_request)
  result
end

#search_actions(subject:, resource:, context: nil, page: nil) ⇒ Object



109
110
111
# File 'lib/action_policy/authzen/client.rb', line 109

def search_actions(subject:, resource:, context: nil, page: nil)
  search(:search_action, :action, subject: subject, resource: resource, context: context, page: page)
end

#search_resources(subject:, action:, resource:, context: nil, page: nil) ⇒ Object



104
105
106
107
# File 'lib/action_policy/authzen/client.rb', line 104

def search_resources(subject:, action:, resource:, context: nil, page: nil)
  search(:search_resource, :resource, subject: subject, action: action, resource: resource,
    context: context, page: page)
end

#search_subjects(subject:, action:, resource:, context: nil, page: nil) ⇒ Object



99
100
101
102
# File 'lib/action_policy/authzen/client.rb', line 99

def search_subjects(subject:, action:, resource:, context: nil, page: nil)
  search(:search_subject, :subject, subject: subject, action: action, resource: resource,
    context: context, page: page)
end