subiam 1.2.1

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