Skip to content

Commit

Permalink
Merge pull request #167 from anders-larsson/api_multiple_urls
Browse files Browse the repository at this point in the history
Allow multiple API servers/URLs with token
  • Loading branch information
anders-larsson authored Apr 2, 2024
2 parents 1c47494 + a25e14d commit 87cdc16
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 117 deletions.
62 changes: 50 additions & 12 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

* [`vas::api_fetch`](#vas--api_fetch): Query a remote HTTP-based service for entries to be added to users_allow.

### Data types

* [`Vas::API::Config`](#Vas--API--Config): API configuration

## Classes

### <a name="vas"></a>`vas`
Expand Down Expand Up @@ -167,6 +171,7 @@ The following parameters are available in the `vas` class:
* [`api_users_allow_url`](#-vas--api_users_allow_url)
* [`api_token`](#-vas--api_token)
* [`api_ssl_verify`](#-vas--api_ssl_verify)
* [`api_config`](#-vas--api_config)

##### <a name="-vas--manage_nis"></a>`manage_nis`

Expand Down Expand Up @@ -1186,6 +1191,7 @@ Default value: `false`
Data type: `Optional[Stdlib::HTTPSUrl]`

The URL towards the API.
Deprecated parameter, replaced by $api_config. Will be removed next major releaase.

Default value: `undef`

Expand All @@ -1194,6 +1200,7 @@ Default value: `undef`
Data type: `Optional[String[1]]`

Security token for authenticated access to the API.
Deprecated parameter, replaced by $api_config. Will be removed next major releaase.

Default value: `undef`

Expand All @@ -1202,9 +1209,18 @@ Default value: `undef`
Data type: `Boolean`

Whether TLS connections should be verified or not.
Deprecated parameter, replaced by $api_config. Will be removed next major releaase

Default value: `false`

##### <a name="-vas--api_config"></a>`api_config`

Data type: `Optional[Vas::API::Config]`

API configuration

Default value: `undef`

## Functions

### <a name="vas--api_fetch"></a>`vas::api_fetch`
Expand All @@ -1218,10 +1234,19 @@ Query a remote HTTP-based service for entries to be added to users_allow.
##### Calling the function

```puppet
vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
```

##### Multiple servers with different tokens, ssl_verify enabled

```puppet
vas::api_fetch([
{'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
{'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
])
```

#### `vas::api_fetch(Stdlib::HTTPUrl $url, String[1] $token, Optional[Boolean] $ssl_verify)`
#### `vas::api_fetch(Vas::API::Config $config)`

Query a remote HTTP-based service for entries to be added to users_allow.

Expand All @@ -1232,24 +1257,37 @@ Returns: `Hash` Key 'content' with [Array] if API responds. Key 'errors' with [A
###### Calling the function

```puppet
vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
```

##### `url`
###### Multiple servers with different tokens, ssl_verify enabled

Data type: `Stdlib::HTTPUrl`
```puppet
vas::api_fetch([
{'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
{'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
])
```

URL to connect to
##### `config`

##### `token`
Data type: `Vas::API::Config`

Data type: `String[1]`
Hash with API configuration

Token used for authentication
## Data types

##### `ssl_verify`
### <a name="Vas--API--Config"></a>`Vas::API::Config`

Data type: `Optional[Boolean]`
API configuration

Alias of

Whether TLS connections should be verified or not
```puppet
Array[Struct[
url => Stdlib::HttpsUrl,
token => Optional[String[1]],
ssl_verify => Optional[Boolean],
]]
```

78 changes: 44 additions & 34 deletions lib/puppet/functions/vas/api_fetch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,62 @@
require 'net/http'
require 'net/https'
require 'openssl'
# @param url URL to connect to
# @param token Token used for authentication
# @param ssl_verify Whether TLS connections should be verified or not
# @param config Hash with API configuration
# @return [Hash] Key 'content' with [Array] if API responds. Key 'errors' with [Array[String]] if errors happens.
# @example Calling the function
# vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
# vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
# @example Multiple servers with different tokens, ssl_verify enabled
# vas::api_fetch([
# {'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
# {'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
# ])
#
dispatch :api_fetch do
param 'Stdlib::HTTPUrl', :url
param 'String[1]', :token
optional_param 'Boolean', :ssl_verify
param 'Vas::API::Config', :config
return_type 'Hash'
end

def api_fetch(url, token, ssl_verify = false)
uri = URI.parse(url)
def api_fetch(config)
data = {}

req = Net::HTTP::Get.new(uri.to_s)
req['Authorization'] = "Bearer #{token}"
req['Accept'] = 'text/plain'
config.shuffle.each do |entry|
url = entry['url']
uri = URI.parse(url)

https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
unless ssl_verify
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
https.open_timeout = 2
https.read_timeout = 2
req = Net::HTTP::Get.new(uri.to_s)
req['Authorization'] = "Bearer #{entry['token']}" if entry.key?('token')
req['Accept'] = 'text/plain'

data = {}
begin
response = https.start do |cx|
cx.request(req)
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
# Set SSL::VERIFY_NONE if key ssl_verify is not present or if set to false
# Should be true by default in next major release
if !entry.key?('ssl_verify') || !entry['ssl_verify']
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
https.open_timeout = 2
https.read_timeout = 2

begin
response = https.start do |cx|
cx.request(req)
end

case response
when Net::HTTPSuccess
data['content'] = if response.body.empty?
[]
else
response.body.split("\n")
end
else
(data['errors'] ||= []) << "#{url} returns HTTP code: #{response.code}"
case response
when Net::HTTPSuccess
data['content'] = if response.body.empty?
[]
else
response.body.split("\n")
end
# Successful response received, break loop
break
else
(data['errors'] ||= []) << "#{url} returns HTTP code: #{response.code}"
end
rescue => error
(data['errors'] ||= []) << "#{url} connection failed: #{error.message}"
end
rescue => error
(data['errors'] ||= []) << "#{url} connection failed: #{error.message}"
end

data
Expand Down
27 changes: 23 additions & 4 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,18 @@
#
# @param api_users_allow_url
# The URL towards the API.
# Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
#
# @param api_token
# Security token for authenticated access to the API.
# Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
#
# @param api_ssl_verify
# Whether TLS connections should be verified or not.
# Deprecated parameter, replaced by $api_config. Will be removed next major releaase
#
# @param api_config
# API configuration
class vas (
Boolean $manage_nis = true,
String[1] $package_version = 'installed',
Expand Down Expand Up @@ -587,6 +593,7 @@
Array[String[1]] $kpasswd_servers = [],
Stdlib::Port $kpasswd_server_port = 464,
Boolean $api_enable = false,
Optional[Vas::API::Config] $api_config = undef,
Optional[Stdlib::HTTPSUrl] $api_users_allow_url = undef,
Optional[String[1]] $api_token = undef,
Boolean $api_ssl_verify = false,
Expand Down Expand Up @@ -673,10 +680,22 @@
}

# functionality
if $api_enable == true and ($api_users_allow_url == undef or $api_token == undef) {
fail('vas::api_enable is set to true but required parameters vas::api_users_allow_url and/or vas::api_token missing')
} elsif $api_enable == true {
$api_users_allow_data = vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify)
if $api_enable == true {
if $api_config {
$api_config_real = $api_config
} elsif $api_users_allow_url and $api_token {
warning('$api_users_allow_url and $api_token deprecated and will be removed next major release. Use $api_config')

$api_config_real = [{
'url' => $api_users_allow_url,
'token' => $api_token,
'ssl_verify' => $api_ssl_verify,
}]
} else {
fail('vas::api_enable is set to true but required parameter $api_config missing')
}

$api_users_allow_data = vas::api_fetch($api_config_real)

if $api_users_allow_data['content'] {
$manage_users_allow = true
Expand Down
21 changes: 10 additions & 11 deletions spec/classes/data_types_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@
],
}

headers = {
'Accept' => 'text/plain',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Authorization' => 'Bearer somesecret',
'User-Agent' => 'Ruby'
}

on_supported_os(test_on).each do |_os, os_facts|
describe 'variable data type and content validations' do
let(:node) { 'data-types.example.com' }
Expand Down Expand Up @@ -49,7 +42,7 @@
},
'Boolean/API' => {
name: ['api_enable'],
params: { api_users_allow_url: 'https://api.example.local', api_token: 'somesecret', },
params: { api_config: [{ 'url': 'https://api.example.local', 'token': 'somesecret' }] },
valid: [true, false],
invalid: ['true', 'false', ['array'], { 'ha' => 'sh' }, 3, 2.42, nil],
message: 'expects a Boolean',
Expand Down Expand Up @@ -172,7 +165,15 @@
invalid: ['true', 'false', 'string', ['array'], { 'ha' => 'sh' }, 3, 2.42],
message: 'expects a value of type Boolean or Enum',
},

'Vas::API::Config' => {
name: ['api_config'],
params: { api_enable: true },
valid: [[{ 'url': 'https://test.ing' }],
[{ 'url': 'https://test.ing', 'token': 'mysecret', 'ssl_verify': true }],
[{ 'url': 'https://test.ing', 'token': 'mysecret', 'ssl_verify': true }, { 'url': 'https://test.ing' }]],
invalid: ['http://str.ing', 'string', ['array'], { 'ha' => 'sh' }, 3, 2.42, false],
message: "(parameter 'api_config' expects a Vas::API::Config|parameter 'api_config' index 0 expects a Struct value)",
},
}
validations.sort.each do |type, var|
mandatory_params = {} if mandatory_params.nil?
Expand All @@ -184,10 +185,8 @@

it do
stub_request(:get, 'https://test.ing/')
.with(headers: headers)

stub_request(:get, 'https://api.example.local')
.with(headers: headers)

is_expected.to compile
end
Expand Down
Loading

0 comments on commit 87cdc16

Please sign in to comment.