stratus 1.0.0

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,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
+