soar-registry-staff 0.0.3 → 0.0.4

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