subiam 1.2.1

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.
@@ -0,0 +1,565 @@
1
+ class Subiam::Client
2
+ include Subiam::Logger::Helper
3
+
4
+ def initialize(options = {})
5
+ @options = {:format => :ruby}.merge(options)
6
+ aws_config = options[:aws_config] || {}
7
+ @iam = Aws::IAM::Client.new(aws_config)
8
+ @driver = Subiam::Driver.new(@iam, options)
9
+ @password_manager = options[:password_manager] || Subiam::PasswordManager.new('-', options)
10
+ @target = nil
11
+ end
12
+
13
+ def export(export_options = {})
14
+ exported, group_users, instance_profile_roles = Subiam::Exporter.export(@iam, @options)
15
+ exported.sort_array!
16
+
17
+ if block_given?
18
+ [:users, :groups, :roles, :instance_profiles, :policies].each do |type|
19
+ splitted = {:users => {}, :groups => {}, :roles => {}, :instance_profiles => {}, :policies => {}}
20
+
21
+ if export_options[:split_more]
22
+ exported[type].sort_by {|k, v| k }.each do |name, attrs|
23
+ more_splitted = splitted.dup
24
+ more_splitted[type] = {}
25
+ more_splitted[type][name] = attrs
26
+
27
+ dsl = exec_by_format(
28
+ :ruby => proc { Subiam::DSL.convert(more_splitted, @options).strip },
29
+ :json => proc { JSON.pretty_generate(more_splitted) }
30
+ )
31
+
32
+ yield(:type => type, :name => name, :dsl => dsl)
33
+ end
34
+ else
35
+ splitted[type] = exported[type]
36
+
37
+ dsl = exec_by_format(
38
+ :ruby => proc { Subiam::DSL.convert(splitted, @options).strip },
39
+ :json => proc { JSON.pretty_generate(splitted) }
40
+ )
41
+
42
+ yield(:type => type, :dsl => dsl)
43
+ end
44
+ end
45
+ else
46
+ dsl = exec_by_format(
47
+ :ruby => proc { Subiam::DSL.convert(exported, @options).strip },
48
+ :json => proc { JSON.pretty_generate(exported) }
49
+ )
50
+ end
51
+ end
52
+
53
+ def apply(file)
54
+ walk(file)
55
+ end
56
+
57
+ private
58
+
59
+ def walk(file)
60
+ expected = load_file(file)
61
+
62
+ unless expected[:target]
63
+ raise "Required setting on DSL `target` not found"
64
+ end
65
+ @target = expected[:target]
66
+
67
+ actual, group_users, instance_profile_roles = Subiam::Exporter.export(@iam, @options)
68
+ updated = pre_walk_managed_policies(expected[:policies], actual[:policies])
69
+ updated = walk_groups(expected[:groups], actual[:groups], actual[:users], group_users) || updated
70
+ updated = walk_users(expected[:users], actual[:users], group_users) || updated
71
+ updated = walk_instance_profiles(expected[:instance_profiles], actual[:instance_profiles], actual[:roles], instance_profile_roles) || updated
72
+ updated = walk_roles(expected[:roles], actual[:roles], instance_profile_roles) || updated
73
+ updated = post_walk_managed_policies(actual[:policies]) || updated
74
+
75
+ if @options[:dry_run]
76
+ false
77
+ else
78
+ updated
79
+ end
80
+ end
81
+
82
+ def walk_users(expected, actual, group_users)
83
+ updated = scan_rename(:user, expected, actual, group_users)
84
+
85
+ expected.each do |user_name, expected_attrs|
86
+ next unless target_matched?(user_name)
87
+
88
+ actual_attrs = actual.delete(user_name)
89
+
90
+ if actual_attrs
91
+ updated = walk_path(:user, user_name, expected_attrs[:path], actual_attrs[:path]) || updated
92
+ updated = walk_user(user_name, expected_attrs, actual_attrs) || updated
93
+ else
94
+ actual_attrs = @driver.create_user(user_name, expected_attrs)
95
+ access_key = @driver.create_access_key(user_name)
96
+
97
+ if access_key
98
+ @password_manager.puts_password(user_name, access_key[:access_key_id], access_key[:secret_access_key])
99
+ end
100
+
101
+ walk_user(user_name, expected_attrs, actual_attrs)
102
+ updated = true
103
+ end
104
+ end
105
+
106
+ if @options[:enable_delete]
107
+ actual.each do |user_name, attrs|
108
+ next unless target_matched?(user_name)
109
+
110
+ @driver.delete_user(user_name, attrs)
111
+
112
+ group_users.each do |group_name, users|
113
+ users.delete(user_name)
114
+ end
115
+
116
+ updated = true
117
+ end
118
+ end
119
+
120
+ updated
121
+ end
122
+
123
+ def walk_user(user_name, expected_attrs, actual_attrs)
124
+ updated = walk_login_profile(user_name, expected_attrs[:login_profile], actual_attrs[:login_profile])
125
+ updated = walk_user_groups(user_name, expected_attrs[:groups], actual_attrs[:groups]) || updated
126
+ updated = walk_attached_managed_policies(:user, user_name, expected_attrs[:attached_managed_policies], actual_attrs[:attached_managed_policies]) || updated
127
+ walk_policies(:user, user_name, expected_attrs[:policies], actual_attrs[:policies]) || updated
128
+ end
129
+
130
+ def walk_login_profile(user_name, expected_login_profile, actual_login_profile)
131
+ updated = false
132
+
133
+ [expected_login_profile, actual_login_profile].each do |login_profile|
134
+ if login_profile and not login_profile.has_key?(:password_reset_required)
135
+ login_profile[:password_reset_required] = false
136
+ end
137
+ end
138
+
139
+ if expected_login_profile and not actual_login_profile
140
+ expected_login_profile[:password] ||= @password_manager.identify(user_name, :login_profile)
141
+ @driver.create_login_profile(user_name, expected_login_profile)
142
+ updated = true
143
+ elsif not expected_login_profile and actual_login_profile
144
+ @driver.delete_login_profile(user_name)
145
+ updated = true
146
+ elsif expected_login_profile != actual_login_profile
147
+ if @options[:ignore_login_profile]
148
+ log(:warn, "User `#{user_name}`: difference of loging profile has been ignored: expected=#{expected_login_profile.inspect}, actual=#{actual_login_profile.inspect}", :color => :yellow)
149
+ else
150
+ @driver.update_login_profile(user_name, expected_login_profile, actual_login_profile)
151
+ updated = true
152
+ end
153
+ end
154
+
155
+ updated
156
+ end
157
+
158
+ def walk_user_groups(user_name, expected_groups, actual_groups)
159
+ expected_groups = expected_groups.sort
160
+ actual_groups = actual_groups.sort
161
+ updated = false
162
+
163
+ if expected_groups != actual_groups
164
+ add_groups = expected_groups - actual_groups
165
+ remove_groups = actual_groups - expected_groups
166
+
167
+ unless add_groups.empty?
168
+ @driver.add_user_to_groups(user_name, add_groups)
169
+ end
170
+
171
+ unless remove_groups.empty?
172
+ @driver.remove_user_from_groups(user_name, remove_groups)
173
+ end
174
+
175
+ updated = true
176
+ end
177
+
178
+ updated
179
+ end
180
+
181
+ def walk_groups(expected, actual, actual_users, group_users)
182
+ updated = scan_rename(:group, expected, actual, group_users)
183
+
184
+ expected.each do |group_name, expected_attrs|
185
+ next unless target_matched?(group_name)
186
+
187
+ actual_attrs = actual.delete(group_name)
188
+
189
+ if actual_attrs
190
+ updated = walk_path(:group, group_name, expected_attrs[:path], actual_attrs[:path]) || updated
191
+ updated = walk_group(group_name, expected_attrs, actual_attrs) || updated
192
+ else
193
+ actual_attrs = @driver.create_group(group_name, expected_attrs)
194
+ walk_group(group_name, expected_attrs, actual_attrs)
195
+ updated = true
196
+ end
197
+ end
198
+
199
+ if @options[:enable_delete]
200
+ actual.each do |group_name, attrs|
201
+ next unless target_matched?(group_name)
202
+ next unless @options[:enable_delete]
203
+
204
+ users_in_group = group_users.delete(group_name) || []
205
+ @driver.delete_group(group_name, attrs, users_in_group)
206
+
207
+ actual_users.each do |user_name, user_attrs|
208
+ user_attrs[:groups].delete(group_name)
209
+ end
210
+
211
+ updated = true
212
+ end
213
+ end
214
+
215
+ updated
216
+ end
217
+
218
+ def walk_group(group_name, expected_attrs, actual_attrs)
219
+ updated = walk_policies(:group, group_name, expected_attrs[:policies], actual_attrs[:policies])
220
+ walk_attached_managed_policies(:group, group_name, expected_attrs[:attached_managed_policies], actual_attrs[:attached_managed_policies]) || updated
221
+ end
222
+
223
+ def walk_roles(expected, actual, instance_profile_roles)
224
+ updated = false
225
+
226
+ expected.each do |role_name, expected_attrs|
227
+ next unless target_matched?(role_name)
228
+
229
+ actual_attrs = actual.delete(role_name)
230
+
231
+ if actual_attrs
232
+ updated = walk_role(role_name, expected_attrs, actual_attrs) || updated
233
+ else
234
+ actual_attrs = @driver.create_role(role_name, expected_attrs)
235
+ walk_role(role_name, expected_attrs, actual_attrs)
236
+ updated = true
237
+ end
238
+ end
239
+
240
+ if @options[:enable_delete]
241
+ actual.each do |role_name, attrs|
242
+ next unless target_matched?(role_name)
243
+
244
+ instance_profile_names = []
245
+
246
+ instance_profile_roles.each do |instance_profile_name, roles|
247
+ if roles.include?(role_name)
248
+ instance_profile_names << instance_profile_name
249
+ end
250
+ end
251
+
252
+ @driver.delete_role(role_name, instance_profile_names, attrs)
253
+
254
+ instance_profile_roles.each do |instance_profile_name, roles|
255
+ roles.delete(role_name)
256
+ end
257
+
258
+ updated = true
259
+ end
260
+ end
261
+
262
+
263
+ updated
264
+ end
265
+
266
+ def walk_role(role_name, expected_attrs, actual_attrs)
267
+ if expected_attrs.values_at(:path) != actual_attrs.values_at(:path)
268
+ log(:warn, "Role `#{role_name}`: 'path' cannot be updated", :color => :yellow)
269
+ end
270
+
271
+ updated = walk_assume_role_policy(role_name, expected_attrs[:assume_role_policy_document], actual_attrs[:assume_role_policy_document])
272
+ updated = walk_role_instance_profiles(role_name, expected_attrs[:instance_profiles], actual_attrs[:instance_profiles]) || updated
273
+ updated = walk_attached_managed_policies(:role, role_name, expected_attrs[:attached_managed_policies], actual_attrs[:attached_managed_policies]) || updated
274
+ walk_policies(:role, role_name, expected_attrs[:policies], actual_attrs[:policies]) || updated
275
+ end
276
+
277
+ def walk_assume_role_policy(role_name, expected_assume_role_policy, actual_assume_role_policy)
278
+ updated = false
279
+ expected_assume_role_policy.sort_array!
280
+ actual_assume_role_policy.sort_array!
281
+
282
+ if expected_assume_role_policy != actual_assume_role_policy
283
+ @driver.update_assume_role_policy(role_name, expected_assume_role_policy, actual_assume_role_policy)
284
+ updated = true
285
+ end
286
+
287
+ updated
288
+ end
289
+
290
+ def walk_role_instance_profiles(role_name, expected_instance_profiles, actual_instance_profiles)
291
+ expected_instance_profiles = expected_instance_profiles.sort
292
+ actual_instance_profiles = actual_instance_profiles.sort
293
+ updated = false
294
+
295
+ if expected_instance_profiles != actual_instance_profiles
296
+ add_instance_profiles = expected_instance_profiles - actual_instance_profiles
297
+ remove_instance_profiles = actual_instance_profiles - expected_instance_profiles
298
+
299
+ unless add_instance_profiles.empty?
300
+ @driver.add_role_to_instance_profiles(role_name, add_instance_profiles)
301
+ end
302
+
303
+ unless remove_instance_profiles.empty?
304
+ @driver.remove_role_from_instance_profiles(role_name, remove_instance_profiles)
305
+ end
306
+
307
+ updated = true
308
+ end
309
+
310
+ updated
311
+ end
312
+
313
+ def walk_instance_profiles(expected, actual, actual_roles, instance_profile_roles)
314
+ updated = false
315
+
316
+ expected.each do |instance_profile_name, expected_attrs|
317
+ next unless target_matched?(instance_profile_name)
318
+
319
+ actual_attrs = actual.delete(instance_profile_name)
320
+
321
+ if actual_attrs
322
+ updated = walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs) || updated
323
+ else
324
+ actual_attrs = @driver.create_instance_profile(instance_profile_name, expected_attrs)
325
+ walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs)
326
+ updated = true
327
+ end
328
+ end
329
+
330
+ if @options[:enable_delete]
331
+ actual.each do |instance_profile_name, attrs|
332
+ next unless target_matched?(instance_profile_name)
333
+
334
+ roles_in_instance_profile = instance_profile_roles.delete(instance_profile_name) || []
335
+ @driver.delete_instance_profile(instance_profile_name, attrs, roles_in_instance_profile)
336
+
337
+ actual_roles.each do |role_name, role_attrs|
338
+ role_attrs[:instance_profiles].delete(instance_profile_name)
339
+ end
340
+
341
+ updated = true
342
+ end
343
+ end
344
+
345
+ updated
346
+ end
347
+
348
+ def walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs)
349
+ updated = false
350
+
351
+ if expected_attrs != actual_attrs
352
+ log(:warn, "InstanceProfile `#{instance_profile_name}`: 'path' cannot be updated", :color => :yellow)
353
+ end
354
+
355
+ updated
356
+ end
357
+
358
+ def scan_rename(type, expected, actual, group_users)
359
+ updated = false
360
+
361
+ expected.each do |name, expected_attrs|
362
+ renamed_from = expected_attrs[:renamed_from]
363
+ next unless renamed_from
364
+
365
+ actual_attrs = actual.delete(renamed_from)
366
+ next unless actual_attrs
367
+
368
+ @driver.update_name(type, renamed_from, name)
369
+ actual[name] = actual_attrs
370
+
371
+ case type
372
+ when :user
373
+ group_users.each do |group_name, users|
374
+ users.each do |user_name|
375
+ if user_name == renamed_from
376
+ user_name.replace(name)
377
+ end
378
+ end
379
+ end
380
+ when :group
381
+ users = group_users.delete(renamed_from)
382
+ group_users[name] = users if users
383
+ end
384
+
385
+ updated = true
386
+ end
387
+
388
+ updated
389
+ end
390
+
391
+ def walk_path(type, user_or_group_name, expected_path, actual_path)
392
+ updated = false
393
+
394
+ if expected_path != actual_path
395
+ @driver.update_path(type, user_or_group_name, expected_path, actual_path)
396
+ updated = true
397
+ end
398
+
399
+ updated
400
+ end
401
+
402
+ def walk_policies(type, user_or_group_name, expected_policies, actual_policies)
403
+ updated = false
404
+
405
+ expected_policies.each do |policy_name, expected_document|
406
+ actual_document = actual_policies.delete(policy_name)
407
+
408
+ if actual_document
409
+ updated = walk_policy(type, user_or_group_name, policy_name, expected_document, actual_document) || updated
410
+ else
411
+ @driver.create_policy(type, user_or_group_name, policy_name, expected_document)
412
+ updated = true
413
+ end
414
+ end
415
+
416
+ actual_policies.each do |policy_name, document|
417
+ @driver.delete_policy(type, user_or_group_name, policy_name)
418
+ updated = true
419
+ end
420
+
421
+ updated
422
+ end
423
+
424
+ def walk_policy(type, user_or_group_name, policy_name, expected_document, actual_document)
425
+ updated = false
426
+ expected_document.sort_array!
427
+ actual_document.sort_array!
428
+
429
+ if expected_document != actual_document
430
+ @driver.update_policy(type, user_or_group_name, policy_name, expected_document, actual_document)
431
+ updated = true
432
+ end
433
+
434
+ updated
435
+ end
436
+
437
+ def walk_attached_managed_policies(type, name, expected_attached_managed_policies, actual_attached_managed_policies)
438
+ expected_attached_managed_policies = expected_attached_managed_policies.sort
439
+ actual_attached_managed_policies = actual_attached_managed_policies.sort
440
+ updated = false
441
+
442
+ if expected_attached_managed_policies != actual_attached_managed_policies
443
+ add_attached_managed_policies = expected_attached_managed_policies - actual_attached_managed_policies
444
+ remove_attached_managed_policies = actual_attached_managed_policies - expected_attached_managed_policies
445
+
446
+ unless add_attached_managed_policies.empty?
447
+ @driver.attach_policies(type, name, add_attached_managed_policies)
448
+ end
449
+
450
+ unless remove_attached_managed_policies.empty?
451
+ @driver.detach_policies(type, name, remove_attached_managed_policies)
452
+ end
453
+
454
+ updated = true
455
+ end
456
+
457
+ updated
458
+ end
459
+
460
+ def pre_walk_managed_policies(expected, actual)
461
+ updated = false
462
+
463
+ expected.each do |policy_name, expected_attrs|
464
+ next unless target_matched?(policy_name)
465
+
466
+ actual_attrs = actual.delete(policy_name)
467
+
468
+ if actual_attrs
469
+ if expected_attrs[:path] != actual_attrs[:path]
470
+ log(:warn, "ManagedPolicy `#{policy_name}`: 'path' cannot be updated", :color => :yellow)
471
+ end
472
+
473
+ updated = walk_managed_policy(policy_name, expected_attrs[:document], actual_attrs[:document]) || updated
474
+ else
475
+ @driver.create_managed_policy(policy_name, expected_attrs)
476
+ updated = true
477
+ end
478
+ end
479
+
480
+ updated
481
+ end
482
+
483
+ def walk_managed_policy(policy_name, expected_document, actual_document)
484
+ updated = false
485
+ expected_document.sort_array!
486
+ actual_document.sort_array!
487
+
488
+ if expected_document != actual_document
489
+ @driver.update_managed_policy(policy_name, expected_document, actual_document)
490
+ updated = true
491
+ end
492
+
493
+ updated
494
+ end
495
+
496
+ def post_walk_managed_policies(actual)
497
+ updated = false
498
+
499
+ if @options[:enable_delete]
500
+ actual.each do |policy_name, actual_attrs|
501
+ next unless target_matched?(policy_name)
502
+
503
+ @driver.delete_managed_policy(policy_name)
504
+ updated = true
505
+ end
506
+ end
507
+
508
+ updated
509
+ end
510
+
511
+ def load_file(file)
512
+ if file.kind_of?(String)
513
+ open(file) do |f|
514
+ exec_by_format(
515
+ :ruby => proc { Subiam::DSL.parse(f.read, file, @options) },
516
+ :json => proc { load_json(f) }
517
+ )
518
+ end
519
+ elsif file.respond_to?(:read)
520
+ exec_by_format(
521
+ :ruby => proc { Subiam::DSL.parse(file.read, file.path, @options) },
522
+ :json => proc { load_json(f) }
523
+ )
524
+ else
525
+ raise TypeError, "can't convert #{file} into File"
526
+ end
527
+ end
528
+
529
+ def target_matched?(name)
530
+ name =~ @target
531
+ end
532
+
533
+ def exec_by_format(proc_by_format)
534
+ format_proc = proc_by_format[@options[:format]]
535
+ raise "Invalid format: #{@options[:format]}" unless format_proc
536
+ format_proc.call
537
+ end
538
+
539
+ def load_json(json)
540
+ json = JSON.load(json)
541
+ normalized = {}
542
+
543
+ json.each do |top_key, top_value|
544
+ normalized[top_key.to_sym] = top_attrs = {}
545
+
546
+ top_value.each do |second_key, second_value|
547
+ top_attrs[second_key] = second_attrs = {}
548
+
549
+ second_value.each do |third_key, third_value|
550
+ third_key = third_key.to_sym
551
+
552
+ if third_key == :login_profile
553
+ new_third_value = {}
554
+ third_value.each {|k, v| new_third_value[k.to_sym] = v }
555
+ third_value = new_third_value
556
+ end
557
+
558
+ second_attrs[third_key] = third_value
559
+ end
560
+ end
561
+ end
562
+
563
+ normalized
564
+ end
565
+ end