stratus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ == 1.0.0 2010-11-19
2
+ Initial release.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Serverworks Co.,Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,102 @@
1
+ Stratus
2
+ ====
3
+
4
+ Stratus is a client interface for the [AWS Identity and Access Management (IAM)](http://aws.amazon.com/documentation/iam/) Services.
5
+
6
+ It was developed to for usage in the Japanese AWS management console service [Cloudworks](http://www.cloudworks.jp/).
7
+
8
+ REQUIREMENTS:
9
+ ----
10
+
11
+ * Ruby 1.8.7 or 1.9.2
12
+ * xml-simple and rest-client gems
13
+ * json_pure or json gem (optionally)
14
+
15
+ INSTALL:
16
+ ----
17
+
18
+ gem install stratus
19
+
20
+ USAGE EXAMPLE
21
+ ----
22
+
23
+ ### As interactive shell
24
+
25
+ You can run interactive shell `iamsh' and call IAM API.
26
+
27
+ $ export AMAZON_ACCESS_KEY_ID=XXXXX
28
+ $ export AMAZON_SECRET_ACCESS_KEY=XXXXX
29
+ $ iamsh
30
+
31
+ @iam defined.
32
+
33
+ Examples to try:
34
+
35
+ returns : all iam public methods
36
+ >> @iam.methods.sort
37
+
38
+ returns : get all Amazon IAM groups.
39
+ >> @iam.list_groups
40
+
41
+ Welcome to IRB.
42
+ >>
43
+
44
+ Create a new IAM user by CreateUser API.
45
+
46
+ >> @iam.create_user :user_name => 'john'
47
+ >> result = @iam.list_users
48
+ >> puts result['ListUsersResult']['Users']['member'].inspect
49
+ [{"UserName"=>"john", "Arn"=>"arn:aws:iam::000000000000:user/john", "Path"=>"/", "UserId"=>"XXXXXXXXXXXXXXXXXXXX"}]
50
+
51
+ Then create an user policy JSON string.
52
+
53
+ >> policy = {}
54
+ >> policy['Statement'] = [{
55
+ 'Effect' => 'Allow',
56
+ 'Action' => 'ec2:Describe*',
57
+ 'Resource' => '*'
58
+ }]
59
+ >> require 'json'
60
+ >> policy = policy.to_json
61
+
62
+ And put it by PutUserPolicy API.
63
+
64
+ >> @iam.put_user_policy :user_name => 'john', :policy_name => 'AllowDescribeEC2', :policy_document => policy
65
+ >> result = @iam.get_user_policy :user_name => 'john', :policy_name => 'AllowDescribeEC2'
66
+ >> result['GetUserPolicyResult']['PolicyDocument']
67
+ "{\"Statement\":[{\"Action\":\"ec2:Describe*\",\"Resource\":\"*\",\"Effect\":\"Allow\"}]}"
68
+
69
+ Delete an user policy and user.
70
+
71
+ >> @iam.delete_user_policy :user_name => 'john', :policy_name => 'AllowDescribeEC2'
72
+ >> @iam.delete_user :user_name => 'john'
73
+
74
+ ### As library
75
+
76
+ You can require the library and call IAM API from any ruby script.
77
+
78
+ require 'rubygems'
79
+ require 'stratus'
80
+
81
+ iam = Stratus::AWS::IAM::Base.new('YOUR_ACCESS_KEY_ID', 'YOUR_SECRET_ACCESS_KEY')
82
+ result = iam.create_group :group_name => 'Developers'
83
+ group = result['CreateGroupResult']['Group']
84
+ puts "Group ARN is #{group['Arn']}"
85
+
86
+ Read the [IAM API Reference](http://docs.amazonwebservices.com/IAM/latest/APIReference/) for further information.
87
+
88
+ REFERENCES:
89
+ ----
90
+
91
+ * [Using AWS Identity and Access Management](http://docs.amazonwebservices.com/IAM/latest/UserGuide/)
92
+ * [AWS Identity and Access Management API Reference](http://docs.amazonwebservices.com/IAM/latest/APIReference/)
93
+
94
+ LICENSE:
95
+ ----
96
+
97
+ This plugin is licensed under the MIT licenses.
98
+
99
+ COPYRIGHT:
100
+ ----
101
+
102
+ Copyright (c) 2010 Serverworks Co.,Ltd. See LICENSE for details.
@@ -0,0 +1,50 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "stratus"
10
+ gem.summary = %Q{Interface classes for the AWS Identity and Access Management (IAM)}
11
+ gem.description = %Q{Interface classes for the AWS Identity and Access Management (IAM)}
12
+ gem.email = "support@serverworks.co.jp"
13
+ gem.homepage = "http://github.com/serverworks/stratus"
14
+ gem.authors = ["Serverworks Co.,Ltd."]
15
+ gem.add_dependency('xml-simple', '>= 1.0.12')
16
+ gem.add_dependency('rest-client', '>= 1.6.1')
17
+ gem.add_development_dependency('rcov', '>= 0.9.6')
18
+ gem.add_development_dependency('rspec', '>= 1.2.9')
19
+ gem.files = FileList['bin/iamsh', 'lib/**/*.rb', '[A-Z]*', 'spec/**/*'].to_a
20
+ end
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+
39
+ task :default => :spec
40
+
41
+ begin
42
+ require 'yard'
43
+ YARD::Rake::YardocTask.new do |t|
44
+ #t.files = ['lib/**/*.rb']
45
+ end
46
+ rescue LoadError
47
+ puts "YARD (or a dependency) not available. Install it with: [sudo] gem install yard"
48
+ end
49
+
50
+ # vim: syntax=Ruby
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ iam_lib = File.dirname(__FILE__) + '/../lib/stratus/aws/iam.rb'
4
+ setup = File.dirname(__FILE__) + '/iamsh-setup'
5
+ irb_name = RUBY_PLATFORM =~ /mswin32/ ? 'irb.bat' : 'irb'
6
+
7
+ if ( ENV['AMAZON_ACCESS_KEY_ID'] && ENV['AMAZON_SECRET_ACCESS_KEY'] )
8
+
9
+ welcome_message = <<-MESSAGE
10
+
11
+ @iam defined.
12
+
13
+ Examples to try:
14
+
15
+ returns : all iam public methods
16
+ >> @iam.methods.sort
17
+
18
+ returns : get all Amazon IAM groups.
19
+ >> @iam.list_groups
20
+
21
+ MESSAGE
22
+
23
+ puts welcome_message
24
+ exec "#{irb_name} -rubygems -r #{iam_lib} -r #{setup} --simple-prompt"
25
+ else
26
+ puts "You must define AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY as shell environment variables before running #{$0}!"
27
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ENV['AMAZON_ACCESS_KEY_ID'] && ENV['AMAZON_SECRET_ACCESS_KEY']
4
+ opts = {
5
+ :access_key_id => ENV['AMAZON_ACCESS_KEY_ID'],
6
+ :secret_access_key => ENV['AMAZON_SECRET_ACCESS_KEY']
7
+ }
8
+ @iam = Stratus::AWS::IAM::Base.new(opts[:access_key_id], opts[:secret_access_key])
9
+ end
@@ -0,0 +1,4 @@
1
+ module Stratus
2
+ end
3
+
4
+ Dir[File.join(File.dirname(__FILE__), 'stratus/**/*.rb')].sort.each { |lib| require lib }
@@ -0,0 +1,4 @@
1
+ module Stratus
2
+ module AWS
3
+ end
4
+ end
@@ -0,0 +1,599 @@
1
+ require 'base64'
2
+ require 'time'
3
+ require 'rest_client'
4
+ require 'rexml/document'
5
+
6
+ begin
7
+ require 'xmlsimple' unless defined? XmlSimple
8
+ rescue Exception => e
9
+ require 'xml-simple' unless defined? XmlSimple
10
+ end
11
+
12
+ module Stratus
13
+ module AWS
14
+
15
+ class Response
16
+ # Parse the XML response from AWS
17
+ #
18
+ # @option options [String] :xml The XML response from AWS that we want to parse
19
+ # @option options [Hash] :parse_options Override the options for XmlSimple.
20
+ # @return [Hash] the input :xml converted to a custom Ruby Hash by XmlSimple.
21
+ def self.parse(options = {})
22
+ options = {
23
+ :xml => '',
24
+ :parse_options => { 'forcearray' => ['item', 'member'], 'suppressempty' => nil, 'keeproot' => false }
25
+ }.merge(options)
26
+ response = XmlSimple.xml_in(options[:xml], options[:parse_options])
27
+ end
28
+ end
29
+
30
+ module IAM
31
+ DEFAULT_HOST = 'iam.amazonaws.com'
32
+ API_VERSION = '2010-05-08'
33
+
34
+ class Base
35
+ VALID_ACCESS_KEY_STATUSES = [:active, :inactive].freeze
36
+ VALID_HTTP_METHODS = [:get, :post].freeze
37
+
38
+ # @param [String] access_key_id
39
+ # @param [String] secret_access_key
40
+ def initialize(access_key_id, secret_access_key)
41
+ @access_key_id = access_key_id
42
+ @secret_access_key = secret_access_key
43
+ @endpoint = "https://#{DEFAULT_HOST}/"
44
+ end
45
+
46
+ # @return [String]
47
+ def api_version
48
+ API_VERSION
49
+ end
50
+
51
+ # @return [String]
52
+ def default_host
53
+ DEFAULT_HOST
54
+ end
55
+
56
+ # @return [String]
57
+ def endpoint
58
+ @endpoint
59
+ end
60
+
61
+ # Calls GetGropup API
62
+ #
63
+ # @param [Hash] :group_name parameter is required
64
+ # @return [Hash]
65
+ def get_group(options = {})
66
+ check_group_name(options)
67
+ params = make_pagination_params(options)
68
+ params['GroupName'] = options[:group_name]
69
+ response = call_api('GetGroup', params)
70
+ Response.parse(:xml => response.to_str)
71
+ end
72
+
73
+ # Calls CreateGropup API
74
+ #
75
+ # @param [Hash] :group_name parameter is required
76
+ # @return [Hash]
77
+ def create_group(options = {})
78
+ check_group_name(options)
79
+ params = { 'GroupName' => options[:group_name] }
80
+ params['Path'] = options[:path] if options[:path]
81
+ response = call_api('CreateGroup', params)
82
+ Response.parse(:xml => response.to_str)
83
+ end
84
+
85
+ # Calls DeleteGropup API
86
+ #
87
+ # @param [Hash] :group_name parameter is required
88
+ # @return [Hash]
89
+ def delete_group(options = {})
90
+ check_group_name(options)
91
+ params = { 'GroupName' => options[:group_name] }
92
+ response = call_api('DeleteGroup', params)
93
+ Response.parse(:xml => response.to_str)
94
+ end
95
+
96
+ # Calls ListGroups API
97
+ #
98
+ # @return [Hash]
99
+ def list_groups(options = {})
100
+ params = make_pagination_params(options)
101
+ params['PathPrefix'] = options[:path_prefix] if options[:path_prefix]
102
+ response = call_api('ListGroups', params)
103
+ Response.parse(:xml => response.to_str)
104
+ end
105
+
106
+ # Calls UpdateGroup API
107
+ #
108
+ # @param [Hash] :group_name parameter is required
109
+ # @return [Hash]
110
+ def update_group(options = {})
111
+ check_group_name(options)
112
+ params = { 'GroupName' => options[:group_name] }
113
+ params['NewGroupName'] = options[:new_group_name] if options[:new_group_name]
114
+ params['NewPathName'] = options[:new_path_name] if options[:new_path_name]
115
+ response = call_api('UpdateGroup', params)
116
+ Response.parse(:xml => response.to_str)
117
+ end
118
+
119
+ # Calls AddUserToGroup API
120
+ #
121
+ # @param [Hash] :group_name and :user_name parameters is required
122
+ # @return [Hash]
123
+ def add_user_to_group(options = {})
124
+ check_group_name(options)
125
+ check_user_name(options)
126
+ params = { 'GroupName' => options[:group_name], 'UserName' => options[:user_name] }
127
+ response = call_api('AddUserToGroup', params)
128
+ Response.parse(:xml => response.to_str)
129
+ end
130
+
131
+ # Calls RemoveUserFromGroup API
132
+ #
133
+ # @param [Hash] :group_name and :user_name parameters is required
134
+ # @return [Hash]
135
+ def remove_user_from_group(options = {})
136
+ check_group_name(options)
137
+ check_user_name(options)
138
+ params = { 'GroupName' => options[:group_name], 'UserName' => options[:user_name] }
139
+ response = call_api('RemoveUserFromGroup', params)
140
+ Response.parse(:xml => response.to_str)
141
+ end
142
+
143
+ # Calls GetUser API
144
+ #
145
+ # @param [Hash]
146
+ # @return [Hash]
147
+ def get_user(options = {})
148
+ params = {}
149
+ params['UserName'] = options[:user_name] if options[:user_name]
150
+ response = call_api('GetUser', params)
151
+ Response.parse(:xml => response.to_str)
152
+ end
153
+
154
+ # Calls CreateUser API
155
+ #
156
+ # @param [Hash] :user_name parameter is required
157
+ # @return [Hash]
158
+ def create_user(options = {})
159
+ check_user_name(options)
160
+ params = { 'UserName' => options[:user_name] }
161
+ params['Path'] = options[:path] if options[:path]
162
+ response = call_api('CreateUser', params)
163
+ Response.parse(:xml => response.to_str)
164
+ end
165
+
166
+ # Calls DeleteUser API
167
+ #
168
+ # @param [Hash] :user_name parameter is required
169
+ # @return [Hash]
170
+ def delete_user(options = {})
171
+ check_user_name(options)
172
+ params = { 'UserName' => options[:user_name] }
173
+ response = call_api('DeleteUser', params)
174
+ Response.parse(:xml => response.to_str)
175
+ end
176
+
177
+ # Calls ListUsers API
178
+ #
179
+ # @param [Hash]
180
+ # @return [Hash]
181
+ def list_users(options = {})
182
+ params = make_pagination_params(options)
183
+ params['PathPrefix'] = options[:path_prefix] if options[:path_prefix]
184
+ response = call_api('ListUsers', params)
185
+ Response.parse(:xml => response.to_str)
186
+ end
187
+
188
+ # Calls ListGroupsForUser API
189
+ #
190
+ # @param [Hash] :user_name parameter is required
191
+ # @return [Hash]
192
+ def list_groups_for_user(options = {})
193
+ check_user_name(options)
194
+ params = make_pagination_params(options)
195
+ params['UserName'] = options[:user_name]
196
+ response = call_api('ListGroupsForUser', params)
197
+ Response.parse(:xml => response.to_str)
198
+ end
199
+
200
+ # Calls UpdateUser API
201
+ #
202
+ # @param [Hash] :user_name parameter is required
203
+ # @return [Hash]
204
+ def update_user(options = {})
205
+ check_user_name(options)
206
+ params = {}
207
+ params['UserName'] = options[:user_name]
208
+ params['NewPath'] = options[:new_path] if options[:new_path]
209
+ params['NewUserName'] = options[:new_user_name] if options[:new_user_name]
210
+ response = call_api('UpdateUser', params)
211
+ Response.parse(:xml => response.to_str)
212
+ end
213
+
214
+ # Calls CreateAccessKey API
215
+ #
216
+ # @param [Hash] options
217
+ # @return [Hash]
218
+ def create_access_key(options = {})
219
+ params = {}
220
+ params['UserName'] = options[:user_name] if options[:user_name]
221
+ response = call_api('CreateAccessKey', params)
222
+ Response.parse(:xml => response.to_str)
223
+ end
224
+
225
+ # Calls DeleteAccessKey API
226
+ #
227
+ # @param [Hash] options :access_key_id option is required.
228
+ # @return [Hash]
229
+ def delete_access_key(options = {})
230
+ check_access_key_id(options)
231
+ params = { 'AccessKeyId' => options[:access_key_id] }
232
+ params['UserName'] = options[:user_name] if options[:user_name]
233
+ response = call_api('DeleteAccessKey', params)
234
+ Response.parse(:xml => response.to_str)
235
+ end
236
+
237
+ # Calls UpdateAccessKey API
238
+ #
239
+ # @param [Hash] options :access_key_id and :status options is required.
240
+ # @return [Hash]
241
+ def update_access_key(options = {})
242
+ check_access_key_id(options)
243
+ check_activity_status(options)
244
+ params = { 'AccessKeyId' => options[:access_key_id], 'Status' => options[:status].to_s.capitalize }
245
+ params['UserName'] = options[:user_name] if options[:user_name]
246
+ response = call_api('UpdateAccessKey', params)
247
+ Response.parse(:xml => response.to_str)
248
+ end
249
+
250
+ # Calls ListAccessKeys API
251
+ #
252
+ # @param [Hash] options
253
+ # @return [Hash]
254
+ def list_access_keys(options = {})
255
+ params = make_pagination_params(options)
256
+ params['UserName'] = options[:user_name] if options[:user_name]
257
+ response = call_api('ListAccessKeys', params)
258
+ Response.parse(:xml => response.to_str)
259
+ end
260
+
261
+ # Calls GetGroupPolicy API
262
+ #
263
+ # @param [Hash] options :group_name and :policy_name options is required.
264
+ # @return [Hash]
265
+ def get_group_policy(options = {})
266
+ check_group_name(options)
267
+ check_policy_name(options)
268
+ params = { 'GroupName' => options[:group_name], 'PolicyName' => options[:policy_name] }
269
+ response = call_api('GetGroupPolicy', params)
270
+ result = Response.parse(:xml => response.to_str)
271
+ if result && result['GetGroupPolicyResult'] && result['GetGroupPolicyResult']['PolicyDocument']
272
+ result['GetGroupPolicyResult']['PolicyDocument'] = decode_uri(result['GetGroupPolicyResult']['PolicyDocument'])
273
+ end
274
+ result
275
+ end
276
+
277
+ # Calls PutGroupPolicy API
278
+ #
279
+ # @param [Hash] options
280
+ # @return [Hash]
281
+ def put_group_policy(options = {})
282
+ check_group_name(options)
283
+ check_policy_name(options)
284
+ check_policy_document(options)
285
+ params = {
286
+ 'GroupName' => options[:group_name],
287
+ 'PolicyName' => options[:policy_name],
288
+ 'PolicyDocument' => options[:policy_document]
289
+ }
290
+ response = call_api('PutGroupPolicy', params)
291
+ Response.parse(:xml => response.to_str)
292
+ end
293
+
294
+ # Calls DeleteGroupPolicy API
295
+ #
296
+ # @param [Hash] options :group_name and :policy_name options is required.
297
+ # @return [Hash]
298
+ def delete_group_policy(options = {})
299
+ check_group_name(options)
300
+ check_policy_name(options)
301
+ params = { 'GroupName' => options[:group_name], 'PolicyName' => options[:policy_name] }
302
+ response = call_api('DeleteGroupPolicy', params)
303
+ Response.parse(:xml => response.to_str)
304
+ end
305
+
306
+ # Calls ListGroupPolicies API
307
+ #
308
+ # @param [Hash] options :group_name options is required.
309
+ # @return [Hash]
310
+ def list_group_policies(options = {})
311
+ check_group_name(options)
312
+ params = make_pagination_params(options)
313
+ params['GroupName'] = options[:group_name]
314
+ response = call_api('ListGroupPolicies', params)
315
+ Response.parse(:xml => response.to_str)
316
+ end
317
+
318
+ # Calls GetUserPolicy API
319
+ #
320
+ # @param [Hash] options :user_name and :policy_name options is required.
321
+ # @return [Hash]
322
+ def get_user_policy(options = {})
323
+ check_user_name(options)
324
+ check_policy_name(options)
325
+ params = { 'UserName' => options[:user_name], 'PolicyName' => options[:policy_name] }
326
+ response = call_api('GetUserPolicy', params)
327
+ result = Response.parse(:xml => response.to_str)
328
+ if result && result['GetUserPolicyResult'] && result['GetUserPolicyResult']['PolicyDocument']
329
+ result['GetUserPolicyResult']['PolicyDocument'] = decode_uri(result['GetUserPolicyResult']['PolicyDocument'])
330
+ end
331
+ result
332
+ end
333
+
334
+ # Calls PutUserPolicy API
335
+ #
336
+ # @param [Hash] options
337
+ # @return [Hash]
338
+ def put_user_policy(options = {})
339
+ check_user_name(options)
340
+ check_policy_name(options)
341
+ check_policy_document(options)
342
+ params = {
343
+ 'UserName' => options[:user_name],
344
+ 'PolicyName' => options[:policy_name],
345
+ 'PolicyDocument' => options[:policy_document]
346
+ }
347
+ response = call_api('PutUserPolicy', params)
348
+ Response.parse(:xml => response.to_str)
349
+ end
350
+
351
+ # Calls DeleteUserPolicy API
352
+ #
353
+ # @param [Hash] options :user_name and :policy_name options is required.
354
+ # @return [Hash]
355
+ def delete_user_policy(options = {})
356
+ check_user_name(options)
357
+ check_policy_name(options)
358
+ params = { 'UserName' => options[:user_name], 'PolicyName' => options[:policy_name] }
359
+ response = call_api('DeleteUserPolicy', params)
360
+ Response.parse(:xml => response.to_str)
361
+ end
362
+
363
+ # Calls ListUserPolicies API
364
+ #
365
+ # @param [Hash] options :user_name options is required.
366
+ # @return [Hash]
367
+ def list_user_policies(options = {})
368
+ check_user_name(options)
369
+ params = make_pagination_params(options)
370
+ params['UserName'] = options[:user_name]
371
+ response = call_api('ListUserPolicies', params)
372
+ Response.parse(:xml => response.to_str)
373
+ end
374
+
375
+ # Calls UploadSigningCertificate API
376
+ #
377
+ # @param [Hash] options :certificate_body options is required.
378
+ # @return [Hash]
379
+ def upload_signing_certificate(options = {})
380
+ check_certificate_body(options)
381
+ params = { 'CertificateBody' => options[:certificate_body] }
382
+ params['UserName'] = options[:user_name] if options[:user_name]
383
+ response = call_api('UploadSigningCertificate', params)
384
+ Response.parse(:xml => response.to_str)
385
+ end
386
+
387
+ # Calls UpdateSigningCertificate API
388
+ #
389
+ # @param [Hash] options :certificate_id and :status options is required.
390
+ # @return [Hash]
391
+ def update_signing_certificate(options = {})
392
+ check_certificate_id(options)
393
+ check_activity_status(options)
394
+ params = { 'CertificateId' => options[:certificate_id], 'Status' => options[:status].to_s.capitalize }
395
+ params['UserName'] = options[:user_name] if options[:user_name]
396
+ response = call_api('UpdateSigningCertificate', params)
397
+ Response.parse(:xml => response.to_str)
398
+ end
399
+
400
+ # Calls DeleteSigningCertificate API
401
+ #
402
+ # @param [Hash] options :certificate_id options is required.
403
+ # @return [Hash]
404
+ def delete_signing_certificate(options = {})
405
+ check_certificate_id(options)
406
+ params = { 'CertificateId' => options[:certificate_id] }
407
+ params['UserName'] = options[:user_name] if options[:user_name]
408
+ response = call_api('DeleteSigningCertificate', params)
409
+ Response.parse(:xml => response.to_str)
410
+ end
411
+
412
+ # Calls list_signing_certificates API
413
+ #
414
+ # @param [Hash] options
415
+ # @return [Hash]
416
+ def list_signing_certificates(options = {})
417
+ params = make_pagination_params(options)
418
+ params['UserName'] = options[:user_name] if options[:user_name]
419
+ response = call_api('ListSigningCertificates', params)
420
+ Response.parse(:xml => response.to_str)
421
+ end
422
+
423
+ # @param [String] action AWS IAM API action name
424
+ # @param [Hash] params
425
+ # @return RestClient::Response
426
+ def call_api(action, params)
427
+ params['Action'] = action.to_s
428
+ if params['Action'] == 'UploadSigningCertificate'
429
+ return request(params, :method => :post)
430
+ end
431
+ request(params)
432
+ end
433
+
434
+ protected
435
+
436
+ # @param [Hash] params
437
+ # @return [RestClient::Response]
438
+ def request(params, options = {})
439
+ options = { :method => :get, :api_version => '2010-05-08' }.merge(options)
440
+ auth_params = {
441
+ 'AWSAccessKeyId' => @access_key_id,
442
+ 'SignatureMethod' => 'HmacSHA1',
443
+ 'SignatureVersion' => '2',
444
+ 'Timestamp' => Time.now.utc.iso8601,
445
+ 'Version' => options[:api_version]
446
+ }
447
+ signed_params = sign_to_params(auth_params.merge(params), options[:method])
448
+ if (options[:method] == :post)
449
+ return RestClient.post self.endpoint, signed_params
450
+ end
451
+ RestClient.get self.endpoint, { :params => signed_params }
452
+ end
453
+
454
+ # @param [Hash] params
455
+ # @return [Hash]
456
+ def sign_to_params(params, http_method = :get)
457
+ unless (VALID_HTTP_METHODS.include?(http_method))
458
+ raise ArgumentError, 'Invalid HTTP method proviced. method must be :get or :post'
459
+ end
460
+ tmp = []
461
+ sorted_params = params.sort { |a, b| a[0] <=> b[0] }
462
+ encoded_params = sorted_params.collect do |p|
463
+ encoded = (CGI::escape(p[0].to_s) + '=' + CGI::escape(p[1].to_s))
464
+ # Ensure spaces are encoded as '%20', not '+'
465
+ encoded = encoded.gsub('+', '%20')
466
+ # According to RFC3986 (the scheme for values expected by signing requests), '~' should not be encoded
467
+ encoded = encoded.gsub('%7E', '~')
468
+ end
469
+ sigquery = encoded_params.join('&')
470
+
471
+ # Generate the request description string
472
+ method = http_method.to_s.upcase
473
+ request_uri = '/'
474
+ req_desc = method + "\n" + default_host + "\n" + request_uri + "\n" + sigquery
475
+
476
+ # create sig
477
+ digest = OpenSSL::Digest::Digest.new('sha1')
478
+ sig = Base64.encode64(OpenSSL::HMAC.digest(digest, @secret_access_key, req_desc)).gsub("\n", '')
479
+
480
+ params.merge({ 'Signature' => sig })
481
+ end
482
+
483
+ private
484
+
485
+ # Check to be sure the :user_name option exist
486
+ # @param [Hash] options
487
+ # @return [Hash]
488
+ # @raise [ArgumentError] throw if the option[:user_name] is nil or empty.
489
+ def check_user_name(options)
490
+ raise ArgumentError, 'No user name provided' if options[:user_name].nil? || options[:user_name].empty?
491
+ options
492
+ end
493
+
494
+ # Check to be sure the :group_name option exist
495
+ #
496
+ # @param [Hash] options
497
+ # @return [Hash]
498
+ # @raise [ArgumentError] throw if the option[:group_name] is nil or empty.
499
+ def check_group_name(options)
500
+ raise ArgumentError, 'No group name provided' if options[:group_name].nil? || options[:group_name].empty?
501
+ options
502
+ end
503
+
504
+ # Check to be sure the :access_key option exist
505
+ #
506
+ # @param [Hash] options
507
+ # @return [Hash]
508
+ # @raise [ArgumentError] throw if the option[:group_name] is nil or empty.
509
+ def check_access_key_id(options)
510
+ raise ArgumentError, 'No access key id provided' if options[:access_key_id].nil? || options[:access_key_id].empty?
511
+ options
512
+ end
513
+
514
+ # Check to be sure the :status option and validate the :status option format
515
+ #
516
+ # @param [Hash] options
517
+ # @return [Hash]
518
+ # @raise [ArgumentError] throw if the option[:group_name] is nil or empty.
519
+ def check_activity_status(options)
520
+ status = options[:status].to_s
521
+ raise ArgumentError, 'No status provided' if status.empty?
522
+ unless VALID_ACCESS_KEY_STATUSES.include?(status.downcase.to_sym)
523
+ raise ArgumentError, 'status option value must be "Active" or "Inactive"'
524
+ end
525
+ options
526
+ end
527
+
528
+ # Check to be sure the :policy_name option exist
529
+ #
530
+ # @param [Hash] options
531
+ # @return [Hash]
532
+ # @raise [ArgumentError] throw if the option[:policy_name] is nil or empty.
533
+ def check_policy_name(options)
534
+ raise ArgumentError, 'No policy name provided' if options[:policy_name].nil? || options[:policy_name].empty?
535
+ options
536
+ end
537
+
538
+ # Check to be sure the :policy_document option exist
539
+ #
540
+ # @param [Hash] options
541
+ # @return [Hash]
542
+ # @raise [ArgumentError] throw if the option[:policy_document] is nil or empty.
543
+ def check_policy_document(options)
544
+ raise ArgumentError, 'No policy document provided' if options[:policy_document].nil? || options[:policy_document].empty?
545
+ options
546
+ end
547
+
548
+ # Check to be sure the :certificate_id option exist
549
+ #
550
+ # @param [Hash] options
551
+ # @return [Hash]
552
+ # @raise [ArgumentError] throw if the option[:policy_document] is nil or empty.
553
+ def check_certificate_id(options)
554
+ raise ArgumentError, 'No certificate body provided' if options[:certificate_id].nil? || options[:certificate_id].empty?
555
+ options
556
+ end
557
+
558
+ # Check to be sure the :certificate_body option exist
559
+ #
560
+ # @param [Hash] options
561
+ # @return [Hash]
562
+ # @raise [ArgumentError] throw if the option[:policy_document] is nil or empty.
563
+ def check_certificate_body(options)
564
+ raise ArgumentError, 'No certificate body provided' if options[:certificate_body].nil? || options[:certificate_body].empty?
565
+ options
566
+ end
567
+
568
+ # Make a parameters hash for ***List methos from options
569
+ #
570
+ # ex. ListAccessKeys, ListUsers, ListGroups, etc...
571
+ #
572
+ # @param [Hash] options that passed from argument
573
+ # @return [Hash]
574
+ def make_pagination_params(options)
575
+ params = {}
576
+ params['Marker'] = options[:marker] if options[:marker]
577
+ params['MaxItems'] = options[:max_items] if options[:max_items]
578
+ params
579
+ end
580
+
581
+ # Decode a URI according to RFC 3986
582
+ #
583
+ # Notice: CGI.escape is not accoding to RFC 3986,
584
+ # but CGI.unescape seems according to RFC 3986.
585
+ # ex. CGI.unescape('~') -> '~'
586
+ # CGI.unescape('%2B') -> '+'
587
+ # CGI.unescape('%20') -> ' '
588
+ #
589
+ # @param [String] uri A string encoded by RFC 3986
590
+ # @return [String] Decoded string
591
+ def decode_uri(uri)
592
+ return uri unless uri
593
+ CGI::unescape(uri)
594
+ end
595
+ end
596
+ end
597
+ end
598
+ end
599
+