diff --git a/History.txt b/History.txt
index 57610ca..dc20425 100644
--- a/History.txt
+++ b/History.txt
@@ -294,6 +294,8 @@ the source key.
- Tags Suport
- ClientToken support added on instance launch
- RDS: API "2010-07-28"
+ - ELB: API '2010-07-01' (SSL support)
+ - IAM: API '2010-05-08' (AWS Identity and Access Management interface)
- 301 Redirect support added
- Removed:
- ActiveSupport dependency
diff --git a/Manifest.txt b/Manifest.txt
index deeb917..5a846d9 100644
--- a/Manifest.txt
+++ b/Manifest.txt
@@ -33,6 +33,11 @@ lib/acf/right_acf_interface.rb
lib/acf/right_acf_streaming_interface.rb
lib/acf/right_acf_origin_access_identities.rb
lib/rds/right_rds_interface.rb
+lib/iam/right_iam_interface.rb
+lib/iam/right_iam_groups.rb
+lib/iam/right_iam_users.rb
+lib/iam/right_iam_access_keys.rb
+lib/iam/right_iam_mfa_devices.rb
test/ec2/test_helper.rb
test/ec2/test_right_ec2.rb
test/http_connection.rb
diff --git a/README.txt b/README.txt
index 8d77756..69a6d33 100644
--- a/README.txt
+++ b/README.txt
@@ -68,10 +68,6 @@ concurrent requests to AWS. The way this plays out in practice is:
Note that due to limitations in the I/O of the Ruby interpreter you
may not get the degree of parallelism you may expect with the multi-threaded setting.
-By default, EC2/S3/SQS/SDB/ACF interface instances are created in single-threaded mode. Set
-"params[:multi_thread]" to "true" in the initialization arguments to use
-multithreaded mode.
-
== GETTING STARTED:
* For EC2 read RightAws::Ec2 and consult the Amazon EC2 API documentation at
diff --git a/lib/acf/right_acf_interface.rb b/lib/acf/right_acf_interface.rb
index 31734e6..038396e 100644
--- a/lib/acf/right_acf_interface.rb
+++ b/lib/acf/right_acf_interface.rb
@@ -96,7 +96,6 @@ def self.bench_service
# * :server: CloudFront service host, default: DEFAULT_HOST
# * :port: CloudFront service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
#
# acf = RightAws::AcfInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX',
diff --git a/lib/acw/right_acw_interface.rb b/lib/acw/right_acw_interface.rb
index f6a6a3d..0691794 100644
--- a/lib/acw/right_acw_interface.rb
+++ b/lib/acw/right_acw_interface.rb
@@ -54,7 +54,6 @@ def self.bench_service
# * :server: ACW service host, default: DEFAULT_HOST
# * :port: ACW service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
# * :signature_version: The signature version : '0','1' or '2'(default)
# * :cache: true/false(default): list_metrics
diff --git a/lib/as/right_as_interface.rb b/lib/as/right_as_interface.rb
index d1840aa..bd3bd33 100644
--- a/lib/as/right_as_interface.rb
+++ b/lib/as/right_as_interface.rb
@@ -95,7 +95,6 @@ def self.bench_service
# * :server: AS service host, default: DEFAULT_HOST
# * :port: AS service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
# * :signature_version: The signature version : '0','1' or '2'(default)
# * :cache: true/false(default): describe_auto_scaling_groups
diff --git a/lib/awsbase/right_awsbase.rb b/lib/awsbase/right_awsbase.rb
index 867fd88..0261d42 100644
--- a/lib/awsbase/right_awsbase.rb
+++ b/lib/awsbase/right_awsbase.rb
@@ -23,7 +23,6 @@
# Test
module RightAws
-# require 'md5'
require 'digest/md5'
require 'pp'
@@ -288,7 +287,6 @@ def init(service_info, aws_access_key_id, aws_secret_access_key, params={}) #:no
# a set of options to be passed to RightHttpConnection object
@params[:connection_options] = {} unless @params[:connection_options].is_a?(Hash)
@with_connection_options = {}
-# @params[:multi_thread] ||= defined?(AWS_DAEMON)
@params[:connections] ||= :shared # || :dedicated
@params[:max_connections] ||= 10
@params[:connection_lifetime] ||= 20*60
@@ -360,11 +358,6 @@ def on_exception(options={:raise=>true, :log=>true}) # :nodoc:
AwsError::on_aws_exception(self, options)
end
-# # Return +true+ if this instance works in multi_thread mode and +false+ otherwise.
-# def multi_thread
-# @params[:multi_thread]
-# end
-
# ACF, AMS, EC2, LBS and SDB uses this guy
# SQS and S3 use their own methods
def generate_request_impl(verb, action, options={}) #:nodoc:
@@ -535,6 +528,20 @@ def last_request_id
@last_response && @last_response.body.to_s[%r{(.+?)}i] && $1
end
+ # Incrementally lists something.
+ def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:
+ params = params.dup
+ params['MaxItems'] = params.delete(:max_items) if params[:max_items]
+ params['Marker'] = params.delete(:marker) if params[:marker]
+ last_response = nil
+ loop do
+ last_response = request_info( generate_request(action, params), parser_class.new(:logger => @logger))
+ params['Marker'] = last_response[:marker]
+ break unless block && block.call(last_response) && !last_response[:marker].right_blank?
+ end
+ last_response
+ end
+
# Format array of items into Amazons handy hash ('?' is a place holder):
# Options:
# :default => "something" : Set a value to "something" when it is nil
diff --git a/lib/ec2/right_ec2.rb b/lib/ec2/right_ec2.rb
index 97c4995..3782129 100644
--- a/lib/ec2/right_ec2.rb
+++ b/lib/ec2/right_ec2.rb
@@ -107,7 +107,6 @@ def self.api
# * :region: EC2 region (North America by default)
# * :port: EC2 service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
# * :signature_version: The signature version : '0','1' or '2'(default)
# * :cache: true/false: caching for: ec2_describe_images, describe_instances,
diff --git a/lib/elb/right_elb_interface.rb b/lib/elb/right_elb_interface.rb
index 3e9a990..986e269 100644
--- a/lib/elb/right_elb_interface.rb
+++ b/lib/elb/right_elb_interface.rb
@@ -62,12 +62,14 @@ class ElbInterface < RightAwsBase
include RightAwsBaseInterface
# Amazon ELB API version being used
- API_VERSION = "2009-11-25"
+ API_VERSION = "2010-07-01"
DEFAULT_HOST = "elasticloadbalancing.amazonaws.com"
DEFAULT_PATH = '/'
DEFAULT_PROTOCOL = 'https'
DEFAULT_PORT = 443
+ LISTENER_PROTOCOLS = [ 'HTTP', 'HTTPS', 'TCP', 'SSL' ]
+
@@bench = AwsBenchmarkingBlock.new
def self.bench_xml
@@bench.xml
@@ -83,7 +85,6 @@ def self.bench_service
# * :server: ELB service host, default: DEFAULT_HOST
# * :port: ELB service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
# * :signature_version: The signature version : '0','1' or '2'(default)
# * :cache: true/false(default): caching works for: describe_load_balancers
@@ -172,11 +173,15 @@ def describe_load_balancers(*load_balancers)
# Create new load balancer.
# Returns a new load balancer DNS name.
#
- # lb = elb.create_load_balancer( 'test-kd1',
- # ['us-east-1a', 'us-east-1b'],
- # [ { :protocol => :http, :load_balancer_port => 80, :instance_port => 80 },
- # { :protocol => :tcp, :load_balancer_port => 443, :instance_port => 443 } ])
- # puts lb #=> "test-kd1-1519253964.us-east-1.elb.amazonaws.com"
+ # Listener options: :protocol, :load_balancer_port, :instance_port and :ssl_certificate_id
+ # Protocols: :tcp, :http, :https or :ssl
+ #
+ # elb.create_load_balancer( 'test-kd1',
+ # ['us-east-1a', 'us-east-1b'],
+ # [ { :protocol => :http, :load_balancer_port => 80, :instance_port => 80 },
+ # { :protocol => :https, :load_balancer_port => 443, :instance_port => 443,
+ # :ssl_certificate_id => 'arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob' } ])
+ # #=> "test-kd1-1519253964.us-east-1.elb.amazonaws.com"
#
def create_load_balancer(load_balancer_name, availability_zones=[], listeners=[])
request_hash = { 'LoadBalancerName' => load_balancer_name }
@@ -188,13 +193,7 @@ def create_load_balancer(load_balancer_name, availability_zones=[], listeners=[]
:load_balancer_port => 80,
:instance_port => 80 }
end
- listeners = [listeners] unless listeners.is_a?(Array)
- request_hash.merge!( amazonize_list( ['Listeners.member.?.Protocol',
- 'Listeners.member.?.LoadBalancerPort',
- 'Listeners.member.?.InstancePort'],
- listeners.map{|i| [ (i[:protocol] || 'HTTP').to_s.upcase,
- (i[:load_balancer_port] || 80),
- (i[:instance_port] || 80) ] } ) )
+ request_hash = merge_listeners_into_request_hash(request_hash, listeners)
link = generate_request("CreateLoadBalancer", request_hash)
request_info(link, CreateLoadBalancerParser.new(:logger => @logger))
end
@@ -212,6 +211,37 @@ def delete_load_balancer(load_balancer_name)
request_info(link, DeleteLoadBalancerParser.new(:logger => @logger))
end
+ # Creates one or more new listeners on a LoadBalancer for the specified port. If a listener with the given
+ # port does not already exist, it will be created; otherwise, the properties of the new listener must match
+ # the the properties of the existing listener.
+ #
+ # Listener options: :protocol, :load_balancer_port, :instance_port and :ssl_certificate_id
+ # Protocols: :tcp, :http, :https or :ssl
+ #
+ # elb.create_load_balancer_listeners( 'test-kd1',
+ # [ { :protocol => :http, :load_balancer_port => 80, :instance_port => 80 },
+ # { :protocol => :https, :load_balancer_port => 443, :instance_port => 443,
+ # :ssl_certificate_id => 'arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob' } ]) #=> true
+ #
+ def create_load_balancer_listeners(load_balancer_name, listeners)
+ request_hash = { 'LoadBalancerName' => load_balancer_name }
+ request_hash = merge_listeners_into_request_hash(request_hash, listeners)
+ link = generate_request("CreateLoadBalancerListeners", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Removes listeners from the load balancer for the specified port number.
+ #
+ # elb.delete_load_balancer_listeners( 'kd_test', 80, 443) #=> true
+ #
+ def delete_load_balancer_listeners(load_balancer_name, *load_balancer_ports)
+ load_balancer_ports.flatten!
+ request_hash = { 'LoadBalancerName' => load_balancer_name }
+ request_hash.merge!( amazonize_list("LoadBalancerPorts.member", load_balancer_ports ) )
+ link = generate_request("DeleteLoadBalancerListeners", request_hash )
+ request_info(link, DeleteLoadBalancerParser.new(:logger => @logger))
+ end
+
# Add one or more zones to a load balancer.
# Returns a list of updated availability zones for the load balancer.
#
@@ -368,6 +398,35 @@ def delete_load_balancer_policy(load_balancer_name, policy_name)
request_info(link, RightHttp2xxParser.new(:logger => @logger))
end
+ def set_load_balancer_listener_ssl_certificate(load_balancer_name, load_balancer_port, ssl_sertificate_id)
+ request_hash = { 'LoadBalancerName' => load_balancer_name,
+ 'LoadBalancerPort' => load_balancer_port,
+ 'SSLCertificateId' => ssl_sertificate_id }
+ link = generate_request("SetLoadBalancerListenerSSLCertificate", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # Helpers
+ #-----------------------------------------------------------------
+
+ def merge_listeners_into_request_hash(request_hash, listeners) # :nodoc:
+ listeners = [listeners] unless listeners.is_a?(Array)
+ request_hash.merge(amazonize_list( ['Listeners.member.?.Protocol',
+ 'Listeners.member.?.LoadBalancerPort',
+ 'Listeners.member.?.InstancePort',
+ 'Listeners.member.?.SSLCertificateId'],
+ listeners.map{ |i|
+ [ (i[:protocol] || 'HTTP').to_s.upcase,
+ i[:load_balancer_port] || 80,
+ i[:instance_port] || 80,
+ i[:ssl_certificate_id]]
+ },
+ :default => :skip_nils
+ )
+ )
+ end
+
#-----------------------------------------------------------------
# PARSERS: Load Balancers
#-----------------------------------------------------------------
@@ -400,6 +459,7 @@ def tagend(name)
when 'Protocol' then @listener[:protocol] = @text
when 'LoadBalancerPort' then @listener[:load_balancer_port] = @text
when 'InstancePort' then @listener[:instance_port] = @text
+ when 'SSLCertificateId' then @listener[:ssl_certificate_id] = @text
end
case full_tag_name
when %r{AvailabilityZones/member$} then @item[:availability_zones] << @text
diff --git a/lib/iam/right_iam_access_keys.rb b/lib/iam/right_iam_access_keys.rb
new file mode 100644
index 0000000..92df5e6
--- /dev/null
+++ b/lib/iam/right_iam_access_keys.rb
@@ -0,0 +1,71 @@
+module RightAws
+
+ class IamInterface < RightAwsBase
+
+ #-----------------------------------------------------------------
+ # Access Keys
+ #-----------------------------------------------------------------
+
+ # Returns information about the Access Key IDs associated with the specified User.
+ #
+ # Options: :user_name, :max_items, :marker
+ #
+ # iam.list_access_keys #=>
+ # [{:create_date=>"2007-01-09T06:16:30Z",
+ # :status=>"Active",
+ # :access_key_id=>"00000000000000000000"}]
+ #
+ def list_access_keys(options={}, &block)
+ incrementally_list_iam_resources('ListAccessKeys', options, &block)
+ end
+
+ # Creates a new AWS Secret Access Key and corresponding AWS Access Key ID for the specified User.
+ #
+ # Options: :user_name
+ #
+ # iam.create_access_key(:user_name => 'kd1') #=>
+ # {:access_key_id=>"AK0000000000000000ZQ",
+ # :status=>"Active",
+ # :secret_access_key=>"QXN0000000000000000000000000000000000Ioj",
+ # :create_date=>"2010-10-29T07:16:32.210Z",
+ # :user_name=>"kd1"}
+ #
+ def create_access_key(options={})
+ request_hash = {}
+ request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?
+ link = generate_request("CreateAccessKey", request_hash)
+ request_info(link, CreateAccessKeyParser.new(:logger => @logger))
+ end
+
+ # Deletes the access key associated with the specified User.
+ #
+ # Options: :user_name
+ #
+ # iam.delete_access_key('AK00000000000000006A', :user_name => 'kd1') #=> true
+ #
+ def delete_access_key(access_key_id, options={})
+ request_hash = { 'AccessKeyId' => access_key_id }
+ request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?
+ link = generate_request("DeleteAccessKey", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS
+ #-----------------------------------------------------------------
+
+ class ListAccessKeysParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ AccessKeyId CreateDate Status UserName }
+ end
+ end
+
+ class CreateAccessKeyParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ AccessKeyId CreateDate SecretAccessKey Status UserName }
+ end
+ end
+
+ end
+
+end
\ No newline at end of file
diff --git a/lib/iam/right_iam_groups.rb b/lib/iam/right_iam_groups.rb
new file mode 100644
index 0000000..3d86937
--- /dev/null
+++ b/lib/iam/right_iam_groups.rb
@@ -0,0 +1,195 @@
+module RightAws
+
+ class IamInterface < RightAwsBase
+
+ #-----------------------------------------------------------------
+ # Groups
+ #-----------------------------------------------------------------
+
+ # Lists the groups that have the specified path prefix.
+ #
+ # Options: :path_prefix, :max_items, :marker
+ #
+ # iam.list_groups #=>
+ # [{:group_id=>"AGP000000000000000UTY",
+ # :arn=>"arn:aws:iam::640000000037:group/kd_test",
+ # :path=>"/",
+ # :group_name=>"kd_test"}]
+ #
+ def list_groups(options={}, &block)
+ incrementally_list_iam_resources('ListGroups', options, &block)
+ end
+
+ # Creates a new group.
+ #
+ # iam.create_group('kd_group') #=>
+ # {:group_id=>"AGP000000000000000UTY",
+ # :arn=>"arn:aws:iam::640000000037:group/kd_test",
+ # :path=>"/",
+ # :group_name=>"kd_test"}
+ #
+ # iam.create_group('kd_test_3', '/kd/') #=>
+ # {:group_id=>"AGP000000000000000G6Q",
+ # :arn=>"arn:aws:iam::640000000037:group/kd/kd_test_3",
+ # :path=>"/kd/",
+ # :group_name=>"kd_test_3"}
+ #
+ def create_group(group_name, path=nil)
+ request_hash = { 'GroupName' => group_name }
+ request_hash['Path'] = path unless path.right_blank?
+ link = generate_request("CreateGroup", request_hash)
+ request_info(link, CreateGroupParser.new(:logger => @logger))
+ end
+
+ # Updates the name and/or the path of the specified group
+ #
+ # Options: :new_group_name, :new_path
+ #
+ # iam.update_group('kd_test', :new_group_name => 'kd_test_1', :new_path => '/kd1/') #=> true
+ #
+ def update_group(group_name, options={})
+ request_hash = { 'GroupName' => group_name}
+ request_hash['NewGroupName'] = options[:new_group_name] unless options[:new_group_name].right_blank?
+ request_hash['NewPath'] = options[:new_path] unless options[:new_path].right_blank?
+ link = generate_request("UpdateGroup", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Returns a list of Users that are in the specified group.
+ #
+ # Options: :max_items, :marker
+ #
+ # iam.get_group('kd_test') #=>
+ # {:arn=>"arn:aws:iam::640000000037:group/kd1/kd_test_1",
+ # :users=>
+ # [{:arn=>"arn:aws:iam::640000000037:user/kd",
+ # :path=>"/",
+ # :user_name=>"kd",
+ # :user_id=>"AID000000000000000WZ2"}],
+ # :group_name=>"kd_test_1",
+ # :group_id=>"AGP000000000000000UTY",
+ # :path=>"/kd1/"}
+ #
+ def get_group(group_name, options={}, &block)
+ options[:group_name] = group_name
+ incrementally_list_iam_resources('GetGroup', options, :items => :users, :except => [:marker, :is_truncated], &block)
+ end
+
+ # Deletes the specified group. The group must not contain any Users or have any attached policies.
+ #
+ # iam.delete_group('kd_test_3') #=> true
+ #
+ def delete_group(group_name)
+ request_hash = { 'GroupName' => group_name }
+ link = generate_request("DeleteGroup", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # Group Policies
+ #-----------------------------------------------------------------
+
+ # Lists the names of the policies associated with the specified group.
+ #
+ # Options: :max_items, :marker
+ #
+ # iam.list_group_policies('kd_test') #=> ["kd_policy_1"]
+ #
+ def list_group_policies(group_name, options={}, &block)
+ options[:group_name] = group_name
+ incrementally_list_iam_resources('ListGroupPolicies', options, :parser => BasicIamListParser, &block)
+ end
+
+ # Adds (or updates) a policy document associated with the specified group.
+ #
+ # iam.put_group_policy('kd_test', 'kd_policy_1', %Q({"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]})) #=> true
+ #
+ def put_group_policy(group_name, policy_name, policy_document)
+ request_hash = { 'GroupName' => group_name,
+ 'PolicyDocument' => policy_document,
+ 'PolicyName' => policy_name }
+ link = generate_request_impl(:post, "PutGroupPolicy", request_hash)
+ result = request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ result[:policy_document] = URI::decode(result[:policy_document])
+ result
+ end
+
+ # Retrieves the specified policy document for the specified group.
+ #
+ # iam.get_group_policy('kd_test', 'kd_policy_1') #=>
+ # {:policy_name=>"kd_policy_1",
+ # :policy_document=>"{\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
+ # :group_name=>"kd_test"}
+ #
+ def get_group_policy(group_name, policy_name)
+ request_hash = { 'GroupName' => group_name,
+ 'PolicyName' => policy_name }
+ link = generate_request("GetGroupPolicy", request_hash)
+ request_info(link, GetGroupPolicyParser.new(:logger => @logger))
+ end
+
+ # Deletes the specified policy that is associated with the specified group
+ #
+ # iam.delete_group_policy('kd_test', 'kd_policy_1') #=> true
+ #
+ def delete_group_policy(group_name, policy_name)
+ request_hash = { 'GroupName' => group_name,
+ 'PolicyName' => policy_name }
+ link = generate_request("DeleteGroupPolicy", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS:
+ #-----------------------------------------------------------------
+
+ class ListGroupsParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn GroupId GroupName Path }
+ end
+ end
+
+ class CreateGroupParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn GroupId GroupName Path }
+ end
+ end
+
+ class GetGroupParser < RightAWSParser #:nodoc:
+ def tagstart(name, attributes)
+ @item = {} if name == 'member'
+ end
+ def tagend(name)
+ case name
+ when 'Marker' then @result[:marker] = @text
+ when 'IsTruncated' then @result[:is_truncated] = @text == 'true'
+
+ when 'GroupName' then @result[:group_name] = @text
+ when 'GroupId' then @result[:group_id] = @text
+ when 'UserName' then @item[:user_name] = @text
+ when 'UserId' then @item[:user_id] = @text
+ when 'member' then @result[:users] << @item
+ else
+ case full_tag_name
+ when %r{/Group/Path$} then @result[:path] = @text
+ when %r{/Group/Arn$} then @result[:arn] = @text
+ when %r{/member/Path$} then @item[:path] = @text
+ when %r{/member/Arn$} then @item[:arn] = @text
+ end
+ end
+ end
+ def reset
+ @result = { :users => [] }
+ end
+ end
+
+ class GetGroupPolicyParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ GroupName PolicyDocument PolicyName }
+ end
+ end
+
+ end
+
+end
+
diff --git a/lib/iam/right_iam_interface.rb b/lib/iam/right_iam_interface.rb
new file mode 100644
index 0000000..9ae659d
--- /dev/null
+++ b/lib/iam/right_iam_interface.rb
@@ -0,0 +1,341 @@
+#
+# Copyright (c) 2007-2010 RightScale Inc
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+module RightAws
+
+ # = RightAWS::Iam -- RightScale AWS Identity and Access Management (IAM) interface
+ #
+ # The RightAws::Iam class provides a complete interface to Amazon's Identity and
+ # Access Management service.
+ #
+ # For explanations of the semantics of each call, please refer to Amazon's documentation at
+ # http://aws.amazon.com/documentation/iam/
+ #
+ # Examples:
+ #
+ # Create an EC2 interface handle:
+ #
+ # iam = RightAws::IamInterface.new(aws_access_key_id, aws_secret_access_key)
+ # iam.list_access_keys
+ # iam.list_users
+ # iam.list_groups
+ #
+ class IamInterface < RightAwsBase
+ include RightAwsBaseInterface
+
+ API_VERSION = "2010-05-08"
+ DEFAULT_HOST = "iam.amazonaws.com"
+ DEFAULT_PATH = '/'
+ DEFAULT_PROTOCOL = 'https'
+ DEFAULT_PORT = 443
+
+ @@bench = AwsBenchmarkingBlock.new
+ def self.bench_xml
+ @@bench.xml
+ end
+ def self.bench_service
+ @@bench.service
+ end
+
+ # Create a new handle to an IAM account. All handles share the same per process or per thread
+ # HTTP connection to Amazon IAM. Each handle is for a specific account. The params have the
+ # following options:
+ # * :endpoint_url a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol).
+ # * :server: IAM service host, default: DEFAULT_HOST
+ # * :port: IAM service port, default: DEFAULT_PORT
+ # * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
+ # * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
+ # * :signature_version: The signature version : '0','1' or '2'(default)
+ # * :cache: true/false(default): caching works for: describe_load_balancers
+ #
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
+ init({ :name => 'IAM',
+ :default_host => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).host : DEFAULT_HOST,
+ :default_port => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).port : DEFAULT_PORT,
+ :default_service => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).path : DEFAULT_PATH,
+ :default_protocol => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).scheme : DEFAULT_PROTOCOL,
+ :default_api_version => ENV['IAM_API_VERSION'] || API_VERSION },
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
+ aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
+ params)
+ end
+
+ def generate_request(action, params={}) #:nodoc:
+ generate_request_impl(:get, action, params )
+ end
+
+ # Sends request to Amazon and parses the response
+ # Raises AwsError if any banana happened
+ def request_info(request, parser) #:nodoc:
+ request_info_impl(:iam_connection, @@bench, request, parser)
+ end
+
+ # Options: :parser, :except, :items
+ #
+ def incrementally_list_iam_resources(api_function, params={}, options={}, &block) #:nodoc:
+ items = options[:items] || :items
+ result = { items => [] }
+ parser = options[:parser] || "RightAws::IamInterface::#{api_function}Parser".right_constantize
+ request_hash = {}
+ params.each { |key,value| request_hash[key.to_s.right_camelize] = value unless value.right_blank? }
+ incrementally_list_items(api_function, parser, request_hash) do |response|
+ if result[items].right_blank?
+ result = response
+ else
+ result[items] += response[items]
+ end
+ block ? block.call(response) : true
+ end
+ if options[:except]
+ Array(options[:except]).each{ |key| result.delete(key)}
+ result
+ else
+ result[items]
+ end
+ end
+
+ #-----------------------------------------------------------------
+ # Server Certificates
+ #-----------------------------------------------------------------
+
+ # Lists the server certificates that have the specified path prefix. If none exist, the action returns an empty list.
+ #
+ # Options: :path_prefix, :max_items, :marker
+ #
+ # iam.list_server_certificates #=>
+ # {:server_certificate_id=>"ASCDJN5K5HRGS1N2UJWWU",
+ # :server_certificate_name=>"KdCert1",
+ # :upload_date=>"2010-12-09T13:21:07.226Z",
+ # :path=>"/kdcert/",
+ # :arn=>"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1"}
+ #
+ def list_server_certificates(options={}, &block)
+ incrementally_list_iam_resources('ListServerCertificates', options, &block)
+ end
+
+ # Uploads a server certificate entity for the AWS Account. The server certificate
+ # entity includes a public key certificate, a private key, and an optional certificate
+ # chain, which should all be PEM-encoded.
+ #
+ # Options: :certificate_chain, :path
+ #
+ # certificate_body =<<-EOB
+ # -----BEGIN CERTIFICATE-----
+ # MIICdzCCAeCgAwIBAgIGANc+Ha2wMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYT
+ # AlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMQwwCgYDVQQLEwNBV1MxITAfBgNVBAMT
+ # GEFXUyBMaW1pdGVkLUFzc3VyYW5jZSBDQTAeFw0wOTAyMDQxNzE5MjdaFw0xMDAy
+ # AEaHzTpmEXAMPLE=
+ # EOB
+ #
+ # private_key =<'/kdcert/') #=>
+ # {:server_certificate_id=>"ASCDJN5K5HRGS1N2UJWWU",
+ # :server_certificate_name=>"KdCert1",
+ # :upload_date=>"2010-12-09T13:21:07.226Z",
+ # :path=>"/kdcert/",
+ # :arn=>"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1"}
+ #
+ def upload_server_certificate(server_certificate_name, certificate_body, private_key, options={})
+ request_hash = { 'CertificateBody' => certificate_body,
+ 'PrivateKey' => private_key,
+ 'ServerCertificateName' => server_certificate_name }
+ request_hash['CertificateChain'] = options[:certificate_chain] unless options[:certificate_chain].right_blank?
+ request_hash['Path'] = options[:path] unless options[:path].right_blank?
+ link = generate_request_impl(:post, "UploadServerCertificate", request_hash)
+ request_info(link, GetServerCertificateParser.new(:logger => @logger))
+ end
+
+ # Updates the name and/or the path of the specified server certificate.
+ #
+ # Options: :new_server_certificate_name, :new_path
+ #
+ # iam.update_server_certificate('ProdServerCert', :new_server_certificate_name => 'OldServerCert') #=> true
+ #
+ def update_server_certificate(server_certificate_name, options={})
+ request_hash = { 'ServerCertificateName' => server_certificate_name}
+ request_hash['NewServerCertificateName'] = options[:new_server_certificate_name] unless options[:new_server_certificate_name].right_blank?
+ request_hash['NewPath'] = options[:new_path] unless options[:new_path].right_blank?
+ link = generate_request("UpdateServerCertificate", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Retrieves information about the specified server certificate.
+ #
+ # iam.get_server_certificate('KdCert1')
+ # {:certificate_body=>
+ # "-----BEGIN CERTIFICATE-----\nMIICATC...TiU5TibMpD1g==\n-----END CERTIFICATE-----",
+ # :server_certificate_id=>"ASCDJN5K5HRGS1N2UJWWU",
+ # :server_certificate_name=>"KdCert1",
+ # :upload_date=>"2010-12-09T13:21:07Z",
+ # :path=>"/kdcert/",
+ # :certificate_chain=>"",
+ # :arn=>"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1"}
+ #
+ def get_server_certificate(server_certificate_name)
+ request_hash = { 'ServerCertificateName' => server_certificate_name}
+ link = generate_request("GetServerCertificate", request_hash)
+ request_info(link, GetServerCertificateParser.new(:logger => @logger))
+ end
+
+ # Deletes the specified server certificate
+ #
+ # iam.delete_server_certificate('ProdServerCert') #=> true
+ #
+ def delete_server_certificate(server_certificate_name)
+ request_hash = { 'ServerCertificateName' => server_certificate_name }
+ link = generate_request("DeleteServerCertificate", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # Signing Certificates
+ #-----------------------------------------------------------------
+
+ # Returns information about the signing certificates associated with the specified User.
+ #
+ # Options: :user_name, :max_items, :marker
+ #
+ # iam.list_signing_certificates #=>
+ # [{:upload_date => "2007-08-11T06:48:35Z",
+ # :status => "Active",
+ # :certificate_id => "00000000000000000000000000000000",
+ # :certificate_body => "-----BEGIN CERTIFICATE-----\nMIICd...PPHQ=\n-----END CERTIFICATE-----\n"}]
+ #
+ def list_signing_certificates(options={}, &block)
+ incrementally_list_iam_resources('ListSigningCertificates', options, &block)
+ end
+
+ # Uploads an X.509 signing certificate and associates it with the specified User.
+ #
+ # Options: :user_name
+ #
+ # certificate_body =<<-EOB
+ # -----BEGIN CERTIFICATE-----
+ # MIICdzCCAeCgAwIBAgIGANc+Ha2wMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYT
+ # AlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMQwwCgYDVQQLEwNBV1MxITAfBgNVBAMT
+ # GEFXUyBMaW1pdGVkLUFzc3VyYW5jZSBDQTAeFw0wOTAyMDQxNzE5MjdaFw0xMDAy
+ # AEaHzTpmEXAMPLE=
+ # EOB
+ #
+ # iam.upload_signing_certificate(certificate_body, :user_name => 'kd1') #=>
+ # {:user_name => "kd1",
+ # :certificate_id => "OBG00000000000000000000000000DHY",
+ # :status => "Active",
+ # :certificate_body => "-----BEGIN CERTIFICATE-----\nMII...5GS\n-----END CERTIFICATE-----\n",
+ # :upload_date => "2010-10-29T10:02:05.929Z"}
+ #
+ def upload_signing_certificate(certificate_body, options={})
+ request_hash = { 'CertificateBody' => certificate_body }
+ request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?
+ link = generate_request_impl(:post, "UploadSigningCertificate", request_hash)
+ request_info(link, GetSigningCertificateParser.new(:logger => @logger))
+ end
+
+ # Deletes the specified signing certificate associated with the specified User.
+ #
+ # Options: :user_name
+ #
+ # pp iam.delete_signing_certificate('OB0000000000000000000000000000HY', :user_name => 'kd1')
+ #
+ def delete_signing_certificate(certificate_id, options={})
+ request_hash = { 'CertificateId' => certificate_id }
+ request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?
+ link = generate_request("DeleteSigningCertificate", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS:
+ #-----------------------------------------------------------------
+
+ class BasicIamParser < RightAWSParser #:nodoc:
+ def tagstart(name, attributes)
+ @result ||= {}
+ end
+ def tagend(name)
+ if Array(@expected_tags).include?(name)
+ @result[name.right_underscore.to_sym] = @text
+ end
+ end
+ end
+
+ class BasicIamListParser < RightAWSParser #:nodoc:
+ def tagstart(name, attributes)
+ @result ||= { :items => [] }
+ @item = {} if name == (@items_splitter || 'member')
+ end
+ def tagend(name)
+ case name
+ when 'Marker' then @result[:marker] = @text
+ when 'IsTruncated' then @result[:is_truncated] = @text == 'true'
+ when (@items_splitter || 'member')
+ @result[:items] << (@item.right_blank? ? @text : @item)
+ else
+ if Array(@expected_tags).include?(name)
+ @item[name.right_underscore.to_sym] = @text
+ end
+ end
+ end
+ end
+
+ #-----------------------------------------------------------------
+ # Server Certificates
+ #-----------------------------------------------------------------
+
+ class GetServerCertificateParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn Path ServerCertificateId ServerCertificateName UploadDate CertificateBody CertificateChain }
+ end
+ end
+
+ class ListServerCertificatesParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn Path ServerCertificateId ServerCertificateName UploadDate }
+ end
+ end
+
+ #-----------------------------------------------------------------
+ # Signing Certificates
+ #-----------------------------------------------------------------
+
+ class ListSigningCertificatesParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ CertificateBody CertificateId Status UploadDate UserName }
+ end
+ end
+
+ class GetSigningCertificateParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ CertificateBody CertificateId Status UploadDate UserName }
+ end
+ end
+
+ end
+
+end
diff --git a/lib/iam/right_iam_mfa_devices.rb b/lib/iam/right_iam_mfa_devices.rb
new file mode 100644
index 0000000..85ea723
--- /dev/null
+++ b/lib/iam/right_iam_mfa_devices.rb
@@ -0,0 +1,67 @@
+module RightAws
+
+ class IamInterface < RightAwsBase
+
+ #-----------------------------------------------------------------
+ # MFADevices
+ #-----------------------------------------------------------------
+
+ # Lists the MFA devices associated with the specified User name.
+ #
+ # Options: :user_name, :max_items, :marker
+ #
+ def list_mfa_devices(options={}, &block)
+ incrementally_list_iam_resources('ListMFADevices', options, &block)
+ end
+
+ # Enables the specified MFA device and associates it with the specified User name.
+ # Once enabled, the MFA device is required for every subsequent login by the User name associated with the device.
+ #
+ # iam.enable_mfa_device('kd1', 'x12345', '12345', '67890') #=> true
+ #
+ def enable_mfa_device(user_name, serial_number, auth_code1, auth_code2)
+ request_hash = { 'UserName' => user_name,
+ 'SerialNumber' => serial_number,
+ 'AuthenticationCode1' => auth_code1,
+ 'AuthenticationCode2' => auth_code2 }
+ link = generate_request("EnableMFADevice", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Synchronizes the specified MFA device with AWS servers.
+ #
+ # iam.resync_mfa_device('kd1', 'x12345', '12345', '67890') #=> true
+ #
+ def resync_mfa_device(user_name, serial_number, auth_code1, auth_code2)
+ request_hash = { 'UserName' => user_name,
+ 'SerialNumber' => serial_number,
+ 'AuthenticationCode1' => auth_code1,
+ 'AuthenticationCode2' => auth_code2 }
+ link = generate_request("ResyncMFADevice", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Deactivates the specified MFA device and removes it from association with the User name for which it was originally enabled.
+ #
+ # deactivate_mfa_device('kd1', 'dev1234567890') #=> true
+ #
+ def deactivate_mfa_device(user_name, serial_number)
+ request_hash = { 'UserName' => user_name,
+ 'SerialNumber' => serial_number }
+ link = generate_request("DeactivateMFADevice", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS
+ #-----------------------------------------------------------------
+
+ class ListMFADevicesParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ SerialNumber UserName }
+ end
+ end
+
+ end
+
+end
\ No newline at end of file
diff --git a/lib/iam/right_iam_users.rb b/lib/iam/right_iam_users.rb
new file mode 100644
index 0000000..0201533
--- /dev/null
+++ b/lib/iam/right_iam_users.rb
@@ -0,0 +1,251 @@
+module RightAws
+
+ class IamInterface < RightAwsBase
+
+ #-----------------------------------------------------------------
+ # Users
+ #-----------------------------------------------------------------
+
+ # Lists the Users that have the specified path prefix.
+ #
+ # Options: :path_prefix, :max_items, :marker
+ #
+ # iam.list_users #=>
+ # [{:user_name=>"kd",
+ # :user_id=>"AI000000000000000006A",
+ # :arn=>"arn:aws:iam::640000000037:user/kd",
+ # :path=>"/"}]
+ #
+ def list_users(options={}, &block)
+ incrementally_list_iam_resources('ListUsers', options, &block)
+ end
+
+ # Creates a new User for your AWS Account.
+ #
+ # Options: :path
+ #
+ # iam.create_user('kd') #=>
+ # {:user_name=>"kd",
+ # :user_id=>"AI000000000000000006A",
+ # :arn=>"arn:aws:iam::640000000037:user/kd",
+ # :path=>"/"}
+ #
+ def create_user(user_name, options={})
+ request_hash = { 'UserName' => user_name }
+ request_hash['Path'] = options[:path] unless options[:path]
+ link = generate_request("CreateUser", request_hash)
+ request_info(link, GetUserParser.new(:logger => @logger))
+ end
+
+ # Updates the name and/or the path of the specified User.
+ #
+ # iam.update_user('kd1', :new_user_name => 'kd1', :new_path => '/kd1/') #=> true
+ #
+ def update_user(user_name, options={})
+ request_hash = { 'UserName' => user_name}
+ request_hash['NewUserName'] = options[:new_user_name] unless options[:new_user_name].right_blank?
+ request_hash['NewPath'] = options[:new_path] unless options[:new_path].right_blank?
+ link = generate_request("UpdateUser", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Retrieves information about the specified User, including the User's path, GUID, and ARN.
+ #
+ # iam.get_user('kd') #=>
+ # {:user_name=>"kd",
+ # :user_id=>"AI000000000000000006A",
+ # :arn=>"arn:aws:iam::640000000037:user/kd",
+ # :path=>"/"}
+ #
+ def get_user(user_name)
+ request_hash = { 'UserName' => user_name }
+ link = generate_request("GetUser", request_hash)
+ request_info(link, GetUserParser.new(:logger => @logger))
+ end
+
+ # Deletes the specified User. The User must not belong to any groups, have any keys or signing certificates, or have any attached policies.
+ #
+ # iam.delete_user('kd') #=> true
+ #
+ def delete_user(user_name)
+ request_hash = { 'UserName' => user_name }
+ link = generate_request("DeleteUser", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # User Policies
+ #-----------------------------------------------------------------
+
+ # Lists the names of the policies associated with the specified User.
+ #
+ # Options: :max_items, :marker
+ #
+ # iam.list_user_policies('kd') #=> ["kd_user_policy_1"]
+ #
+ def list_user_policies(user_name, options={}, &block)
+ options[:user_name] = user_name
+ incrementally_list_iam_resources('ListUserPolicies', options, :parser => BasicIamListParser, &block)
+ end
+
+ # Adds (or updates) a policy document associated with the specified User
+ #
+ # iam.put_user_policy('kd', 'kd_user_policy_1', %Q({"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]})) #=> true
+ #
+ def put_user_policy(user_name, policy_name, policy_document)
+ request_hash = { 'UserName' => user_name,
+ 'PolicyDocument' => policy_document,
+ 'PolicyName' => policy_name }
+ link = generate_request_impl(:post, "PutUserPolicy", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Retrieves the specified policy document for the specified User.
+ #
+ # iam.get_user_policy('kd','kd_user_policy_1') #=>
+ # {:user_name=>"kd",
+ # :policy_name=>"kd_user_policy_1",
+ # :policy_document=>"{\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}]}"}
+ #
+ def get_user_policy(user_name, policy_name)
+ request_hash = { 'UserName' => user_name,
+ 'PolicyName' => policy_name }
+ link = generate_request("GetUserPolicy", request_hash)
+ result = request_info(link, GetUserPolicyParser.new(:logger => @logger))
+ result[:policy_document] = URI::decode(result[:policy_document])
+ result
+ end
+
+ # Deletes the specified policy associated with the specified User.
+ #
+ # iam.delete_user_policy('kd','kd_user_policy_1') #=> true
+ #
+ def delete_user_policy(user_name, policy_name)
+ request_hash = { 'UserName' => user_name,
+ 'PolicyName' => policy_name }
+ link = generate_request("DeleteUserPolicy", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # User Groups
+ #-----------------------------------------------------------------
+
+ # Lists the names of the policies associated with the specified group. If there are none,
+ # the action returns an empty list.
+ #
+ # Options: :max_items, :marker
+ #
+ # iam.list_groups_for_user('kd') #=>
+ # [{:group_name=>"kd_test_1",
+ # :group_id=>"AGP000000000000000UTY",
+ # :arn=>"arn:aws:iam::640000000037:group/kd1/kd_test_1",
+ # :path=>"/kd1/"}]
+ #
+ def list_groups_for_user(user_name, options={}, &block)
+ options[:user_name] = user_name
+ incrementally_list_iam_resources('ListGroupsForUser', options, :parser => ListGroupsParser, &block)
+ end
+
+ # Adds the specified User to the specified group.
+ #
+ # iam.add_user_to_group('kd', 'kd_test_1') #=> true
+ #
+ def add_user_to_group(user_name, group_name)
+ request_hash = { 'UserName' => user_name,
+ 'GroupName' => group_name }
+ link = generate_request("AddUserToGroup", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Removes the specified User from the specified group.
+ #
+ # iam.remove_user_from_group('kd', 'kd_test_1') #=> true
+ #
+ def remove_user_from_group(user_name, group_name)
+ request_hash = { 'UserName' => user_name,
+ 'GroupName' => group_name }
+ link = generate_request("RemoveUserFromGroup", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # User Login Profiles
+ #-----------------------------------------------------------------
+
+ # Creates a login profile for the specified User, giving the User the ability to access
+ # AWS services such as the AWS Management Console.
+ #
+ # iam.create_login_profile('kd','q1w2e3r4t5') #=> { :user_name => 'kd' }
+ #
+ def create_login_profile(user_name, password)
+ request_hash = { 'UserName' => user_name,
+ 'Password' => password}
+ link = generate_request("CreateLoginProfile", request_hash)
+ request_info(link, GetLoginProfileParser.new(:logger => @logger))
+ end
+
+ # Updates the login profile for the specified User. Use this API to change the User's password.
+ #
+ # update_login_profile('kd', '00000000') #=> true
+ #
+ def update_login_profile(user_name, options={})
+ request_hash = { 'UserName' => user_name}
+ request_hash['Password'] = options[:password] unless options[:passwrod].right_blank?
+ link = generate_request("UpdateLoginProfile", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ # Retrieves the login profile for the specified User
+ #
+ # iam.create_login_profile('kd','q1w2e3r4t5') #=> { :user_name => 'kd' }
+ #
+ def get_login_profile(user_name)
+ request_hash = { 'UserName' => user_name }
+ link = generate_request("GetLoginProfile", request_hash)
+ request_info(link, GetLoginProfileParser.new(:logger => @logger))
+ end
+
+ # Deletes the login profile for the specified User, which terminates the User's ability to access
+ # AWS services through the IAM login page.
+ #
+ # iam.delete_login_profile('kd') #=> true
+ #
+ def delete_login_profile(user_name)
+ request_hash = { 'UserName' => user_name }
+ link = generate_request("DeleteLoginProfile", request_hash)
+ request_info(link, RightHttp2xxParser.new(:logger => @logger))
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS
+ #-----------------------------------------------------------------
+
+ class ListUsersParser < BasicIamListParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn Path UserId UserName }
+ end
+ end
+
+ class GetUserParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ Arn Path UserId UserName }
+ end
+ end
+
+ class GetUserPolicyParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ PolicyDocument PolicyName UserName }
+ end
+ end
+
+ class GetLoginProfileParser < BasicIamParser #:nodoc:
+ def reset
+ @expected_tags = %w{ UserName }
+ end
+ end
+
+ end
+
+end
+
diff --git a/lib/rds/right_rds_interface.rb b/lib/rds/right_rds_interface.rb
index b9b0017..19742fb 100644
--- a/lib/rds/right_rds_interface.rb
+++ b/lib/rds/right_rds_interface.rb
@@ -51,7 +51,6 @@ def self.bench_service
# * :server: RDS service host, default: DEFAULT_HOST
# * :port: RDS service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
- # * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
#
# rds = RightAws::RdsInterface.new('xxxxxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
diff --git a/lib/right_aws.rb b/lib/right_aws.rb
index 80b7b04..f58a467 100644
--- a/lib/right_aws.rb
+++ b/lib/right_aws.rb
@@ -65,6 +65,12 @@
require 'acf/right_acf_streaming_interface'
require 'acf/right_acf_origin_access_identities'
require 'rds/right_rds_interface'
+require 'iam/right_iam_interface'
+require 'iam/right_iam_groups'
+require 'iam/right_iam_users'
+require 'iam/right_iam_access_keys'
+require 'iam/right_iam_mfa_devices'
+
#-
# We also want everything available in the Rightscale namespace for backward
diff --git a/lib/s3/right_s3.rb b/lib/s3/right_s3.rb
index 459c857..7d19604 100644
--- a/lib/s3/right_s3.rb
+++ b/lib/s3/right_s3.rb
@@ -59,7 +59,6 @@ class S3
# {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
# :port => 443 # Amazon service port: 80 or 443(default)
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
@interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
diff --git a/lib/s3/right_s3_interface.rb b/lib/s3/right_s3_interface.rb
index f93015b..bf816fc 100644
--- a/lib/s3/right_s3_interface.rb
+++ b/lib/s3/right_s3_interface.rb
@@ -63,14 +63,13 @@ def param(name)
# Creates new RightS3 instance.
#
- # s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #
+ # s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #
#
# Params is a hash:
#
# {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
# :port => 443 # Amazon service port: 80 or 443(default)
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
#
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
diff --git a/lib/sdb/active_sdb.rb b/lib/sdb/active_sdb.rb
index ee5fea9..cbbafb1 100644
--- a/lib/sdb/active_sdb.rb
+++ b/lib/sdb/active_sdb.rb
@@ -152,7 +152,6 @@ def connection
# :port => 443 # Amazon service port: 80 or 443(default)
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
# :signature_version => '0' # The signature version : '0' or '1'(default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
# :logger => Logger Object # Logger instance: logs to STDOUT if omitted
# :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
diff --git a/lib/sdb/right_sdb_interface.rb b/lib/sdb/right_sdb_interface.rb
index f712c69..44717d7 100644
--- a/lib/sdb/right_sdb_interface.rb
+++ b/lib/sdb/right_sdb_interface.rb
@@ -49,13 +49,12 @@ def self.bench_sdb; @@bench.service; end
# :port => 443 # Amazon service port: 80 or 443(default)
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
# :signature_version => '0' # The signature version : '0','1 or '2'(default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
# :logger => Logger Object # Logger instance: logs to STDOUT if omitted
# :nil_representation => 'mynil'} # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')
#
# Example:
#
- # sdb = RightAws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #
+ # sdb = RightAws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #
#
# see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
#
diff --git a/lib/sqs/right_sqs.rb b/lib/sqs/right_sqs.rb
index 13decbf..22b337c 100644
--- a/lib/sqs/right_sqs.rb
+++ b/lib/sqs/right_sqs.rb
@@ -59,7 +59,6 @@ module RightAws
#
# {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)
# :port => 443 # Amazon service port: 80 or 443 (default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false (default)
# :signature_version => '0' # The signature version : '0' or '1'(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
#
diff --git a/lib/sqs/right_sqs_gen2.rb b/lib/sqs/right_sqs_gen2.rb
index 1f9ff48..8575c57 100644
--- a/lib/sqs/right_sqs_gen2.rb
+++ b/lib/sqs/right_sqs_gen2.rb
@@ -60,7 +60,6 @@ module RightAws
#
# {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)
# :port => 443 # Amazon service port: 80 or 443 (default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false (default)
# :signature_version => '0' # The signature version : '0' or '1'(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
class SqsGen2
diff --git a/lib/sqs/right_sqs_gen2_interface.rb b/lib/sqs/right_sqs_gen2_interface.rb
index 2928ae0..8bf1758 100644
--- a/lib/sqs/right_sqs_gen2_interface.rb
+++ b/lib/sqs/right_sqs_gen2_interface.rb
@@ -65,13 +65,12 @@ def self.api
# Amazon's article "Migrating to Amazon SQS API version 2008-01-01" at:
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1148
#
- # sqs = RightAws::SqsGen2Interface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')})
+ # sqs = RightAws::SqsGen2Interface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:logger => Logger.new('/tmp/x.log')})
#
# Params is a hash:
#
# {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)
# :port => 443 # Amazon service port: 80 or 443 (default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false (default)
# :signature_version => '0' # The signature version : '0', '1' or '2'(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
#
diff --git a/lib/sqs/right_sqs_interface.rb b/lib/sqs/right_sqs_interface.rb
index e9c4723..5e50b78 100644
--- a/lib/sqs/right_sqs_interface.rb
+++ b/lib/sqs/right_sqs_interface.rb
@@ -49,13 +49,12 @@ def self.api
# Creates a new SqsInterface instance.
#
- # sqs = RightAws::SqsInterface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')})
+ # sqs = RightAws::SqsInterface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:logger => Logger.new('/tmp/x.log')})
#
# Params is a hash:
#
# {:server => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com'(default)
# :port => 443 # Amazon service port: 80 or 443(default)
- # :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
# :signature_version => '0' # The signature version : '0', '1' or '2'(default)
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
#
diff --git a/test/sqs/test_right_sqs.rb b/test/sqs/test_right_sqs.rb
index ea780e8..b13c7be 100644
--- a/test/sqs/test_right_sqs.rb
+++ b/test/sqs/test_right_sqs.rb
@@ -273,12 +273,6 @@ def test_27_set_amazon_problems
assert_nil(Rightscale::SqsInterface.amazon_problems)
end
- def test_28_check_threading_model
- assert(!@sqs.multi_thread)
- newsqs = Rightscale::SqsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, {:multi_thread => true})
- assert(newsqs.multi_thread)
- end
-
def test_29_signature_version_0
sqs = Rightscale::SqsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0')
assert_nothing_raised do