configuration_service 2.3.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Rakefile +12 -0
- data/features/authorization.feature +22 -30
- data/features/consuming.feature +7 -8
- data/features/publishing.feature +9 -11
- data/features/step_definitions/authorization_steps.rb +17 -28
- data/features/step_definitions/consuming_steps.rb +3 -4
- data/features/step_definitions/publishing_steps.rb +0 -12
- data/lib/configuration_service/admin_client.rb +7 -12
- data/lib/configuration_service/base.rb +8 -13
- data/lib/configuration_service/client.rb +82 -0
- data/lib/configuration_service/factory.rb +8 -21
- data/lib/configuration_service/factory/context.rb +2 -0
- data/lib/configuration_service/factory/environment_context.rb +1 -1
- data/lib/configuration_service/factory/yaml_file_context.rb +1 -1
- data/lib/configuration_service/provider/stub.rb +31 -10
- data/lib/configuration_service/test/orchestration_provider.rb +57 -37
- data/lib/configuration_service/test/orchestrator.rb +25 -7
- data/lib/configuration_service/test/response.rb +2 -0
- data/lib/configuration_service/test/stub_orchestration_provider.rb +4 -4
- data/lib/configuration_service/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4a468a73fdaa5ac4d860611af3fe7f60bbb8533
|
4
|
+
data.tar.gz: 1c7cd9e09bf66113b28950938b00299a420d0cc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7475e17b49bd4430c01850923eebd2ad3e4d1b5cf764e2c1f16d21552cefd5d854cf1c963edf2942568ad42b2d9917db052e0bf223f149fe27815878c97f90fc
|
7
|
+
data.tar.gz: f024394e9df43fb5dfdc026c5cef4132f1529a85ae075af7c8205a264acb8d119f64eda80d646fc20e5950e5a5d9917276efcc5e88a0a9527c9320ca98711a2d
|
data/.rspec
CHANGED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
configuration_service
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.0
|
data/Rakefile
CHANGED
@@ -7,6 +7,18 @@ RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
8
8
|
desc "Test the stub test orchestrator"
|
9
9
|
task :test => :spec do
|
10
|
+
|
10
11
|
ENV["TEST_ORCHESTRATION_PROVIDER"] ||= "stub"
|
11
12
|
sh %{bundle exec cucumber}
|
12
13
|
end
|
14
|
+
|
15
|
+
namespace :test do
|
16
|
+
desc "Test includes"
|
17
|
+
task :includes do
|
18
|
+
sh %{cd lib && for i in configuration_service.rb $(find configuration_service -name '*.rb'); do
|
19
|
+
i=${i%.rb}
|
20
|
+
ruby -e '$LOAD_PATH.unshift("."); '"require '$i'; "'exit 0' || break
|
21
|
+
done}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -1,46 +1,38 @@
|
|
1
1
|
@authorization @authorize
|
2
2
|
Feature: Authorization
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Scenario:
|
8
|
-
Given
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
Scenario: Token request with invalid credentials
|
13
|
-
Given I have invalid credentials
|
14
|
-
When I request a token
|
15
|
-
Then I should not receive a token
|
16
|
-
|
17
|
-
Scenario: Token request with valid credentials
|
18
|
-
Given I have valid credentials
|
19
|
-
When I request a token
|
20
|
-
Then I should receive a token
|
21
|
-
|
22
|
-
Scenario: Allowed request
|
23
|
-
Given I have a token that allows requesting configurations
|
24
|
-
When I request a configuration
|
3
|
+
As the configuration service
|
4
|
+
I want to enforce authorization policies
|
5
|
+
In order to regulate client actions
|
6
|
+
|
7
|
+
Scenario: Allowed consumption
|
8
|
+
Given the specified configuration exists
|
9
|
+
And I am allowed to request configuration
|
10
|
+
When I request configuration data
|
25
11
|
Then I should be allowed to request configurations
|
26
12
|
|
27
|
-
Scenario: Denied
|
28
|
-
Given I
|
29
|
-
When I request
|
13
|
+
Scenario: Denied consumption
|
14
|
+
Given I am not allowed to request configuration
|
15
|
+
When I request configuration data
|
30
16
|
Then I should be notified that my request is 'not authorized'
|
31
17
|
|
32
18
|
Scenario: Allowed publication
|
33
|
-
Given I
|
34
|
-
When I request publication of
|
19
|
+
Given I am allowed to publish configurations
|
20
|
+
When I request publication of the configuration data
|
35
21
|
Then I should be allowed to publish configurations
|
36
22
|
|
37
23
|
Scenario: Denied publication
|
38
|
-
Given I
|
39
|
-
When I request publication of
|
24
|
+
Given I am not allowed to publish configurations
|
25
|
+
When I request publication of the configuration data
|
40
26
|
Then I should be notified that my request is 'not authorized'
|
41
27
|
|
28
|
+
Scenario: Allowed to create credentials for consuming configuration
|
29
|
+
Given the specified configuration exists
|
30
|
+
And I am allowed to authorize consumption of configuration
|
31
|
+
When I authorize consumption of the configuration
|
32
|
+
Then I receive credentials that only allow consumption of the configuration
|
33
|
+
|
42
34
|
Scenario: Token-less request
|
43
35
|
Given I do not have a token
|
44
|
-
When I request
|
36
|
+
When I request configuration data
|
45
37
|
Then I should be notified that my request is 'not authorized'
|
46
38
|
|
data/features/consuming.feature
CHANGED
@@ -5,16 +5,14 @@ Feature: Consuming configuration data
|
|
5
5
|
I want to request access to configuration data
|
6
6
|
|
7
7
|
Scenario: Metadata-free hit
|
8
|
-
Given
|
9
|
-
And
|
10
|
-
And configuration data at the index is present
|
8
|
+
Given I am allowed to request configuration
|
9
|
+
And the specified configuration exists
|
11
10
|
When I request configuration data
|
12
|
-
Then the configuration
|
11
|
+
Then the specified configuration should be returned
|
13
12
|
|
14
13
|
Scenario: Metadata-free miss
|
15
|
-
Given
|
16
|
-
And
|
17
|
-
And configuration data at the index is not present
|
14
|
+
Given I am allowed to request configuration
|
15
|
+
And the specified configuration does not exist
|
18
16
|
When I request configuration data
|
19
17
|
Then I should receive a 'not found' indication
|
20
18
|
|
@@ -37,7 +35,8 @@ Feature: Consuming configuration data
|
|
37
35
|
Then I should receive a 'no match' indication
|
38
36
|
|
39
37
|
Scenario: CS request failure
|
40
|
-
Given
|
38
|
+
Given I am allowed to request configuration
|
39
|
+
And a CS request failure
|
41
40
|
When I request configuration data
|
42
41
|
Then I should receive a 'request failure' notification
|
43
42
|
|
data/features/publishing.feature
CHANGED
@@ -5,39 +5,37 @@ Feature: Publishing configuration data
|
|
5
5
|
I want to publish configuration data
|
6
6
|
|
7
7
|
Scenario: Revision added to metadata
|
8
|
-
Given
|
9
|
-
And some configuration data
|
8
|
+
Given I am allowed to publish configurations
|
10
9
|
When I request publication of the configuration data
|
11
10
|
Then I receive a unique revision for my publication
|
12
11
|
And the metadata is updated with the revision by the CS
|
13
12
|
|
14
13
|
Scenario: Metadata saved with data
|
15
|
-
Given
|
14
|
+
Given I am allowed to publish configurations
|
16
15
|
And some metadata
|
17
|
-
And some configuration data
|
18
16
|
When I request publication of the configuration data
|
19
17
|
Then the metadata is also remembered
|
20
18
|
|
21
19
|
Scenario: Timestamp added to metadata
|
22
|
-
Given
|
23
|
-
And some metadata
|
24
|
-
And some configuration data
|
20
|
+
Given I am allowed to publish configurations
|
25
21
|
When I request publication of the configuration data
|
26
22
|
Then the entry is timestamped
|
27
23
|
|
28
24
|
Scenario: New revision for publication to existing identifier
|
29
|
-
Given
|
30
|
-
|
25
|
+
Given I am allowed to publish configurations
|
26
|
+
And the specified configuration exists
|
27
|
+
When I request publication of the configuration data
|
31
28
|
Then I receive a new unique revision for the publication
|
32
29
|
|
33
30
|
Scenario: Invalid data
|
34
|
-
Given
|
31
|
+
Given I am allowed to publish configurations
|
35
32
|
And invalid configuration data
|
36
33
|
When I request publication of the configuration data
|
37
34
|
Then I receive an 'invalid data' notification
|
38
35
|
|
39
36
|
Scenario: CS publication failure
|
40
|
-
Given
|
37
|
+
Given I am allowed to publish configurations
|
38
|
+
And a CS publication failure
|
41
39
|
When I request publication of the configuration data
|
42
40
|
Then I should receive a 'publication failure' notification
|
43
41
|
|
@@ -1,32 +1,8 @@
|
|
1
|
-
Given(/^I have no credentials$/) do
|
2
|
-
pending "Use case supported by Vault command-line client"
|
3
|
-
end
|
4
|
-
|
5
|
-
When(/^I request a token$/) do
|
6
|
-
pending "Use case supported by Vault command-line client"
|
7
|
-
end
|
8
|
-
|
9
|
-
Then(/^I should not receive a token$/) do
|
10
|
-
pending "Use case supported by Vault command-line client"
|
11
|
-
end
|
12
|
-
|
13
|
-
Given(/^I have invalid credentials$/) do
|
14
|
-
pending "Use case supported by Vault command-line client"
|
15
|
-
end
|
16
|
-
|
17
|
-
Given(/^I have valid credentials$/) do
|
18
|
-
pending "Use case supported by Vault command-line client"
|
19
|
-
end
|
20
|
-
|
21
|
-
Then(/^I should receive a token$/) do
|
22
|
-
pending "Use case supported by Vault command-line client"
|
23
|
-
end
|
24
|
-
|
25
1
|
Given(/^I do not have a token$/) do
|
26
2
|
@test.deauthorize
|
27
3
|
end
|
28
4
|
|
29
|
-
Given(/^I
|
5
|
+
Given(/^I am allowed to request configuration$/) do
|
30
6
|
@test.authorize(:requesting_configurations)
|
31
7
|
end
|
32
8
|
|
@@ -38,7 +14,7 @@ Then(/^I should be allowed to request configurations$/) do
|
|
38
14
|
expect(@test.request_allowed?).to eq true
|
39
15
|
end
|
40
16
|
|
41
|
-
Given(/^I
|
17
|
+
Given(/^I am not allowed to request configuration$/) do
|
42
18
|
@test.authorize(:nothing)
|
43
19
|
end
|
44
20
|
|
@@ -46,7 +22,7 @@ Then(/^I should be notified that my request is 'not authorized'$/) do
|
|
46
22
|
expect(@test.request_allowed?).to eq false
|
47
23
|
end
|
48
24
|
|
49
|
-
Given(/^I
|
25
|
+
Given(/^I am allowed to publish configurations$/) do
|
50
26
|
@test.authorize(:publishing_configurations)
|
51
27
|
end
|
52
28
|
|
@@ -58,6 +34,19 @@ Then(/^I should be allowed to publish configurations$/) do
|
|
58
34
|
expect(@test.request_allowed?).to eq true
|
59
35
|
end
|
60
36
|
|
61
|
-
Given(/^I
|
37
|
+
Given(/^I am not allowed to publish configurations$/) do
|
62
38
|
@test.authorize(:requesting_configurations)
|
63
39
|
end
|
40
|
+
|
41
|
+
Given(/^I am allowed to authorize consumption of configuration$/) do
|
42
|
+
@test.authorize(:admin)
|
43
|
+
end
|
44
|
+
|
45
|
+
When(/^I authorize consumption of the configuration$/) do
|
46
|
+
@test.authorize_consumption
|
47
|
+
end
|
48
|
+
|
49
|
+
Then(/^I receive credentials that only allow consumption of the configuration$/) do
|
50
|
+
expect(@test.credentials_allow_consumption?).to eq true
|
51
|
+
expect(@test.credentials_allow_publication?).to eq false
|
52
|
+
end
|
@@ -6,19 +6,18 @@ Then(/^I should receive a 'no match' indication$/) do
|
|
6
6
|
expect(@test.request_not_matched?).to eq true
|
7
7
|
end
|
8
8
|
|
9
|
-
Then(/^the configuration
|
9
|
+
Then(/^the specified configuration should be returned$/) do
|
10
10
|
expect(@test.configuration_found_for_identifier?).to eq true
|
11
11
|
end
|
12
12
|
|
13
13
|
Given(/^no meta data filter$/) do
|
14
14
|
end
|
15
15
|
|
16
|
-
Given(/^
|
16
|
+
Given(/^the specified configuration exists$/) do
|
17
17
|
@test.given_existing_configuration
|
18
18
|
end
|
19
19
|
|
20
20
|
When(/^I request configuration data$/) do
|
21
|
-
@test.authorize(:requesting_configurations)
|
22
21
|
@test.request_configuration
|
23
22
|
end
|
24
23
|
|
@@ -26,7 +25,7 @@ Then(/^the configuration data should be returned for the index and meta data fil
|
|
26
25
|
pending "Searching with metadata filters not yet supported"
|
27
26
|
end
|
28
27
|
|
29
|
-
Given(/^
|
28
|
+
Given(/^the specified configuration does not exist$/) do
|
30
29
|
@test.given_missing_configuration
|
31
30
|
end
|
32
31
|
|
@@ -1,11 +1,4 @@
|
|
1
|
-
Given(/^an identifier$/) do
|
2
|
-
end
|
3
|
-
|
4
|
-
Given(/^some configuration data$/) do
|
5
|
-
end
|
6
|
-
|
7
1
|
When(/^I request publication of the configuration data$/) do
|
8
|
-
@test.authorize(:publishing_configurations)
|
9
2
|
@test.publish_configuration
|
10
3
|
end
|
11
4
|
|
@@ -33,11 +26,6 @@ Given(/^existing configuration data$/) do
|
|
33
26
|
@test.given_existing_configuration
|
34
27
|
end
|
35
28
|
|
36
|
-
When(/^I request publication of configuration data using the existing identifier$/) do
|
37
|
-
@test.authorize(:publishing_configurations)
|
38
|
-
@test.publish_configuration
|
39
|
-
end
|
40
|
-
|
41
29
|
Then(/^I receive a new unique revision for the publication$/) do
|
42
30
|
expect(@test.published_revision).to_not eq @test.existing_revision
|
43
31
|
end
|
@@ -1,16 +1,17 @@
|
|
1
|
-
require "configuration_service/
|
1
|
+
require "configuration_service/client"
|
2
2
|
|
3
3
|
module ConfigurationService
|
4
4
|
class AdminClient
|
5
5
|
|
6
6
|
##
|
7
|
+
# @deprecated use ConfigurationService::Client instead
|
7
8
|
# @param [String] token
|
8
9
|
# @param [String] provider
|
9
10
|
# @return [ConfigurationService::AdminClient] object
|
10
11
|
#
|
11
12
|
def initialize(token, provider)
|
12
|
-
|
13
|
-
@
|
13
|
+
warn "[DEPRECATION] 'ConfigurationService::AdminClient' is deprecated. Please use 'ConfigurationService::Client'."
|
14
|
+
@client = ConfigurationService::Client.new(token, provider)
|
14
15
|
end
|
15
16
|
|
16
17
|
##
|
@@ -19,8 +20,7 @@ module ConfigurationService
|
|
19
20
|
# @raise [ConfigurationService::ConfigurationNotFoundError]
|
20
21
|
#
|
21
22
|
def request_configuration(identifier)
|
22
|
-
@
|
23
|
-
raise ConfigurationNotFoundError, "configuration not found for identifier: #{identifier}"
|
23
|
+
@client.request_configuration(identifier)
|
24
24
|
end
|
25
25
|
|
26
26
|
##
|
@@ -31,14 +31,9 @@ module ConfigurationService
|
|
31
31
|
# @raise [ConfigurationaService::Error] if data or metadata is not a hash
|
32
32
|
#
|
33
33
|
def publish_configuration(identifier, data, metadata = {})
|
34
|
-
|
35
|
-
Utils.dictionary?(metadata) or raise ConfigurationService::Error, "metadata must be a dictionary"
|
36
|
-
|
37
|
-
metadata = Utils.decorate(metadata)
|
38
|
-
configuration = Configuration.new(identifier, data, metadata)
|
39
|
-
@provider.publish_configuration(configuration, @token)
|
34
|
+
@client.publish_configuration(identifier, data, metadata)
|
40
35
|
end
|
41
|
-
|
36
|
+
|
42
37
|
end
|
43
38
|
|
44
39
|
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require "
|
2
|
-
require "configuration_service/utils"
|
1
|
+
require "configuration_service/client"
|
3
2
|
|
4
3
|
module ConfigurationService
|
5
4
|
|
@@ -15,6 +14,8 @@ module ConfigurationService
|
|
15
14
|
##
|
16
15
|
# Creates a new configuration service API instance
|
17
16
|
#
|
17
|
+
# @deprecated use ConfigurationService::Client instead
|
18
|
+
#
|
18
19
|
# @param [String] identifier
|
19
20
|
# the unique identity of some configuration data and its associated metadata.
|
20
21
|
# It might be the name of a service or service component. It might include an environment label
|
@@ -30,9 +31,9 @@ module ConfigurationService
|
|
30
31
|
# a configured service provider instance, to which requests will be delegated.
|
31
32
|
#
|
32
33
|
def initialize(identifier, token, provider)
|
34
|
+
warn "[DEPRECATION] 'ConfigurationService::Base' is deprecated. Please use 'ConfigurationService::Client'."
|
33
35
|
@identifier = identifier
|
34
|
-
@
|
35
|
-
@provider = provider
|
36
|
+
@client = ConfigurationService::Client.new(token, provider)
|
36
37
|
end
|
37
38
|
|
38
39
|
##
|
@@ -45,8 +46,7 @@ module ConfigurationService
|
|
45
46
|
# @raise [ConfigurationService::Error] if the request failed
|
46
47
|
#
|
47
48
|
def request_configuration
|
48
|
-
@
|
49
|
-
raise ConfigurationNotFoundError, "configuration not found for identifier: #{@identifier}"
|
49
|
+
@client.request_configuration(@identifier)
|
50
50
|
end
|
51
51
|
|
52
52
|
##
|
@@ -70,13 +70,8 @@ module ConfigurationService
|
|
70
70
|
# @return [ConfigurationService::Configuration] object containing the configuration data, decorated metadata and identifier
|
71
71
|
# @raise [ConfigurationService::Error] if publication failed
|
72
72
|
#
|
73
|
-
def
|
74
|
-
|
75
|
-
Utils.dictionary?(metadata) or raise ConfigurationService::Error, "metadata must be a dictionary"
|
76
|
-
|
77
|
-
metadata = Utils.decorate(metadata)
|
78
|
-
configuration = Configuration.new(@identifier, data, metadata)
|
79
|
-
@provider.publish_configuration(configuration, @token)
|
73
|
+
def publish_configuraion(data, metadata = {})
|
74
|
+
@client.publish_configuration(@identifier, data, metadata = {})
|
80
75
|
end
|
81
76
|
|
82
77
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "configuration_service/utils"
|
2
|
+
|
3
|
+
module ConfigurationService
|
4
|
+
class Client
|
5
|
+
|
6
|
+
##
|
7
|
+
# Creates a new instance of configuration service client
|
8
|
+
#
|
9
|
+
# @param [String] credentials
|
10
|
+
# opaque string representing authority to access the configuration data and associated metadata.
|
11
|
+
# Interpretation of the credentials is provider-dependent; it is opaque to the API and client.
|
12
|
+
# @param [Object] provider
|
13
|
+
# a configured service provider instance, to which requests will be delegated.
|
14
|
+
# @return [ConfigurationService::Client] client to interact with Configuration::Service
|
15
|
+
#
|
16
|
+
def initialize(credentials, provider)
|
17
|
+
@credentials = credentials
|
18
|
+
@provider = provider
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Requests the configuration data and metadata
|
23
|
+
# Delegates the request to the configured +provider+.
|
24
|
+
#
|
25
|
+
# @param [String] identifier unique identifier of configuration
|
26
|
+
# @return [ConfigurationService::Configuration] object containing the configuration data, metadata and identifier
|
27
|
+
# @raise [ConfigurationService::ConfigurationNotFoundError] if no configuration was found for the configured +identifier+
|
28
|
+
# @raise [ConfigurationService::Error] if the request failed
|
29
|
+
#
|
30
|
+
def request_configuration(identifier)
|
31
|
+
@provider.request_configuration(identifier, @credentials) or
|
32
|
+
raise ConfigurationNotFoundError, "configuration not found for identifier: #{identifier}"
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Publishes configuration data and metadata
|
37
|
+
#
|
38
|
+
# The +metadata+ is decorated with the following keys:
|
39
|
+
#
|
40
|
+
# * "timestamp" - the current UTC time in ISO8601 format
|
41
|
+
# * "revision" - a UUID for this publication
|
42
|
+
#
|
43
|
+
# Delegates the request to the configured +provider+. The provider may further decorate the +metadata+.
|
44
|
+
#
|
45
|
+
# It is recommended that both the +data+ and +metadata+ dictionaries use strings as keys,
|
46
|
+
# and that values be limited to those that can be serialized to JSON.
|
47
|
+
#
|
48
|
+
# @param [String] identifier
|
49
|
+
# unique configuration identifier
|
50
|
+
# @param [Hash] data
|
51
|
+
# dictionary of probably sensitive configuration data intended for an application, which providers are expected to secure
|
52
|
+
# @param [Hash] metadata
|
53
|
+
# dictionary of data about the configuration data, which providers are not expected to secure
|
54
|
+
# @return [ConfigurationService::Configuration] object containing the configuration data, decorated metadata and identifier
|
55
|
+
# @raise [ConfigurationService::Error] if publication failed
|
56
|
+
#
|
57
|
+
def publish_configuration(identifier, data, metadata = {})
|
58
|
+
Utils.dictionary?(data) or raise ConfigurationService::Error, "data must be a dictionary"
|
59
|
+
Utils.dictionary?(metadata) or raise ConfigurationService::Error, "metadata must be a dictionary"
|
60
|
+
|
61
|
+
metadata = Utils.decorate(metadata)
|
62
|
+
configuration = Configuration.new(identifier, data, metadata)
|
63
|
+
@provider.publish_configuration(configuration, @credentials)
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Authorize consumption of a configuration ie. get credentials to consume configurations ie. get a token to consume configurations
|
68
|
+
#
|
69
|
+
# @param [String] identifier
|
70
|
+
# unique identifier of configuration
|
71
|
+
# @return [String] credentials allowing consumption of the provided configuration
|
72
|
+
# @raise [ConfigurationService::ConfigurationNotFoundError] if no configuration was found for the configured +identifier+
|
73
|
+
# @raise [ConfigurationService::Error] if the request failed
|
74
|
+
##
|
75
|
+
def authorize_consumption(identifier)
|
76
|
+
@provider.authorize_consumption(identifier, @credentials) or
|
77
|
+
raise ConfigurationNotFoundError, "configuration not found for identifier: #{identifier}"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
require "configuration_service/factory/context"
|
2
|
+
require "configuration_service/factory/environment_context"
|
3
|
+
require "configuration_service/factory/yaml_file_context"
|
2
4
|
|
3
5
|
module ConfigurationService
|
4
6
|
|
@@ -60,32 +62,17 @@ module ConfigurationService
|
|
60
62
|
def self.create_client(context = EnvironmentContext.new)
|
61
63
|
context = Context.new(context) if context.is_a?(Hash)
|
62
64
|
provider = create_provider(context)
|
63
|
-
ConfigurationService::
|
65
|
+
ConfigurationService::Client.new(context.token, provider)
|
64
66
|
ensure
|
65
67
|
context.scrub!
|
66
68
|
end
|
67
69
|
|
68
70
|
##
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
# the factory context
|
73
|
-
# @return [ConfigurationService::AdminClient] the configuration service admin client created
|
74
|
-
#
|
75
|
-
# When the +context+ is a Hash, it is wrapped in a {ConfigurationService::Factory::Context}, which does not scrub
|
76
|
-
# sources after the configuration service client is created. For this reason, the process +ENV+ should never
|
77
|
-
# be given as the +context+; rather give no +context+, so that the default {ConfigurationService::Factory::EnvironmentContext}
|
78
|
-
# will be used to safely scrub the process +ENV+ and (on JRuby) system properties after the configuration service client is created.
|
79
|
-
#
|
80
|
-
# Because the {ConfigurationService::AdminClient} operates over multiple configuration identifiers,
|
81
|
-
# it does not require an +identifier+ property from the context.
|
82
|
-
#
|
71
|
+
# @deprecated use create_client()
|
72
|
+
# @ see create_client()
|
73
|
+
##
|
83
74
|
def self.create_admin_client(context = EnvironmentContext.new)
|
84
|
-
|
85
|
-
provider = create_provider(context)
|
86
|
-
ConfigurationService::AdminClient.new(context.token, provider)
|
87
|
-
ensure
|
88
|
-
context.scrub!
|
75
|
+
create_client(context)
|
89
76
|
end
|
90
77
|
|
91
78
|
private
|
@@ -71,7 +71,7 @@ module ConfigurationService
|
|
71
71
|
.select { |envvar| envvar.start_with?(config_prefix) && @scrub_keys << envvar }
|
72
72
|
.map { |envvar, value| [envvar.sub(config_prefix, "").downcase.intern, value] }
|
73
73
|
.to_h
|
74
|
-
@env = SymbolicAccessWrapper.new(env)
|
74
|
+
@env = Context::SymbolicAccessWrapper.new(env)
|
75
75
|
end
|
76
76
|
|
77
77
|
##
|
@@ -22,7 +22,7 @@ module ConfigurationService
|
|
22
22
|
:admin => 'b72b9ae7-3849-a335-67b6-administrator',
|
23
23
|
:consumer => '64867ebd-6364-0bd3-3fda-81-requestor',
|
24
24
|
:publisher => 'f53606cb-7f3c-4432-afe8-44-publisher',
|
25
|
-
:
|
25
|
+
:none => '2972abd7-b055-4841-8ad1-4a34-nothing',
|
26
26
|
} unless defined?(BUILTIN_TOKENS)
|
27
27
|
|
28
28
|
##
|
@@ -41,14 +41,19 @@ module ConfigurationService
|
|
41
41
|
#
|
42
42
|
# Fetches configuration from the singleton {ConfigurationService::StubStore}.
|
43
43
|
#
|
44
|
+
# @param [String] identifier
|
45
|
+
# unique configuration identifier
|
46
|
+
# @param [String] credentials
|
47
|
+
# token for authentication and authorization
|
48
|
+
#
|
44
49
|
# @return [ConfigurationService::Configuration] if authorized and found
|
45
50
|
# @return [nil] if not found
|
46
51
|
# @raise [ConfigurationService::AuthorizationError] if not authorized for the +:consumer+ role
|
47
52
|
#
|
48
53
|
# @see ConfigurationService::Base#request_configuration
|
49
54
|
#
|
50
|
-
def request_configuration(identifier,
|
51
|
-
authorize_request(:consumer,
|
55
|
+
def request_configuration(identifier, credentials)
|
56
|
+
authorize_request(:consumer, credentials)
|
52
57
|
data, metadata = @configurations.fetch(identifier)
|
53
58
|
Configuration.new(identifier, data, metadata)
|
54
59
|
rescue KeyError
|
@@ -58,25 +63,41 @@ module ConfigurationService
|
|
58
63
|
##
|
59
64
|
# Publish configuration
|
60
65
|
#
|
61
|
-
# @param [ConfigurationService::Configuration] configuration
|
66
|
+
# @param [ConfigurationService::Configuration] configuration
|
67
|
+
# configuration to publish
|
68
|
+
# @param [String] credentials
|
69
|
+
# token to authenticate and authorize
|
62
70
|
#
|
63
71
|
# @return [ConfigurationService::Configuration] published configuration
|
64
72
|
# @raise [ConfigurationService::Error] on failure.
|
65
73
|
#
|
66
74
|
# @see ConfigurationService::Base#publish_configuration
|
67
75
|
#
|
68
|
-
def publish_configuration(configuration,
|
69
|
-
authorize_request(:publisher,
|
76
|
+
def publish_configuration(configuration, credentials)
|
77
|
+
authorize_request(:publisher, credentials)
|
70
78
|
@configurations.store(configuration.identifier, configuration.data, configuration.metadata)
|
71
79
|
configuration
|
72
80
|
end
|
73
81
|
|
82
|
+
##
|
83
|
+
# Authorizes consumption of configuration
|
84
|
+
#
|
85
|
+
# @param [String] identifier of configuration to be consumed
|
86
|
+
# @param [String] credentials that allow me to authorize consumption of the configuration
|
87
|
+
#
|
88
|
+
# @return [String] credentials allowing consumption of the configuration
|
89
|
+
##
|
90
|
+
def authorize_consumption(identifier, credentials)
|
91
|
+
authorize_request(:admin, credentials)
|
92
|
+
BUILTIN_TOKENS[:consumer]
|
93
|
+
end
|
94
|
+
|
74
95
|
private
|
75
96
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
97
|
+
def authorize_request(role, credentials)
|
98
|
+
raise AuthorizationError.new("missing credentials") unless credentials
|
99
|
+
raise AuthorizationError.new("authorization failed") unless credentials == BUILTIN_TOKENS[role] or credentials == BUILTIN_TOKENS[:admin]
|
100
|
+
end
|
80
101
|
|
81
102
|
end
|
82
103
|
|
@@ -21,12 +21,13 @@ module ConfigurationService
|
|
21
21
|
# * {#service_provider_configuration}
|
22
22
|
# * {#service_provider}
|
23
23
|
# * {#broken_service_provider}
|
24
|
-
# * {#
|
24
|
+
# * {#credentials_for}
|
25
25
|
# * {#delete_configuration}
|
26
26
|
#
|
27
27
|
class OrchestrationProvider
|
28
28
|
|
29
29
|
ACTIVITY_ROLE_MAP = {
|
30
|
+
:admin => :admin,
|
30
31
|
:requesting_configurations => :consumer,
|
31
32
|
:publishing_configurations => :publisher,
|
32
33
|
:nothing => :none # if possible, provide credentials that don't allow operations on the configuration identifier
|
@@ -84,7 +85,7 @@ module ConfigurationService
|
|
84
85
|
#
|
85
86
|
# Deleting non-existent configuration should not produce an error.
|
86
87
|
# The +@identifier+ instance variable may be used to identify the configuration to delete,
|
87
|
-
# but +@
|
88
|
+
# but +@credentials+ should not be used, because it may not be sufficiently authorized.
|
88
89
|
#
|
89
90
|
# @return [nil] always
|
90
91
|
#
|
@@ -93,7 +94,7 @@ module ConfigurationService
|
|
93
94
|
end
|
94
95
|
|
95
96
|
##
|
96
|
-
# Provide a
|
97
|
+
# Provide a credentials that authorizes a role
|
97
98
|
#
|
98
99
|
# Valid roles are:
|
99
100
|
#
|
@@ -101,27 +102,27 @@ module ConfigurationService
|
|
101
102
|
# * +:publisher+
|
102
103
|
# * +:nothing+
|
103
104
|
#
|
104
|
-
# Note that a
|
105
|
+
# Note that a credentials should be returned for +:nothing+, but the credentials should
|
105
106
|
# not be authorized to consume or publish to the +identifier+.
|
106
107
|
#
|
107
|
-
# @return [String] a
|
108
|
+
# @return [String] a credentials
|
108
109
|
#
|
109
|
-
def
|
110
|
-
raise NotImplementedError, "#{self.class} must implement
|
110
|
+
def credentials_for(role)
|
111
|
+
raise NotImplementedError, "#{self.class} must implement credentials_for(role)"
|
111
112
|
end
|
112
113
|
|
113
114
|
##
|
114
115
|
# @see ConfigurationService::Test::Orchestrator#authorize
|
115
116
|
#
|
116
117
|
def authorize(role)
|
117
|
-
@
|
118
|
+
@credentials = credentials_for(role)
|
118
119
|
end
|
119
120
|
|
120
121
|
##
|
121
122
|
# @see ConfigurationService::Test::Orchestrator#deauthorize
|
122
123
|
#
|
123
124
|
def deauthorize
|
124
|
-
@
|
125
|
+
@credentials = nil
|
125
126
|
end
|
126
127
|
|
127
128
|
##
|
@@ -178,10 +179,27 @@ module ConfigurationService
|
|
178
179
|
#
|
179
180
|
def request_configuration
|
180
181
|
wrap_response do
|
181
|
-
@requested_configuration = service.request_configuration
|
182
|
+
@requested_configuration = service.request_configuration(@identifier)
|
182
183
|
end
|
183
184
|
end
|
184
185
|
|
186
|
+
##
|
187
|
+
# Authorize consumption of a configuration
|
188
|
+
#
|
189
|
+
# @return [String] credentials allowing consumption of a configuration
|
190
|
+
##
|
191
|
+
def authorize_consumption
|
192
|
+
@credentials = service.authorize_consumption(@identifier)
|
193
|
+
end
|
194
|
+
|
195
|
+
def credentials_allow_consumption?
|
196
|
+
request_configuration
|
197
|
+
end
|
198
|
+
|
199
|
+
def credentials_allow_publication?
|
200
|
+
publish_configuration
|
201
|
+
end
|
202
|
+
|
185
203
|
##
|
186
204
|
# Publish configuration
|
187
205
|
#
|
@@ -197,9 +215,9 @@ module ConfigurationService
|
|
197
215
|
def publish_configuration
|
198
216
|
wrap_response do
|
199
217
|
if @metadata
|
200
|
-
service.publish_configuration(configuration, @metadata)
|
218
|
+
service.publish_configuration(@identifier, configuration, @metadata)
|
201
219
|
else
|
202
|
-
service.publish_configuration(configuration)
|
220
|
+
service.publish_configuration(@identifier, configuration)
|
203
221
|
end
|
204
222
|
end
|
205
223
|
end
|
@@ -241,37 +259,39 @@ module ConfigurationService
|
|
241
259
|
|
242
260
|
private
|
243
261
|
|
244
|
-
|
245
|
-
|
246
|
-
|
262
|
+
def configuration
|
263
|
+
@configuration ||= {"verbose" => true}
|
264
|
+
end
|
265
|
+
|
266
|
+
def service
|
247
267
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
service_provider
|
254
|
-
end
|
255
|
-
ConfigurationService.new(@identifier, @token, provider)
|
268
|
+
provider = service_provider
|
269
|
+
|
270
|
+
if @fail_next
|
271
|
+
@fail_next = false
|
272
|
+
provider = broken_service_provider
|
256
273
|
end
|
274
|
+
|
275
|
+
ConfigurationService::Client.new(@credentials, provider)
|
276
|
+
end
|
257
277
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
278
|
+
def authorized_as(role)
|
279
|
+
restore_credentials = @credentials
|
280
|
+
authorize(role)
|
281
|
+
yield.tap do
|
282
|
+
@credentials = restore_credentials
|
264
283
|
end
|
284
|
+
end
|
265
285
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
end
|
286
|
+
def wrap_response # :nodoc:
|
287
|
+
begin
|
288
|
+
ConfigurationService::Test::Response::Success.new(yield)
|
289
|
+
rescue ConfigurationService::ConfigurationNotFoundError
|
290
|
+
ConfigurationService::Test::Response::Success.new(nil)
|
291
|
+
rescue ConfigurationService::Error => e
|
292
|
+
ConfigurationService::Test::Response::Failure.new(e)
|
274
293
|
end
|
294
|
+
end
|
275
295
|
|
276
296
|
end
|
277
297
|
|
@@ -124,6 +124,23 @@ module ConfigurationService
|
|
124
124
|
@response = @provider.publish_configuration
|
125
125
|
end
|
126
126
|
|
127
|
+
##
|
128
|
+
# Get credentials to consume a configuration
|
129
|
+
##
|
130
|
+
def authorize_consumption
|
131
|
+
@provider.authorize_consumption
|
132
|
+
end
|
133
|
+
|
134
|
+
def credentials_allow_consumption?
|
135
|
+
@response = @provider.credentials_allow_consumption?
|
136
|
+
request_allowed?
|
137
|
+
end
|
138
|
+
|
139
|
+
def credentials_allow_publication?
|
140
|
+
@response = @provider.credentials_allow_publication?
|
141
|
+
request_allowed?
|
142
|
+
end
|
143
|
+
|
127
144
|
##
|
128
145
|
# Configuration for the requested identifier was found
|
129
146
|
#
|
@@ -263,10 +280,10 @@ module ConfigurationService
|
|
263
280
|
#
|
264
281
|
def bootstrapped_configuration_service_functional?
|
265
282
|
response = begin
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
283
|
+
ConfigurationService::Test::Response::Success.new(@service.request_configuration('test'))
|
284
|
+
rescue ConfigurationService::Error => e
|
285
|
+
ConfigurationService::Test::Response::Failure.new(e)
|
286
|
+
end
|
270
287
|
!response.failed?
|
271
288
|
end
|
272
289
|
|
@@ -281,9 +298,10 @@ module ConfigurationService
|
|
281
298
|
|
282
299
|
private
|
283
300
|
|
284
|
-
|
285
|
-
|
286
|
-
|
301
|
+
def role_for(activity)
|
302
|
+
ConfigurationService::Test::OrchestrationProvider::ACTIVITY_ROLE_MAP[activity]
|
303
|
+
end
|
304
|
+
|
287
305
|
end
|
288
306
|
|
289
307
|
end
|
@@ -19,6 +19,8 @@ module ConfigurationService
|
|
19
19
|
#
|
20
20
|
class Success
|
21
21
|
|
22
|
+
attr_reader :response
|
23
|
+
|
22
24
|
##
|
23
25
|
# @param [ConfigurationService::Configuration, nil] response
|
24
26
|
# a configuration service response, or +nil+ if a {ConfigurationService::ConfigurationNotFoundError} was raised
|
@@ -51,13 +51,13 @@ module ConfigurationService
|
|
51
51
|
end
|
52
52
|
|
53
53
|
##
|
54
|
-
# Provide
|
54
|
+
# Provide credentials that authorizes a role
|
55
55
|
#
|
56
|
-
# The
|
56
|
+
# The credentials are taken from {ConfigurationService::Provider::Stub::BUILTIN_TOKENS}
|
57
57
|
#
|
58
|
-
# @see ConfigurationService::Test::OrchestrationProvider#
|
58
|
+
# @see ConfigurationService::Test::OrchestrationProvider#credentials_for
|
59
59
|
#
|
60
|
-
def
|
60
|
+
def credentials_for(role)
|
61
61
|
ConfigurationService::Provider::Stub::BUILTIN_TOKENS[role]
|
62
62
|
end
|
63
63
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configuration_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sheldon Hearn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -90,6 +90,8 @@ files:
|
|
90
90
|
- ".gemspec"
|
91
91
|
- ".gitignore"
|
92
92
|
- ".rspec"
|
93
|
+
- ".ruby-gemset"
|
94
|
+
- ".ruby-version"
|
93
95
|
- ".travis.yml"
|
94
96
|
- ".yardopts"
|
95
97
|
- Gemfile
|
@@ -109,6 +111,7 @@ files:
|
|
109
111
|
- lib/configuration_service.rb
|
110
112
|
- lib/configuration_service/admin_client.rb
|
111
113
|
- lib/configuration_service/base.rb
|
114
|
+
- lib/configuration_service/client.rb
|
112
115
|
- lib/configuration_service/configuration.rb
|
113
116
|
- lib/configuration_service/errors.rb
|
114
117
|
- lib/configuration_service/factory.rb
|
@@ -155,4 +158,3 @@ signing_key:
|
|
155
158
|
specification_version: 4
|
156
159
|
summary: Configuration service
|
157
160
|
test_files: []
|
158
|
-
has_rdoc:
|