Class: GlobiGuard::Transport

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

Constant Summary collapse

RESERVED_HEADERS =
%w[
  x-globiguard-project-id
  x-globiguard-secret-key
  x-globiguard-publishable-key
  x-globiguard-local-mode
  x-globiguard-local-token
  x-globiguard-client
  x-globiguard-environment
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(environment:, services:, credential:) ⇒ Transport

Returns a new instance of Transport.

Raises:

  • (ArgumentError)


66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/globiguard.rb', line 66

def initialize(environment:, services:, credential:)
  raise ArgumentError, "Environment must be local, sandbox, or live." unless ENVIRONMENTS.include?(environment)
  raise ArgumentError, "Credential environment must match client environment." unless credential.environment == environment

  @environment = environment
  @services = services
  @credential = credential
  @base_uri = URI(services.fetch("controlPlane"))
  raise ArgumentError, "HTTPS is required outside local." if environment != "local" && @base_uri.scheme != "https"
  if credential.kind == "local" && !%w[localhost 127.0.0.1 ::1].include?(@base_uri.host)
    raise ArgumentError, "Local credentials require localhost or loopback URLs."
  end
end

Class Method Details

.validate_path(path) ⇒ Object

Raises:

  • (ArgumentError)


114
115
116
117
118
119
120
121
# File 'lib/globiguard.rb', line 114

def self.validate_path(path)
  raise ArgumentError, "Request path must start with /." unless path.start_with?("/")
  if path.start_with?("//") || path.include?("\\") || path.include?("?") || path.include?("#") || path.match?(/%(?![0-9A-Fa-f]{2})/)
    raise ArgumentError, "Unsafe request path."
  end
  raise ArgumentError, "Absolute request paths are not allowed." if URI(path).absolute?
  raise ArgumentError, "Dot segments are not allowed." if path.split("/").any? { |segment| segment == "." || segment == ".." }
end

Instance Method Details

#auth_headersObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/globiguard.rb', line 99

def auth_headers
  headers = {
    "x-globiguard-client" => "globiguard-ruby/#{VERSION}",
    "x-globiguard-environment" => @environment
  }
  if @credential.kind == "local"
    headers["x-globiguard-local-mode"] = "true"
    headers["x-globiguard-local-token"] = @credential.token if @credential.token && !@credential.token.empty?
  else
    headers["x-globiguard-project-id"] = require_value(@credential.project_id, "project id")
    headers[@credential.kind == "secret" ? "x-globiguard-secret-key" : "x-globiguard-publishable-key"] = require_value(@credential.token, "credential token")
  end
  headers
end

#request(method, path, body: nil, headers: {}) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/globiguard.rb', line 80

def request(method, path, body: nil, headers: {})
  self.class.validate_path(path)
  headers.each_key do |name|
    raise ArgumentError, "Reserved GlobiGuard header cannot be overridden: #{name}" if RESERVED_HEADERS.include?(name.downcase)
  end

  uri = URI(@base_uri.to_s.sub(%r{/+\z}, "") + path)
  request = Net::HTTP.const_get(method.capitalize).new(uri)
  auth_headers.merge(headers).each { |name, value| request[name] = value }
  request["content-type"] = "application/json" if body
  request.body = JSON.generate(body) if body
  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https", read_timeout: 30, open_timeout: 30) do |http|
    http.request(request)
  end
  raise "GlobiGuard request failed with #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)

  response.body.nil? || response.body.empty? ? {} : JSON.parse(response.body)
end