# # 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