soar-registry-staff 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b870c2449a793ab6ce47a3b265be8eecd6f7d688
4
- data.tar.gz: dcab46f89e3425a949580f83a78f00ab240953fc
3
+ metadata.gz: 7f6b422c48fd608fa4d16edd4393b0a8113eed41
4
+ data.tar.gz: 9b63627ed150163f5862a36308068766d3e8c992
5
5
  SHA512:
6
- metadata.gz: 152fa38f09839258442d7e73b4f2a18124786d40f8770d4bc174c690e8efeb20afc386eca7851d4d2e92cf5e5eef4aaa2514edf4749ca63f334cbecca626b13e
7
- data.tar.gz: d8f7afdcbdf5607a9997d58a3b539e39e04a99afe144d5adaa0f4d9da5a8500b598147af83b458e4a05a05a17e0c8af477b48fde474620087755a73ce4b51c11
6
+ metadata.gz: 3cb61fbda6f6f42a5b3a3341ff6b9bd639cd7e4e37dc6cc1de1e8a3b997ecae65ed122b60dcf359caa6b1e0767469e25ace0cec7925cf45d871e87527c656d2a
7
+ data.tar.gz: 6aa66da6cd94c508c16552f36bab9c179e69ef32ba5bf74479d66f6ee69ab0302d52763f8d46873a560d4aa448e928d5a65924ea4c16b34c5227b9ed9f6b1b8e
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Registry of staff identities
2
- Allows you to query staff identities using the idm api.
3
-
4
- *Please note:* Querying staff entities requires a different idr just for entities.
2
+ Allows you to find staff identities by id using *Soar::Registry::Staff::Identity::Id* or email *Soar::Registry::Staff::Identity::Email*
5
3
 
6
4
  ## Quickstart
7
5
 
@@ -21,26 +19,31 @@ provider:
21
19
  password: 'secret'
22
20
  ```
23
21
 
24
- ### Create an instance of the staff identity registry
22
+ ### Create an instance of the chosen staff identity registry
23
+
24
+ #### Find by id
25
+ ```ruby
26
+ require 'soar/registry/staff/identity/id'
27
+ @id_idr = Soar::Registry::Staff::Identity::Id.new(configuration)
28
+ ```
29
+ #### Find by email
25
30
  ```ruby
26
- require 'soar/registry/staff/identity'
27
- @idr = Soar::Registry::Staff::Identity.new(configuration)
31
+ require 'soar/registry/staff/identity/email'
32
+ @email_idr = Soar::Registry::Staff::Identity::Email.new(configuration)
28
33
  ```
29
34
 
30
35
  ### Getting a list of identifiers
31
36
  ```ruby
32
- > identifiers = @idr.get_identifiers("identity-820d5660-2204-4f7d-8c04-746313439b81")
33
- > identifiers = @idr.get_identifiers({"email" => "admin@hetzner.co.za"}.to_json)
34
- > identifiers = @idr.get_identifiers({"identity_id" => "identity-820d5660-2204-4f7d-8c04-746313439b81"}.to_json)
37
+ > identifiers = @id_idr.get_identifiers("identity-820d5660-2204-4f7d-8c04-746313439b81")
38
+ > identifiers = @email_idr.get_identifiers("admin@hetzner.co.za")
35
39
  > puts identifiers.inspect
36
40
  ["admin@hetzner.co.za", "identity-820d5660-2204-4f7d-8c04-746313439b81"]
37
41
  ```
38
42
 
39
43
  ### Getting a list of roles
40
44
  ```ruby
41
- > roles = @idr.get_roles("identity-820d5660-2204-4f7d-8c04-746313439b81")
42
- > roles = @idr.get_roles({"email" => "admin@hetzner.co.za"}.to_json)
43
- > roles = @idr.get_roles({"identity_id" => "identity-820d5660-2204-4f7d-8c04-746313439b81"}.to_json)
45
+ > roles = @id_idr.get_roles("identity-820d5660-2204-4f7d-8c04-746313439b81")
46
+ > roles = @email_idr.get_roles("admin@hetzner.co.za")
44
47
  > puts roles.inspect
45
48
  ["staff", "configuration_publisher", "configuration_consumer"]
46
49
  ```
@@ -48,9 +51,8 @@ require 'soar/registry/staff/identity'
48
51
  ### Getting a hash of attributes for a role
49
52
  ```ruby
50
53
  > role = 'staff'
51
- > attributes = @idr.get_attributes("identity-820d5660-2204-4f7d-8c04-746313439b81", role)
52
- > attributes = @idr.get_attributes({"email" => "admin@hetzner.co.za"}.to_json, role)
53
- > attributes = @idr.get_attributes({"identity_id" => "identity-820d5660-2204-4f7d-8c04-746313439b81"}.to_json, role)
54
+ > attributes = @id_idr.get_attributes("identity-820d5660-2204-4f7d-8c04-746313439b81", role)
55
+ > attributes = @email_idr.get_attributes("admin@hetzner.co.za", role)
54
56
  > puts attributes.inspect
55
57
  {
56
58
  "staff": {
@@ -62,9 +64,8 @@ require 'soar/registry/staff/identity'
62
64
 
63
65
  ### Getting a hash of all attributes
64
66
  ```ruby
65
- > attributes = @idr.get_attributes("identity-820d5660-2204-4f7d-8c04-746313439b81")
66
- > attributes = @idr.get_attributes({"email" => "admin@hetzner.co.za"}.to_json)
67
- > attributes = @idr.get_attributes({"identity_id" => "identity-820d5660-2204-4f7d-8c04-746313439b81"}.to_json)
67
+ > attributes = @id_idr.get_attributes("identity-820d5660-2204-4f7d-8c04-746313439b81")
68
+ > attributes = @email_idr.get_attributes("admin@hetzner.co.za")
68
69
  > puts attributes.inspect
69
70
  {
70
71
  "identity_id" => "identity-820d5660-2204-4f7d-8c04-746313439b81",
@@ -89,38 +90,30 @@ require 'soar/registry/staff/identity'
89
90
  }
90
91
  ```
91
92
 
92
- ## Tests
93
93
 
94
- ### Quicktest
94
+ ## Quicktest
95
+
96
+ ### Local Stub
97
+ ```bash
98
+ TEST_ORCHESTRATION_PROVIDER=Stub rspec
99
+ ```
100
+
101
+ ### DynamoDb
95
102
  ```
96
- $ docker-compose --file docker-compose.test.yml up --abort-on-container-exit --remove-orphans
103
+ $ TEST_ORCHESTRATION_PROVIDER=DynamoDb docker-compose --file docker-compose.test.yml up --abort-on-container-exit --remove-orphans
97
104
  ```
98
105
 
99
106
  ### CI
100
107
  *NB:* container_name in docker-compose.ci.yml corresponds with name parameter of docker ps command in below bash script.
101
108
  ```bash
102
109
  #!/bin/bash
103
- docker-compose --file docker-compose.test.yml up --build --abort-on-container-exit --remove-orphans --force-recreate;
110
+ TEST_ORCHESTRATION_PROVIDER=DynamoDb docker-compose --file docker-compose.test.yml up --build --abort-on-container-exit --remove-orphans --force-recreate;
104
111
  EXIT_CODE=$(docker ps -a -f "name=soar-registry-staff" -q | xargs docker inspect -f "{{ .State.ExitCode }}");
105
112
  exit $EXIT_CODE;
106
113
  ```
107
114
 
108
- ### Unit tests
109
-
110
- #### Run dynamodb
111
- ```
112
- $ docker-compose up
113
- ```
114
-
115
- #### Open another terminal and run:
116
- ```
117
- $ sudo -H pip install awscli
118
- $ aws configure
119
- $ bundle
120
- $ rspec
121
- ```
122
115
 
123
- ### Useful commands:
116
+ ## Useful commands:
124
117
  ```
125
118
  $ aws dynamodb list-tables --endpoint-url http://localhost:8000
126
119
  $ aws dynamodb delete-table --table-name identities --endpoint-url http://localhost:8000
@@ -0,0 +1,151 @@
1
+ require 'soar_idm/directory_provider'
2
+ require 'aws-sdk'
3
+ require 'hashie'
4
+
5
+ module Soar
6
+ module Registry
7
+ module Staff
8
+ module Directory
9
+ class DynamoDb < SoarIdm::DirectoryProvider
10
+
11
+ LIMIT = 10
12
+ INDEXED_ATTRIBUTES = ['email', 'entity_id']
13
+
14
+ attr_reader :configuration, :credentials, :client
15
+
16
+ ##
17
+ # @param [Hash] configuration for the directory provider
18
+ # @return [Nil]
19
+ # @raise [Soar::Registry::Staff::Directory::Error::BootstrapError] if required configuration is missing
20
+ ##
21
+ def bootstrap(configuration)
22
+ @configuration = Hashie.stringify_keys(configuration)
23
+ raise Soar::Registry::Staff::Directory::Error::BootstrapError, 'Missing region' if not @configuration.has_key?('region')
24
+ raise Soar::Registry::Staff::Directory::Error::BootstrapError, 'Missing endpoint' if not @configuration.has_key?('endpoint')
25
+ @table_name = @configuration.delete('table')
26
+ raise Soar::Registry::Staff::Directory::Error::BootstrapError, 'Missing table name' if not @table_name
27
+ end
28
+
29
+ ##
30
+ # @return [Bool] whether directory provider received minimum required configuration
31
+ ##
32
+ def bootstrapped?
33
+ @configuration.has_key?('region') and @configuration.has_key?('endpoint')
34
+ end
35
+
36
+ def uri
37
+ @configuration['endpoint']
38
+ end
39
+
40
+ def authenticate(credentials)
41
+ raise Soar::Registry::Staff::Directory::Error::AuthenticationError, 'missing username' if not credentials.key?('username')
42
+ raise Soar::Registry::Staff::Directory::Error::AuthenticationError, 'missing password' if not credentials.key?('password')
43
+ @credentials = {
44
+ "access_key_id" => credentials['username'],
45
+ "secret_access_key" => credentials['password']
46
+ }
47
+ end
48
+
49
+ def connect
50
+ begin
51
+ options = @configuration
52
+ options['credentials'] = Aws::Credentials.new(@credentials['access_key_id'], @credentials['secret_access_key'])
53
+ @client = Aws::DynamoDB::Client.new(Hashie.symbolize_keys(options))
54
+ true
55
+ rescue ArgumentError => e
56
+ raise Soar::Registry::Staff::Directory::Error::ConnectionError, e.message
57
+ rescue
58
+ raise Soar::Registry::Staff::Directory::Error::ConnectionError, 'error creating client'
59
+ end
60
+ end
61
+
62
+ def connected?
63
+ return @client.class.name == 'Aws::DynamoDB::Client'
64
+ end
65
+
66
+ def ready?
67
+ begin
68
+ #return @client.list_tables.table_names.class.name == 'Array'
69
+ resp = @client.describe_table({
70
+ table_name: @table_name
71
+ })
72
+ return resp.table.table_name == @table_name
73
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException, Seahorse::Client::NetworkingError
74
+ return false
75
+ end
76
+ end
77
+
78
+ def put_identity(entity)
79
+ @client.put_item({
80
+ table_name: @table_name,
81
+ item: Hashie.symbolize_keys(entity)
82
+ })
83
+ end
84
+
85
+ ##
86
+ # @param [String] identity_id primary key of the identity
87
+ # @return [Hash] the identity
88
+ # @raise [Soar::Registry::Staff::Directory::DynamoDb::Error::UniqueIdentifierNotFoundError] if primary key not found
89
+ ##
90
+ def fetch_identity(identity_id)
91
+ options = {
92
+ table_name: @table_name,
93
+ key: {
94
+ identity_id: identity_id
95
+ }
96
+ }
97
+ identity = @client.get_item(options)
98
+ raise Soar::Registry::Staff::Directory::Error::UniqueIdentifierNotFoundError, 'Unable to find identity' if identity.item.nil?
99
+ identity.item
100
+ end
101
+
102
+ ##
103
+ # @param [String] identifier_attribute
104
+ # @param [String] identifier_value
105
+ # @return [Array] list of identities
106
+ # @raise [ArgumentError] if query or index is not specified
107
+ ##
108
+ def search_identities(identifier_attribute, identifier_value)
109
+ raise ArgumentError, "Attribute is required" if identifier_attribute.nil?
110
+ raise ArgumentError, 'Value is required' if identifier_value.nil?
111
+ options = {
112
+ table_name: @table_name,
113
+ select: 'ALL_ATTRIBUTES',
114
+ limit: LIMIT,
115
+ key_condition_expression: "#{identifier_attribute} = :value",
116
+ expression_attribute_values: {
117
+ ":value": identifier_value
118
+ }
119
+ }
120
+
121
+ options.merge!({index_name: "#{identifier_attribute}_index"}) if INDEXED_ATTRIBUTES.include?(identifier_attribute)
122
+ identity = @client.query(options)
123
+ identity.items.map { |item|
124
+ Hashie.stringify_keys(item)
125
+ }
126
+ end
127
+
128
+ ##
129
+ # @return [Array] a list of primary keys and global secondary indexes
130
+ ##
131
+ def indexed_attributes
132
+ resp = @client.describe_table({
133
+ table_name: @table_name
134
+ })
135
+ indexed_attributes = []
136
+ resp['table']['key_schema'].each { |key_schema|
137
+ indexed_attributes << key_schema['attribute_name']
138
+ }
139
+ resp['table']['global_secondary_indexes'].each { |index|
140
+ index['key_schema'].each { |key_schema|
141
+ indexed_attributes << key_schema['attribute_name']
142
+ }
143
+ }
144
+ indexed_attributes
145
+ end
146
+
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,107 @@
1
+ require 'soar_idm/directory_provider'
2
+ require 'soar/registry/staff/directory/error'
3
+ require 'aws-sdk'
4
+ require 'hashie'
5
+ require 'mince'
6
+ require 'hashy_db'
7
+
8
+ module Soar
9
+ module Registry
10
+ module Staff
11
+ module Directory
12
+ class Stub < SoarIdm::DirectoryProvider
13
+
14
+ attr_accessor :interface
15
+ attr_writer :network
16
+ attr_writer :connection
17
+
18
+ def initialize
19
+ @interface = Mince::HashyDb::Interface
20
+ @network = true
21
+ @connection = true
22
+ end
23
+
24
+ ##
25
+ # @param [Hash] configuration for the directory provider
26
+ # @return [Nil]
27
+ # @raise [Soar::Registry::Staff::Directory::Error::BootstrapError] if required configuration is missing
28
+ ##
29
+ def bootstrap(configuration)
30
+ @configuration = configuration
31
+ raise Soar::Registry::Staff::Directory::Error::BootstrapError if configuration.empty?
32
+ end
33
+
34
+ ##
35
+ # @return [Bool] whether directory provider received minimum required configuration
36
+ ##
37
+ def bootstrapped?
38
+ true
39
+ end
40
+
41
+ def uri
42
+ @configuration['endpoint']
43
+ end
44
+
45
+ def authenticate(credentials)
46
+ raise Soar::Registry::Staff::Directory::Error::AuthenticationError, 'missing username' if not credentials.key?('username')
47
+ raise Soar::Registry::Staff::Directory::Error::AuthenticationError, 'missing password' if not credentials.key?('password')
48
+ end
49
+
50
+ def connect
51
+ raise Soar::Registry::Staff::Directory::Error::ConnectionError if not @connection
52
+ true
53
+ end
54
+
55
+ def connected?
56
+ true
57
+ end
58
+
59
+ def ready?
60
+ @network
61
+ end
62
+
63
+ def put_identity(entity)
64
+ @interface.add('identities', entity)
65
+ end
66
+
67
+ ##
68
+ # @param [String] identity_id primary key of the identity
69
+ # @return [Hash] the identity
70
+ # @raise [Soar::Registry::Staff::Directory::DynamoDb::Error::UniqueIdentifierNotFoundError] if primary key not found
71
+ ##
72
+ def fetch_identity(identity_id)
73
+ identity = @interface.get_for_key_with_value('identities', "identity_id", identity_id)
74
+ raise Soar::Registry::Staff::Directory::Error::UniqueIdentifierNotFoundError if identity.nil?
75
+ identity
76
+ end
77
+
78
+ ##
79
+ # @param [String] identifier_attribute
80
+ # @param [String] identifier_value
81
+ # @return [Array] list of identities
82
+ # @raise [ArgumentError] if query or index is not specified
83
+ ##
84
+ def search_identities(identifier_attribute, identifier_value)
85
+ identities = []
86
+ @interface.find_all('identities').each { |identity|
87
+ if identity[identifier_attribute] == identifier_value
88
+ identities << identity
89
+ else
90
+ next
91
+ end
92
+ }
93
+ identities
94
+ end
95
+
96
+ ##
97
+ # @return [Array] a list of primary keys and global secondary indexes
98
+ ##
99
+ def indexed_attributes
100
+ ["identity_id", "email", "entity_id"]
101
+ end
102
+
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,84 @@
1
+ require 'soar_idm/soar_idm'
2
+ require 'soar/registry/staff/translator/dynamo_db'
3
+ require 'soar/registry/staff/directory/dynamo_db'
4
+
5
+ module Soar
6
+ module Registry
7
+ module Staff
8
+ module Identity
9
+ class Base < SoarIdm::IdmApi
10
+
11
+ attr_reader :directory
12
+ attr_reader :translator
13
+ attr_reader :client
14
+
15
+ PRIMARY_KEY = 'identity_id'
16
+
17
+ ##
18
+ # @param [Hash] configuration
19
+ # for example see config/config.yml
20
+ ##
21
+ def initialize(configuration)
22
+ @translator = Object.const_get(configuration['rule_set']['adaptor']).new
23
+ @directory = Object::const_get(configuration['provider']['adaptor']).new()
24
+ @directory.bootstrap(configuration['provider']['config'])
25
+ @directory.authenticate(configuration['provider']['credentials'])
26
+ @client = @directory.connect
27
+ raise Soar::Registry::Staff::Directory::Error::BootstrapError if not @directory.bootstrapped?
28
+ raise Soar::Registry::Staff::Directory::Error::ConnectionError if not @directory.connected?
29
+ raise Soar::Registry::Staff::Directory::Error::NotReadyError if not @directory.ready?
30
+ end
31
+
32
+ ##
33
+ # @param [Hash] identity
34
+ # @return [Array] list of roles
35
+ def calculate_roles(identity)
36
+ entry = @directory.fetch_identity(identity[PRIMARY_KEY])
37
+ return nil if not entry
38
+ identity = @translator.get_identity(entry)
39
+ roles = []
40
+ identity['roles'].each do |role, attributes|
41
+ roles << role
42
+ end
43
+ roles
44
+ end
45
+
46
+ ##
47
+ # @param [Hash] identity
48
+ # @return [Array] list of identifiers
49
+ ##
50
+ def calculate_identifiers(identity)
51
+ indexes = @directory.indexed_attributes
52
+ entry = @directory.fetch_identity(identity[PRIMARY_KEY])
53
+ identity = @translator.get_identity(entry)
54
+ identifiers = []
55
+ indexes.each { |index|
56
+ identifiers << identity[index]
57
+ }
58
+ identifiers
59
+ end
60
+
61
+ ##
62
+ # @param [Hash] identity
63
+ # @param [String] role
64
+ # @return [Hash] A hash of attributes
65
+ def calculate_attributes(identity, role)
66
+ entry = @directory.fetch_identity(identity[PRIMARY_KEY])
67
+ return nil if not entry
68
+ identity = @translator.get_identity(entry)
69
+ { role => identity['roles'][role] }
70
+ end
71
+
72
+ ##
73
+ # @param [Hash] identity
74
+ # @return [Hash] Hash of attributes keyed by role
75
+ def calculate_all_attributes(identity)
76
+ entry = @directory.fetch_identity(identity[PRIMARY_KEY])
77
+ @translator.get_identity(entry)
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end