configuration_service 2.3.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|