Class: HighLevel::TokenResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/high_level/token_resolver.rb

Overview

Resolves the bearer token for a single request based on OpenAPI security requirements + request data.

Ported from vendor/highlevel-api-sdk/lib/HighLevel.ts @ b1a1454 (getTokenForSecurity + extractResourceId).

Priority chain:

1. Configuration#private_integration_token always wins.
2. If the op declares Agency-Access-Only:
     agency_access_token, else storage(resource_id), else raise.
3. If the op declares Location-Access-Only:
     location_access_token, else storage(resource_id), else raise.
4. If the op declares Agency-Access, Location-Access, or bearer (flexible):
     agency_access_token, else location_access_token,
     else storage(resource_id), else raise.
5. No specific requirements: storage(resource_id), else raise.

Defined Under Namespace

Classes: Result

Constant Summary collapse

AGENCY_ONLY =

OpenAPI security requirement: agency token only.

"Agency-Access-Only"
LOCATION_ONLY =

OpenAPI security requirement: location token only.

"Location-Access-Only"
AGENCY_ACCESS =

OpenAPI security requirement: agency token (flexible).

"Agency-Access"
LOCATION_ACCESS =

OpenAPI security requirement: location token (flexible).

"Location-Access"
BEARER =

OpenAPI security requirement: generic bearer (location-style).

"bearer"
LOCATION_REQS =

Security requirements that call for a location-level token.

[LOCATION_ACCESS, LOCATION_ONLY, BEARER].freeze
AGENCY_REQS =

Security requirements that call for an agency-level token.

[AGENCY_ACCESS, AGENCY_ONLY].freeze

Instance Method Summary collapse

Constructor Details

#initialize(config:, storage: nil) ⇒ TokenResolver

Returns a new instance of TokenResolver.



42
43
44
45
# File 'lib/high_level/token_resolver.rb', line 42

def initialize(config:, storage: nil)
  @config = config
  @storage = storage
end

Instance Method Details

#extract_resource_id(security_requirements:, headers: {}, query: {}, body: {}, preference: nil) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/high_level/token_resolver.rb', line 80

def extract_resource_id(security_requirements:, headers: {}, query: {}, body: {}, preference: nil)
  headers_hash = stringify_keys(headers)

  company_id = first_present(headers_hash, "x-company-id", "companyId", "company-id")
  location_id = first_present(headers_hash, "x-location-id", "locationId", "location-id")

  company_id  ||= first_present(query, :companyId, "companyId", :company_id, "company_id")
  location_id ||= first_present(query, :locationId, "locationId", :location_id, "location_id")

  if company_id.nil? && location_id.nil? && body.is_a?(Hash)
    company_id  = first_present(body, :companyId, "companyId", :company_id, "company_id")
    location_id = first_present(body, :locationId, "locationId", :location_id, "location_id")
  end

  needs_location = security_requirements.intersect?(LOCATION_REQS)
  needs_agency   = security_requirements.intersect?(AGENCY_REQS)

  if needs_location && needs_agency
    return company_id  if preference == :company  && company_id
    return location_id if preference == :location && location_id
  end

  return location_id if needs_location
  return company_id  if needs_agency

  nil
end

#resolve(security_requirements: [], headers: {}, query: {}, body: {}, preference: nil) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength

Raises:



48
49
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
77
# File 'lib/high_level/token_resolver.rb', line 48

def resolve(security_requirements: [], headers: {}, query: {}, body: {}, preference: nil)
  requirements = Array(security_requirements)

  if @config.private_integration_token
    return Result.new(token: @config.private_integration_token,
                      source: :private_integration_token)
  end

  resource_id = extract_resource_id(
    security_requirements: requirements,
    headers: headers, query: query, body: body, preference: preference
  )

  if requirements.include?(AGENCY_ONLY)
    return resolve_only(@config.agency_access_token, :agency_access_token, resource_id,
                        "Agency Access Token required but not available")
  end

  if requirements.include?(LOCATION_ONLY)
    return resolve_only(@config.location_access_token, :location_access_token, resource_id,
                        "Location Access Token required but not available")
  end

  return resolve_flexible(resource_id) if requirements.intersect?([AGENCY_ACCESS, LOCATION_ACCESS, BEARER])

  storage_token = fetch_storage_token(resource_id)
  return Result.new(token: storage_token, source: :storage) if storage_token

  raise ConfigurationError, "No authentication token available"
end