scooter 0.0.0 → 3.2.19

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.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/.env +5 -0
  3. data/.gitignore +47 -19
  4. data/Gemfile +3 -0
  5. data/HISTORY.md +1539 -0
  6. data/README.md +69 -10
  7. data/Rakefile +7 -0
  8. data/docs/http_dispatchers.md +79 -0
  9. data/lib/scooter.rb +11 -3
  10. data/lib/scooter/httpdispatchers.rb +12 -0
  11. data/lib/scooter/httpdispatchers/activity.rb +46 -0
  12. data/lib/scooter/httpdispatchers/activity/v1/v1.rb +50 -0
  13. data/lib/scooter/httpdispatchers/classifier.rb +376 -0
  14. data/lib/scooter/httpdispatchers/classifier/v1/v1.rb +99 -0
  15. data/lib/scooter/httpdispatchers/code_manager.rb +31 -0
  16. data/lib/scooter/httpdispatchers/code_manager/v1/v1.rb +17 -0
  17. data/lib/scooter/httpdispatchers/consoledispatcher.rb +132 -0
  18. data/lib/scooter/httpdispatchers/httpdispatcher.rb +168 -0
  19. data/lib/scooter/httpdispatchers/orchestrator/v1/v1.rb +87 -0
  20. data/lib/scooter/httpdispatchers/orchestratordispatcher.rb +83 -0
  21. data/lib/scooter/httpdispatchers/puppetdb/v4/v4.rb +51 -0
  22. data/lib/scooter/httpdispatchers/puppetdbdispatcher.rb +390 -0
  23. data/lib/scooter/httpdispatchers/rbac.rb +231 -0
  24. data/lib/scooter/httpdispatchers/rbac/v1/directory_service.rb +68 -0
  25. data/lib/scooter/httpdispatchers/rbac/v1/v1.rb +116 -0
  26. data/lib/scooter/ldap.rb +349 -0
  27. data/lib/scooter/ldap/ldap_fixtures.rb +60 -0
  28. data/lib/scooter/middleware/rbac_auth_token.rb +35 -0
  29. data/lib/scooter/utilities.rb +9 -0
  30. data/lib/scooter/utilities/beaker_utilities.rb +41 -0
  31. data/lib/scooter/utilities/string_utilities.rb +32 -0
  32. data/lib/scooter/version.rb +3 -1
  33. data/scooter.gemspec +23 -6
  34. data/spec/scooter/beaker_utilities_spec.rb +53 -0
  35. data/spec/scooter/httpdispatchers/activity/activity_spec.rb +218 -0
  36. data/spec/scooter/httpdispatchers/classifier/classifier_spec.rb +542 -0
  37. data/spec/scooter/httpdispatchers/code_manager/code-manager_spec.rb +67 -0
  38. data/spec/scooter/httpdispatchers/consoledispatcher_spec.rb +80 -0
  39. data/spec/scooter/httpdispatchers/httpdispatcher_spec.rb +91 -0
  40. data/spec/scooter/httpdispatchers/middleware/rbac_auth_token_spec.rb +58 -0
  41. data/spec/scooter/httpdispatchers/orchestratordispatcher_spec.rb +195 -0
  42. data/spec/scooter/httpdispatchers/puppetdbdispatcher_spec.rb +246 -0
  43. data/spec/scooter/httpdispatchers/rbac/rbac_spec.rb +387 -0
  44. data/spec/scooter/string_utilities_spec.rb +83 -0
  45. data/spec/spec_helper.rb +8 -0
  46. metadata +270 -18
  47. data/LICENSE.txt +0 -15
@@ -0,0 +1,231 @@
1
+ %w( v1 ).each do |lib|
2
+ require "scooter/httpdispatchers/rbac/v1/#{lib}"
3
+ end
4
+
5
+ module Scooter
6
+ module HttpDispatchers
7
+ # Methods added here are not representative of endpoints, but are more
8
+ # generalized to be helper methods to to acquire data, such as getting
9
+ # the id of a user based on their login name. Be cautious about using
10
+ # these methods if you are utilizing a dispatcher with credentials;
11
+ # the user is not guaranteed to have privileges for all the methods
12
+ # defined here, or the user may not be signed in. If you have a method
13
+ # defined here that is using the connection object directly, you should
14
+ # probably be using a method defined in the version module instead.
15
+ module Rbac
16
+
17
+ include Scooter::HttpDispatchers::Rbac::V1
18
+ include Scooter::Utilities
19
+
20
+ def set_rbac_path(connection=self.connection)
21
+ set_url_prefix
22
+ connection.url_prefix.path = '/rbac-api'
23
+ end
24
+
25
+ def generate_local_user(options = {})
26
+ email = options['email'] || "#{RandomString.generate(8)}@example.com"
27
+ display_name = options['display_name'] || RandomString.generate(8)
28
+ login = options['login'] || RandomString.generate(16)
29
+ role_ids = options['role_ids'] || []
30
+ password = options['password'] || 'Puppet11'
31
+
32
+ user_hash = { 'email' => email,
33
+ 'display_name' => display_name,
34
+ 'login' => login,
35
+ 'role_ids' => role_ids,
36
+ 'password' => password }
37
+
38
+ response = create_local_user(user_hash)
39
+ return response if response.env.status != 200
40
+ Scooter::HttpDispatchers::ConsoleDispatcher.new(@host,
41
+ login: login,
42
+ password: password)
43
+ end
44
+
45
+ def generate_role(options = {})
46
+ permissions = options['permissions'] || []
47
+ user_ids = options['user_ids'] || []
48
+ group_ids = options['group_ids'] || []
49
+ display_name = options['display_name'] || RandomString.generate
50
+ description = options['description'] || RandomString.generate
51
+
52
+ role_hash = { 'permissions' => permissions,
53
+ 'user_ids' => user_ids,
54
+ 'group_ids' => group_ids,
55
+ 'display_name' => display_name,
56
+ 'description' => description }
57
+
58
+ response = create_role(role_hash)
59
+ return response if response.env.status != 200
60
+ response.env.body
61
+ end
62
+
63
+ def delete_role_by_name(role_name)
64
+ role_id = get_role_id(role_name)
65
+ delete_role(role_id)
66
+ end
67
+
68
+ def add_user_to_role(console_dispatcher, role)
69
+ user_id = get_user_id_of_console_dispatcher(console_dispatcher)
70
+ role['user_ids'].push(user_id)
71
+ replace_role(role)
72
+ end
73
+
74
+ def remove_user_from_role(console_dispatcher, role)
75
+ user_id = get_user_id_of_console_dispatcher(console_dispatcher)
76
+ role['user_ids'].delete(user_id)
77
+ replace_role(role)
78
+ end
79
+
80
+ def get_user_id_of_console_dispatcher(console_dispatcher)
81
+ return get_user_id_by_login_name('api_user') if console_dispatcher.credentials == nil
82
+ get_user_id_by_login_name(console_dispatcher.credentials.login)
83
+ end
84
+
85
+ def get_current_user_id
86
+ get_current_user_data['id']
87
+ end
88
+
89
+ def get_console_dispatcher_data(console_dispatcher)
90
+ users = get_list_of_users
91
+ users.each do |user|
92
+ return user if user['login'] == console_dispatcher.credentials.login
93
+ end
94
+ nil #return nil if the console dispatcher is not found
95
+ end
96
+
97
+ def update_console_dispatcher(update_hash, console_dispatcher)
98
+ user = get_console_dispatcher_data(console_dispatcher)
99
+ user.merge!(update_hash)
100
+ update_local_user(user)
101
+ end
102
+
103
+ def revoke_console_dispatcher(console_dispatcher)
104
+ update_console_dispatcher({ 'is_revoked' => true }, console_dispatcher)
105
+ end
106
+
107
+ def get_user_id_by_login_name(name)
108
+ users = get_list_of_users
109
+ users.each do |user|
110
+ return user['id'] if user['login'] == name
111
+ end
112
+ nil #return nil if name is not found
113
+ end
114
+
115
+ def delete_local_console_dispatcher(console_dispatcher)
116
+ uuid = get_user_id_of_console_dispatcher(console_dispatcher)
117
+ delete_local_user(uuid)
118
+ end
119
+
120
+ def get_group_data_by_name(name)
121
+ groups = get_list_of_groups
122
+ groups.each do |group|
123
+ return group if name == group['login']
124
+ end
125
+ nil #return nil if name is not found
126
+ end
127
+
128
+ def get_group_id(group_name)
129
+ groups = get_list_of_groups
130
+ groups.each do |group|
131
+ return group['id'] if group_name == group['display_name']
132
+ end
133
+ nil #return nil if group_name not found
134
+ end
135
+
136
+ def get_role_by_name(role_name)
137
+ roles = get_list_of_roles
138
+ roles.each do |role|
139
+ return role if role['display_name'] == role_name
140
+ end
141
+ nil # return nil if role_name not found
142
+ end
143
+
144
+ def get_role_id(role_name)
145
+ roles = get_list_of_roles
146
+ roles.each do |role|
147
+ return role['id'] if role['display_name'] == role_name
148
+ end
149
+ nil #return nil if role_name not found
150
+ end
151
+
152
+ def reset_console_dispatcher_password(console_dispatcher, password)
153
+ token = get_password_reset_token_for_console_dispatcher(console_dispatcher)
154
+ reset_local_user_password(token, password)
155
+ console_dispatcher.credentials.password = password
156
+ end
157
+
158
+ def reset_console_dispatcher_password_to_default(console_dispatcher)
159
+ token = get_password_reset_token_for_console_dispatcher(console_dispatcher)
160
+ reset_local_user_password(token, 'Puppet11')
161
+ console_dispatcher.credentials.password = 'Puppet11'
162
+ end
163
+
164
+ def get_password_reset_token_for_console_dispatcher(console_dispatcher)
165
+ uuid = get_user_id_of_console_dispatcher(console_dispatcher)
166
+ create_password_reset_token(uuid)
167
+ end
168
+
169
+ def acquire_token_with_credentials(lifetime=nil)
170
+ @token = acquire_token(credentials.login, credentials.password, lifetime)
171
+ end
172
+
173
+ def rbac_database_matches_self?(host_name)
174
+ original_host_name = self.host
175
+ begin
176
+ self.host = host_name.to_s
177
+ initialize_connection
178
+ other_users = get_list_of_users
179
+ other_groups = get_list_of_groups
180
+ other_roles = get_list_of_roles
181
+ ensure
182
+ self.host = original_host_name
183
+ initialize_connection
184
+ end
185
+
186
+ self_users = get_list_of_users
187
+ self_groups = get_list_of_groups
188
+ self_roles = get_list_of_roles
189
+
190
+ errors = ''
191
+ errors << "Users do not match\r\n" unless users_match?(self_users, other_users)
192
+ errors << "Groups do not match\r\n" unless groups_match?(self_groups, other_groups)
193
+ errors << "Roles do not match\r\n" unless roles_match?(self_roles, other_roles)
194
+
195
+ @faraday_logger.warn(errors.chomp) unless errors.empty?
196
+ errors.empty?
197
+ end
198
+
199
+ private
200
+
201
+ def users_match?(other_users, self_users)
202
+ other_users == self_users
203
+ end
204
+
205
+ def groups_match?(other_groups, self_groups)
206
+ other_groups == self_groups
207
+ end
208
+
209
+ def roles_match?(other_roles, self_roles)
210
+ return false unless other_roles.size == self_roles.size
211
+ other_roles.each_index { |idx| return false unless role_matches?(other_roles[idx], self_roles[idx]) }
212
+ true
213
+ end
214
+
215
+ def role_matches?(role1, role2)
216
+ keys_with_expected_diffs = ['permissions']
217
+ same_num_fields = (role1.size == role2.size)
218
+ same_byte_length = (role1.to_s.size == role2.to_s.size)
219
+ same_num_fields && same_byte_length && same_role_contents?(role1, role2, keys_with_expected_diffs)
220
+ end
221
+
222
+ def same_role_contents?(role1, role2, keys_to_ignore)
223
+ role1.keys.each do |key|
224
+ next if keys_to_ignore.include?(key)
225
+ return false unless role1[key] == role2[key]
226
+ end
227
+ true
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,68 @@
1
+ module Scooter
2
+ module HttpDispatchers
3
+ module Rbac
4
+ module V1
5
+ # Methods defined here are broken out from the rest of the RBAC
6
+ # endpoints because they use a ldapdispatcher object to determine
7
+ # various settings for the ds endpoint.
8
+ module DirectoryService
9
+
10
+ def ds_default_settings(ldapdispatcher)
11
+ base_dn_to_chomp = ',' + ldapdispatcher.base
12
+ user_rdn = ldapdispatcher.users_dn.chomp(base_dn_to_chomp)
13
+ group_rdn = ldapdispatcher.groups_dn.chomp(base_dn_to_chomp)
14
+ settings = {
15
+ "id" => 1,
16
+ "display_name" => 'test_ds',
17
+ "help_link" => 'https://example.com',
18
+ "hostname" => ldapdispatcher.host,
19
+ "port" => ldapdispatcher.port,
20
+ "login" => ldapdispatcher.admin_dn,
21
+ "password" => ldapdispatcher.return_default_password,
22
+ "connect_timeout" => 20,
23
+ "ssl" => true,
24
+ "base_dn" => ldapdispatcher.base,
25
+ "user_lookup_attr" => 'cn',
26
+ "user_email_attr" => 'mail',
27
+ "user_display_name_attr" => 'displayName',
28
+ "group_object_class" => '*',
29
+ "group_name_attr" => 'name',
30
+ "group_member_attr" => 'uniqueMember',
31
+ "group_lookup_attr" => 'cn',
32
+ "user_rdn" => user_rdn,
33
+ "group_rdn" => group_rdn
34
+ }
35
+
36
+ # Change the group_member_attr to just member if windows is the
37
+ # directory service, because AD doesn't support the attribute
38
+ # uniqueMember
39
+ settings['group_member_attr'] = 'member' if ldapdispatcher.is_windows_ad?
40
+ settings
41
+ end
42
+
43
+ def attach_ds_to_rbac(ldapdispatcher=nil, options={})
44
+ settings = ldapdispatcher ? ds_default_settings(ldapdispatcher) : {}
45
+ settings.merge!(options)
46
+
47
+ set_rbac_path
48
+ @connection.put('v1/ds') do |req|
49
+ req.body = settings
50
+ end
51
+ end
52
+
53
+ def test_attach_ds_to_rbac(ldapdispatcher=nil, options={})
54
+ settings = ldapdispatcher ? ds_default_settings(ldapdispatcher) : {}
55
+ settings.merge!(options)
56
+
57
+ set_rbac_path
58
+ @connection.put('v1/ds/test') do |req|
59
+ req.body = settings
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,116 @@
1
+ %w( directory_service ).each do |lib|
2
+ require "scooter/httpdispatchers/rbac/v1/#{lib}"
3
+ end
4
+ module Scooter
5
+ module HttpDispatchers
6
+ module Rbac
7
+ # Methods here are generally representative of endpoints, and depending
8
+ # on the method, return either a Faraday response object or some sort of
9
+ # instance of the object created/modified.
10
+ module V1
11
+
12
+ include Scooter::HttpDispatchers::Rbac::V1::DirectoryService
13
+
14
+ def create_local_user(options)
15
+ set_rbac_path
16
+ @connection.post 'v1/users' do |request|
17
+ request.body = options
18
+ end
19
+ end
20
+
21
+ def update_local_user(update_hash)
22
+ set_rbac_path
23
+ @connection.put "v1/users/#{update_hash['id']}" do |request|
24
+ request.body = update_hash
25
+ end
26
+ end
27
+
28
+ def delete_local_user(user_id)
29
+ set_rbac_path
30
+ @connection.delete "v1/users/#{user_id}"
31
+ end
32
+
33
+ def get_single_user_data(uuid)
34
+ set_rbac_path
35
+ @connection.get("v1/users/#{uuid}").env.body
36
+ end
37
+
38
+ def get_current_user_data
39
+ set_rbac_path
40
+ @connection.get('v1/users/current').env.body
41
+ end
42
+
43
+ # The ParseJson middleware throws an exception because this returns
44
+ # json headers while simply returning a token. In order to avoid this
45
+ # middleware throwing an error, we catch the exception and parse the
46
+ # ParsingError object for a token in the error message.
47
+ def create_password_reset_token(uuid)
48
+ set_rbac_path
49
+ begin
50
+ token = @connection.post("v1/users/#{uuid}/password/reset").env.body
51
+ rescue Faraday::ParsingError => error
52
+ # Use a regex to parse the token from the ParsingError object
53
+ regex = /\'(.+)\'/
54
+ token = regex.match(error.message)[1]
55
+ end
56
+ token
57
+ end
58
+
59
+ def get_list_of_users
60
+ set_rbac_path
61
+ @connection.get('v1/users').env.body
62
+ end
63
+
64
+ def get_list_of_groups
65
+ set_rbac_path
66
+ @connection.get('v1/groups').env.body
67
+ end
68
+
69
+ def import_ldap_group(group_name, role_ids=[])
70
+ set_rbac_path
71
+ @connection.post('v1/groups') do |request|
72
+ request.body = {'login' => group_name,
73
+ 'role_ids' => role_ids}
74
+ end
75
+ end
76
+
77
+ def get_list_of_roles
78
+ set_rbac_path
79
+ @connection.get('v1/roles').env.body
80
+ end
81
+
82
+ def create_role(options)
83
+ set_rbac_path
84
+ @connection.post('v1/roles') do |request|
85
+ request.body = options
86
+ end
87
+ end
88
+
89
+ def replace_role(role)
90
+ set_rbac_path
91
+ @connection.put("v1/roles/#{role['id']}") do |request|
92
+ request.body = role
93
+ end
94
+ end
95
+
96
+ def delete_role(role_id)
97
+ set_rbac_path
98
+ @connection.delete("v1/roles/#{role_id}")
99
+ end
100
+
101
+ def acquire_token(login, password, lifetime=nil)
102
+ set_rbac_path
103
+ response = @connection.post "v1/auth/token" do |request|
104
+ creds= {}
105
+ creds[:login] = login
106
+ creds[:password] = password
107
+ creds[:lifetime] = lifetime if lifetime
108
+ request.body = creds
109
+ end
110
+ response.env.body['token']
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,349 @@
1
+ %w( ldap_fixtures ).each do |lib|
2
+ require "scooter/ldap/#{lib}"
3
+ end
4
+ module Scooter
5
+ module LDAP
6
+
7
+ DEFAULT_DS_PORT = 636
8
+ DEFAULT_USER_PASSWORD = 'Puppet11'
9
+
10
+ # == Quick-start guide for the impatient
11
+ # === Quick example creating an LDAPDispatcher object with a beaker config:
12
+ #
13
+ # #Example beaker configuration
14
+ # #
15
+ # #HOSTS:
16
+ # # win_2008_r2_x64:
17
+ # # roles:
18
+ # # - directory_service
19
+ # # platform: windows-2008r2-x86_64
20
+ #
21
+ # require 'scooter'
22
+ # ldapdispatcher = Scooter::LDAP::LDAPDispatcher.new(directory_service)
23
+ # # If you are not using the default static fixtures, you probably want
24
+ # # to change the credentials for your LDAP instance
25
+ # ldapdispatcher.auth(user_dn, password)
26
+ # # This is the normal method you would use to set up a standard test
27
+ # # environment, with groups of writers(poets, lyricists, novelists)
28
+ # # to populate your directory_service
29
+ # ldapdispatcher.create_default_test_groups_and_users
30
+ class LDAPDispatcher < Net::LDAP
31
+
32
+
33
+ attr_accessor :test_uid
34
+ attr_reader :ds_type, :users_dn, :groups_dn, :ds_users
35
+
36
+ # Instantiating an object of type Scooter::LDAP::LDAPDispatcher extends
37
+ # the Net::LDAP class to include helper methods to make test setup and
38
+ # cleanup more consistent and reliable for beaker tests involving the
39
+ # Puppet RBAC Service. LDAPDispatcher objects require either a Unix::Host
40
+ # or Windows::Host object passed in as a parameter, which will dictate how
41
+ # helper methods construct a test environment of groups and users.
42
+ #
43
+ # Unlike the Net::LDAP, LDAPDispatcher <i>does</i> test the network
44
+ # connection during initialization and raises a warning if it fails.
45
+ # @param host [Unix::Host, Windows::Host] the DS host object defined in
46
+ # your Beaker config
47
+ # @param options [hash] any params you would like to override; you are
48
+ # likely to want to do this if you are not using the static Puppet LDAP
49
+ # fixtures
50
+ def initialize(host, options={})
51
+
52
+ # All initialized LDAPDispatcher objects will have test_uids to ensure
53
+ # no collisions when creating entries in the directory services.
54
+ @test_uid = Scooter::Utilities::RandomString.generate(4)
55
+ if host.is_a? Windows::Host
56
+ @ds_type = :ad
57
+ elsif host.is_a? Unix::Host
58
+ @ds_type = :openldap
59
+ else
60
+ raise "host must be Unix::Host or Windows::Host, not #{host.class}"
61
+ end
62
+
63
+ generated_args = {}
64
+ generated_args[:host] = host.reachable_name
65
+ generated_args[:port] = DEFAULT_DS_PORT
66
+ generated_args[:encryption] = {:method => :simple_tls}
67
+ generated_args[:base] = return_default_base
68
+
69
+ generated_args.merge!(options)
70
+ super(generated_args)
71
+
72
+ # If we didn't pass in an :auth hash, generate the default settings
73
+ # using the auth method of Net::LDAP
74
+ if !options[:auth]
75
+ self.auth admin_dn, return_default_password
76
+ end
77
+
78
+ if !bind
79
+ warn "Problem binding to #{host}, #{get_operation_result}\n
80
+ username: #{admin_dn}, pw: #{return_default_password}"
81
+ end
82
+ end
83
+
84
+ def return_default_password
85
+ "Puppet11"
86
+ end
87
+
88
+ def return_default_base
89
+ 'dc=delivery,dc=puppetlabs,dc=net'
90
+ end
91
+
92
+ def is_openldap?
93
+ true if @ds_type == :openldap
94
+ end
95
+
96
+ def is_windows_ad?
97
+ true if @ds_type == :ad
98
+ end
99
+
100
+ def admin_dn
101
+ if is_windows_ad?
102
+ "cn=Administrator,cn=Users,#{return_default_base}"
103
+ else
104
+ "cn=admin,#{return_default_base}"
105
+ end
106
+ end
107
+
108
+ def create_temp_ou(base_string='test_')
109
+ ou = base_string + @test_uid
110
+ dn = "ou=#{ou},#{self.base}"
111
+ attr = {:objectClass => ['top', 'organizationalUnit'],
112
+ :ou => ou}
113
+ add(:dn => dn, :attributes => attr)
114
+
115
+ if get_operation_result.code != 0
116
+ raise "OU creation failed: #{get_operation_result}, #{dn}"
117
+ end
118
+
119
+ dn
120
+ end
121
+
122
+ # This method should execute after a test's completion; the group's ou
123
+ # and users's ou will be deleted, as will any entity with those
124
+ # respective ou's in their distinguished name.
125
+ # === Example beaker teardown
126
+ #
127
+ # Example beaker teardown
128
+ #
129
+ # teardown do
130
+ # ldapdispatcher.delete_users_and_groups_organizational_units
131
+ # end
132
+ def delete_users_and_groups_organizational_units
133
+ delete_all_dn_entries(@groups_dn)
134
+ delete_all_dn_entries(@users_dn)
135
+ end
136
+
137
+ def delete_all_dn_entries(dn)
138
+ entries = search(:base => dn, :attributes => ['dn'])
139
+ entries.each do |entry|
140
+ delete :dn => entry.dn
141
+ end
142
+
143
+ #This needs to be repeated because it may have failed deleting a group
144
+ #that still had users associated.
145
+ entries.each do |entry|
146
+ delete :dn => entry.dn
147
+ end
148
+
149
+ #This request should return nil; all entities with the dn provided
150
+ #should now be deleted.
151
+ entries = search(:base => dn, :attributes => ['dn'])
152
+ if entries != nil
153
+ raise "Problem deleting all entries for this dn: #{dn}"
154
+ end
155
+ end
156
+
157
+ def create_ds_user(attributes, users_dn=self.users_dn)
158
+ default_attributes = {:objectClass => ['top',
159
+ 'person',
160
+ 'organizationalPerson',
161
+ 'inetOrgPerson']}
162
+
163
+ if is_windows_ad?
164
+ default_attributes[:userAccountControl] = ['544']
165
+ end
166
+
167
+ default_attributes.merge!(attributes)
168
+
169
+ add(:dn => "cn=#{default_attributes[:cn]},#{users_dn}",
170
+ :attributes => default_attributes)
171
+
172
+ if get_operation_result.code != 0
173
+ raise "Creating user failed: #{get_operation_result}\n
174
+ #{default_attributes}"
175
+ end
176
+ end
177
+
178
+ def create_ds_group(attributes, groups_dn=self.groups_dn)
179
+
180
+ #When Openldap, you must specify :member entries in the attributes
181
+ default_attributes = {:objectClass => ["top", "groupOfUniqueNames"]}
182
+
183
+ if is_windows_ad?
184
+ default_attributes[:objectClass] = ["top", "group"]
185
+ end
186
+
187
+ default_attributes.merge!(attributes)
188
+
189
+ add(:dn => "cn=#{default_attributes[:cn]},#{groups_dn}",
190
+ :attributes => default_attributes)
191
+
192
+ if get_operation_result.code != 0
193
+ raise "Creating group failed: #{get_operation_result},\n
194
+ #{default_attributes}"
195
+ end
196
+ end
197
+
198
+ def create_ou_for_users_and_groups
199
+ @users_dn = create_temp_ou('users')
200
+ @groups_dn = create_temp_ou('groups')
201
+ end
202
+
203
+ # This is the primary method most tests will use. It creates two
204
+ # organizational units, or ou's, to base all your testing around. There is
205
+ # one ou for groups and one for users. Most testing can be covered by
206
+ # simply running the method <tt>create_default_test_groups_and_users</tt>.
207
+ def create_default_test_groups_and_users
208
+
209
+ create_ou_for_users_and_groups
210
+
211
+ create_default_users
212
+
213
+ if is_windows_ad?
214
+ create_windows_ad_default_users_and_test_groups
215
+ elsif is_openldap?
216
+ create_openldap_default_users_and_test_groups
217
+ end
218
+
219
+ end
220
+
221
+ def create_default_users
222
+ users = Scooter::LDAP::LDAPFixtures.users(@test_uid)
223
+
224
+ users.each do |name, hash|
225
+ create_ds_user(hash)
226
+ update_user_password("CN=#{hash[:cn]},#{users_dn}", DEFAULT_USER_PASSWORD)
227
+ hash[:password] = DEFAULT_USER_PASSWORD
228
+ end
229
+ @ds_users = users
230
+ end
231
+
232
+ #This is used to encode passwords for Windows AD
233
+ #See URL: http://msdn.microsoft.com/en-us/library/cc223248.aspx
234
+ def str_to_unicode_pwd(str) #:nodoc:
235
+ ('"' + str + '"').encode("utf-16le").force_encoding("utf-8")
236
+ end
237
+
238
+ def update_user_password(user_dn, password) #:nodoc:
239
+ if is_windows_ad?
240
+ password = str_to_unicode_pwd(password)
241
+ ops = [[:replace, :unicodePwd, password]]
242
+ else
243
+ ops = [[:replace, :userPassword, password]]
244
+ end
245
+
246
+ modify :dn => user_dn, :operations => ops
247
+
248
+ if get_operation_result.code != 0
249
+ raise "Updating password failed: #{get_operation_result}\n
250
+ #{ops}"
251
+ end
252
+ end
253
+
254
+ private
255
+ def create_openldap_default_users_and_test_groups #:nodoc:
256
+
257
+ #add novelists
258
+ novelists =["cn=#{ds_users['arthur'][:cn]},#{users_dn}",
259
+ "cn=#{ds_users['howard'][:cn]},#{users_dn}",
260
+ "cn=#{ds_users['oscar'][:cn]},#{users_dn}"]
261
+
262
+ create_ds_group({ :cn => "novelists#{@test_uid}",
263
+ :uniqueMember => novelists })
264
+
265
+ #add poets
266
+ poets = ["cn=#{ds_users['sylvia'][:cn]},#{users_dn}",
267
+ "cn=#{ds_users['jorge'][:cn]},#{users_dn}",
268
+ "cn=#{ds_users['oscar'][:cn]},#{users_dn}"]
269
+
270
+ create_ds_group({ :cn => "poets#{@test_uid}",
271
+ :uniqueMember => poets })
272
+
273
+ #add lyricists
274
+ lyricists = ["cn=#{ds_users['stephen'][:cn]},#{users_dn}"]
275
+
276
+ create_ds_group({ :cn => "lyricists#{@test_uid}",
277
+ :uniqueMember => lyricists })
278
+
279
+ #add writers
280
+ writers = ["cn=#{ds_users['guillermo'][:cn]},#{users_dn}",
281
+ "cn=novelists#{test_uid},#{groups_dn}",
282
+ "cn=poets#{test_uid},#{groups_dn}",
283
+ "cn=lyricists#{test_uid},#{groups_dn}"]
284
+
285
+ create_ds_group({ :cn => "writers#{@test_uid}",
286
+ :uniqueMember => writers })
287
+
288
+ end
289
+
290
+ def create_windows_ad_default_users_and_test_groups #:nodoc:
291
+
292
+ writers_cn = "writers#{@test_uid}"
293
+ poets_cn = "poets#{@test_uid}"
294
+ novelists_cn = "novelists#{@test_uid}"
295
+ lyricists_cn = "lyricists#{@test_uid}"
296
+
297
+ create_ds_group(:cn => lyricists_cn)
298
+ create_ds_group(:cn => novelists_cn)
299
+ create_ds_group(:cn => poets_cn)
300
+ create_ds_group(:cn => writers_cn)
301
+
302
+ add_attribute("cn=#{writers_cn},#{groups_dn}",
303
+ :member,
304
+ "cn=#{lyricists_cn},#{groups_dn}")
305
+
306
+ add_attribute("cn=#{writers_cn},#{groups_dn}",
307
+ :member,
308
+ "cn=#{poets_cn},#{groups_dn}")
309
+
310
+ add_attribute("cn=#{writers_cn},#{groups_dn}",
311
+ :member,
312
+ "cn=#{novelists_cn},#{groups_dn}")
313
+
314
+ add_attribute("cn=#{novelists_cn},#{groups_dn}",
315
+ :member,
316
+ "cn=#{ds_users['howard'][:cn]},#{users_dn}")
317
+
318
+ add_attribute("cn=#{novelists_cn},#{groups_dn}",
319
+ :member,
320
+ "cn=#{ds_users['arthur'][:cn]},#{users_dn}")
321
+
322
+ add_attribute("cn=#{novelists_cn},#{groups_dn}",
323
+ :member,
324
+ "cn=#{ds_users['oscar'][:cn]},#{users_dn}")
325
+
326
+ add_attribute("cn=#{poets_cn},#{groups_dn}",
327
+ :member,
328
+ "cn=#{ds_users['sylvia'][:cn]},#{users_dn}")
329
+
330
+ add_attribute("cn=#{poets_cn},#{groups_dn}",
331
+ :member,
332
+ "cn=#{ds_users['jorge'][:cn]},#{users_dn}")
333
+
334
+ add_attribute("cn=#{poets_cn},#{groups_dn}",
335
+ :member,
336
+ "cn=#{ds_users['oscar'][:cn]},#{users_dn}")
337
+
338
+ add_attribute("cn=#{writers_cn},#{groups_dn}",
339
+ :member,
340
+ "cn=#{ds_users['guillermo'][:cn]},#{users_dn}")
341
+
342
+ add_attribute("cn=#{lyricists_cn},#{groups_dn}",
343
+ :member,
344
+ "cn=#{ds_users['stephen'][:cn]},#{users_dn}")
345
+
346
+ end
347
+ end
348
+ end
349
+ end