| OLD | NEW |
| 1 # Copyright (c) 2010-2011 Mitch Garnaat http://garnaat.org/ | 1 # Copyright (c) 2010-2011 Mitch Garnaat http://garnaat.org/ |
| 2 # Copyright (c) 2010-2011, Eucalyptus Systems, Inc. | 2 # Copyright (c) 2010-2011, Eucalyptus Systems, Inc. |
| 3 # | 3 # |
| 4 # Permission is hereby granted, free of charge, to any person obtaining a | 4 # Permission is hereby granted, free of charge, to any person obtaining a |
| 5 # copy of this software and associated documentation files (the | 5 # copy of this software and associated documentation files (the |
| 6 # "Software"), to deal in the Software without restriction, including | 6 # "Software"), to deal in the Software without restriction, including |
| 7 # without limitation the rights to use, copy, modify, merge, publish, dis- | 7 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 8 # tribute, sublicense, and/or sell copies of the Software, and to permit | 8 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 9 # persons to whom the Software is furnished to do so, subject to the fol- | 9 # persons to whom the Software is furnished to do so, subject to the fol- |
| 10 # lowing conditions: | 10 # lowing conditions: |
| 11 # | 11 # |
| 12 # The above copyright notice and this permission notice shall be included | 12 # The above copyright notice and this permission notice shall be included |
| 13 # in all copies or substantial portions of the Software. | 13 # in all copies or substantial portions of the Software. |
| 14 # | 14 # |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 # IN THE SOFTWARE. | 21 # IN THE SOFTWARE. |
| 22 | 22 |
| 23 import boto | 23 import boto |
| 24 import boto.jsonresponse | 24 import boto.jsonresponse |
| 25 from boto.iam.summarymap import SummaryMap |
| 25 from boto.connection import AWSQueryConnection | 26 from boto.connection import AWSQueryConnection |
| 26 | 27 |
| 27 #boto.set_stream_logger('iam') | 28 #boto.set_stream_logger('iam') |
| 28 | 29 |
| 29 class IAMConnection(AWSQueryConnection): | 30 class IAMConnection(AWSQueryConnection): |
| 30 | 31 |
| 31 APIVersion = '2010-05-08' | 32 APIVersion = '2010-05-08' |
| 32 | 33 |
| 33 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, | 34 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, |
| 34 is_secure=True, port=None, proxy=None, proxy_port=None, | 35 is_secure=True, port=None, proxy=None, proxy_port=None, |
| 35 proxy_user=None, proxy_pass=None, host='iam.amazonaws.com', | 36 proxy_user=None, proxy_pass=None, host='iam.amazonaws.com', |
| 36 debug=0, https_connection_factory=None, path='/'): | 37 debug=0, https_connection_factory=None, |
| 38 path='/'): |
| 37 AWSQueryConnection.__init__(self, aws_access_key_id, | 39 AWSQueryConnection.__init__(self, aws_access_key_id, |
| 38 aws_secret_access_key, | 40 aws_secret_access_key, |
| 39 is_secure, port, proxy, | 41 is_secure, port, proxy, |
| 40 proxy_port, proxy_user, proxy_pass, | 42 proxy_port, proxy_user, proxy_pass, |
| 41 host, debug, https_connection_factory, path) | 43 host, debug, https_connection_factory, |
| 44 path) |
| 42 | 45 |
| 43 def _required_auth_capability(self): | 46 def _required_auth_capability(self): |
| 44 return ['iam'] | 47 return ['iam'] |
| 45 | 48 |
| 46 def get_response(self, action, params, path='/', parent=None, | 49 def get_response(self, action, params, path='/', parent=None, |
| 47 verb='GET', list_marker='Set'): | 50 verb='GET', list_marker='Set'): |
| 48 """ | 51 """ |
| 49 Utility method to handle calls to IAM and parsing of responses. | 52 Utility method to handle calls to IAM and parsing of responses. |
| 50 """ | 53 """ |
| 51 if not parent: | 54 if not parent: |
| 52 parent = self | 55 parent = self |
| 53 response = self.make_request(action, params, path, verb) | 56 response = self.make_request(action, params, path, verb) |
| 54 body = response.read() | 57 body = response.read() |
| 55 boto.log.debug(body) | 58 boto.log.debug(body) |
| 56 if response.status == 200: | 59 if response.status == 200: |
| 57 e = boto.jsonresponse.Element(list_marker=list_marker) | 60 e = boto.jsonresponse.Element(list_marker=list_marker, |
| 61 pythonize_name=True) |
| 58 h = boto.jsonresponse.XmlHandler(e, parent) | 62 h = boto.jsonresponse.XmlHandler(e, parent) |
| 59 h.parse(body) | 63 h.parse(body) |
| 60 return e | 64 return e |
| 61 else: | 65 else: |
| 62 boto.log.error('%s %s' % (response.status, response.reason)) | 66 boto.log.error('%s %s' % (response.status, response.reason)) |
| 63 boto.log.error('%s' % body) | 67 boto.log.error('%s' % body) |
| 64 raise self.ResponseError(response.status, response.reason, body) | 68 raise self.ResponseError(response.status, response.reason, body) |
| 65 | 69 |
| 66 # | 70 # |
| 67 # Group methods | 71 # Group methods |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 params['NewGroupName'] = new_group_name | 175 params['NewGroupName'] = new_group_name |
| 172 if new_path: | 176 if new_path: |
| 173 params['NewPath'] = new_path | 177 params['NewPath'] = new_path |
| 174 return self.get_response('UpdateGroup', params) | 178 return self.get_response('UpdateGroup', params) |
| 175 | 179 |
| 176 def add_user_to_group(self, group_name, user_name): | 180 def add_user_to_group(self, group_name, user_name): |
| 177 """ | 181 """ |
| 178 Add a user to a group | 182 Add a user to a group |
| 179 | 183 |
| 180 :type group_name: string | 184 :type group_name: string |
| 181 :param group_name: The name of the new group | 185 :param group_name: The name of the group |
| 182 | 186 |
| 183 :type user_name: string | 187 :type user_name: string |
| 184 :param user_name: The to be added to the group. | 188 :param user_name: The to be added to the group. |
| 185 | 189 |
| 186 """ | 190 """ |
| 187 params = {'GroupName' : group_name, | 191 params = {'GroupName' : group_name, |
| 188 'UserName' : user_name} | 192 'UserName' : user_name} |
| 189 return self.get_response('AddUserToGroup', params) | 193 return self.get_response('AddUserToGroup', params) |
| 190 | 194 |
| 191 def remove_user_from_group(self, group_name, user_name): | 195 def remove_user_from_group(self, group_name, user_name): |
| 192 """ | 196 """ |
| 193 Remove a user from a group. | 197 Remove a user from a group. |
| 194 | 198 |
| 195 :type group_name: string | 199 :type group_name: string |
| 196 :param group_name: The name of the new group | 200 :param group_name: The name of the group |
| 197 | 201 |
| 198 :type user_name: string | 202 :type user_name: string |
| 199 :param user_name: The user to remove from the group. | 203 :param user_name: The user to remove from the group. |
| 200 | 204 |
| 201 """ | 205 """ |
| 202 params = {'GroupName' : group_name, | 206 params = {'GroupName' : group_name, |
| 203 'UserName' : user_name} | 207 'UserName' : user_name} |
| 204 return self.get_response('RemoveUserFromGroup', params) | 208 return self.get_response('RemoveUserFromGroup', params) |
| 205 | 209 |
| 206 def put_group_policy(self, group_name, policy_name, policy_json): | 210 def put_group_policy(self, group_name, policy_name, policy_json): |
| (...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 486 | 490 |
| 487 # | 491 # |
| 488 # Access Keys | 492 # Access Keys |
| 489 # | 493 # |
| 490 | 494 |
| 491 def get_all_access_keys(self, user_name, marker=None, max_items=None): | 495 def get_all_access_keys(self, user_name, marker=None, max_items=None): |
| 492 """ | 496 """ |
| 493 Get all access keys associated with an account. | 497 Get all access keys associated with an account. |
| 494 | 498 |
| 495 :type user_name: string | 499 :type user_name: string |
| 496 :param user_name: The username of the new user | 500 :param user_name: The username of the user |
| 497 | 501 |
| 498 :type marker: string | 502 :type marker: string |
| 499 :param marker: Use this only when paginating results and only in | 503 :param marker: Use this only when paginating results and only in |
| 500 follow-up request after you've received a response | 504 follow-up request after you've received a response |
| 501 where the results are truncated. Set this to the | 505 where the results are truncated. Set this to the |
| 502 value of the Marker element in the response you | 506 value of the Marker element in the response you |
| 503 just received. | 507 just received. |
| 504 | 508 |
| 505 :type max_items: int | 509 :type max_items: int |
| 506 :param max_items: Use this only when paginating results to indicate | 510 :param max_items: Use this only when paginating results to indicate |
| (...skipping 10 matching lines...) Expand all Loading... |
| 517 | 521 |
| 518 def create_access_key(self, user_name=None): | 522 def create_access_key(self, user_name=None): |
| 519 """ | 523 """ |
| 520 Create a new AWS Secret Access Key and corresponding AWS Access Key ID | 524 Create a new AWS Secret Access Key and corresponding AWS Access Key ID |
| 521 for the specified user. The default status for new keys is Active | 525 for the specified user. The default status for new keys is Active |
| 522 | 526 |
| 523 If the user_name is not specified, the user_name is determined | 527 If the user_name is not specified, the user_name is determined |
| 524 implicitly based on the AWS Access Key ID used to sign the request. | 528 implicitly based on the AWS Access Key ID used to sign the request. |
| 525 | 529 |
| 526 :type user_name: string | 530 :type user_name: string |
| 527 :param user_name: The username of the new user | 531 :param user_name: The username of the user |
| 528 | 532 |
| 529 """ | 533 """ |
| 530 params = {'UserName' : user_name} | 534 params = {'UserName' : user_name} |
| 531 return self.get_response('CreateAccessKey', params) | 535 return self.get_response('CreateAccessKey', params) |
| 532 | 536 |
| 533 def update_access_key(self, access_key_id, status, user_name=None): | 537 def update_access_key(self, access_key_id, status, user_name=None): |
| 534 """ | 538 """ |
| 535 Changes the status of the specified access key from Active to Inactive | 539 Changes the status of the specified access key from Active to Inactive |
| 536 or vice versa. This action can be used to disable a user's key as | 540 or vice versa. This action can be used to disable a user's key as |
| 537 part of a key rotation workflow. | 541 part of a key rotation workflow. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 559 """ | 563 """ |
| 560 Delete an access key associated with a user. | 564 Delete an access key associated with a user. |
| 561 | 565 |
| 562 If the user_name is not specified, it is determined implicitly based | 566 If the user_name is not specified, it is determined implicitly based |
| 563 on the AWS Access Key ID used to sign the request. | 567 on the AWS Access Key ID used to sign the request. |
| 564 | 568 |
| 565 :type access_key_id: string | 569 :type access_key_id: string |
| 566 :param access_key_id: The ID of the access key to be deleted. | 570 :param access_key_id: The ID of the access key to be deleted. |
| 567 | 571 |
| 568 :type user_name: string | 572 :type user_name: string |
| 569 :param user_name: The username of the new user | 573 :param user_name: The username of the user |
| 570 | 574 |
| 571 """ | 575 """ |
| 572 params = {'AccessKeyId' : access_key_id} | 576 params = {'AccessKeyId' : access_key_id} |
| 573 if user_name: | 577 if user_name: |
| 574 params['UserName'] = user_name | 578 params['UserName'] = user_name |
| 575 return self.get_response('DeleteAccessKey', params) | 579 return self.get_response('DeleteAccessKey', params) |
| 576 | 580 |
| 577 # | 581 # |
| 578 # Signing Certificates | 582 # Signing Certificates |
| 579 # | 583 # |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 Uploads an X.509 signing certificate and associates it with | 644 Uploads an X.509 signing certificate and associates it with |
| 641 the specified user. | 645 the specified user. |
| 642 | 646 |
| 643 If the user_name is not specified, it is determined implicitly based | 647 If the user_name is not specified, it is determined implicitly based |
| 644 on the AWS Access Key ID used to sign the request. | 648 on the AWS Access Key ID used to sign the request. |
| 645 | 649 |
| 646 :type cert_body: string | 650 :type cert_body: string |
| 647 :param cert_body: The body of the signing certificate. | 651 :param cert_body: The body of the signing certificate. |
| 648 | 652 |
| 649 :type user_name: string | 653 :type user_name: string |
| 650 :param user_name: The username of the new user | 654 :param user_name: The username of the user |
| 651 | 655 |
| 652 """ | 656 """ |
| 653 params = {'CertificateBody' : cert_body} | 657 params = {'CertificateBody' : cert_body} |
| 654 if user_name: | 658 if user_name: |
| 655 params['UserName'] = user_name | 659 params['UserName'] = user_name |
| 656 return self.get_response('UploadSigningCertificate', params, | 660 return self.get_response('UploadSigningCertificate', params, |
| 657 verb='POST') | 661 verb='POST') |
| 658 | 662 |
| 659 def delete_signing_cert(self, cert_id, user_name=None): | 663 def delete_signing_cert(self, cert_id, user_name=None): |
| 660 """ | 664 """ |
| 661 Delete a signing certificate associated with a user. | 665 Delete a signing certificate associated with a user. |
| 662 | 666 |
| 663 If the user_name is not specified, it is determined implicitly based | 667 If the user_name is not specified, it is determined implicitly based |
| 664 on the AWS Access Key ID used to sign the request. | 668 on the AWS Access Key ID used to sign the request. |
| 665 | 669 |
| 666 :type user_name: string | 670 :type user_name: string |
| 667 :param user_name: The username of the new user | 671 :param user_name: The username of the user |
| 668 | 672 |
| 669 :type cert_id: string | 673 :type cert_id: string |
| 670 :param cert_id: The ID of the certificate. | 674 :param cert_id: The ID of the certificate. |
| 671 | 675 |
| 672 """ | 676 """ |
| 673 params = {'CertificateId' : cert_id} | 677 params = {'CertificateId' : cert_id} |
| 674 if user_name: | 678 if user_name: |
| 675 params['UserName'] = user_name | 679 params['UserName'] = user_name |
| 676 return self.get_response('DeleteSigningCertificate', params) | 680 return self.get_response('DeleteSigningCertificate', params) |
| 677 | 681 |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 899 """ | 903 """ |
| 900 params = {'UserName' : user_name, | 904 params = {'UserName' : user_name, |
| 901 'SerialNumber' : serial_number, | 905 'SerialNumber' : serial_number, |
| 902 'AuthenticationCode1' : auth_code_1, | 906 'AuthenticationCode1' : auth_code_1, |
| 903 'AuthenticationCode2' : auth_code_2} | 907 'AuthenticationCode2' : auth_code_2} |
| 904 return self.get_response('ResyncMFADevice', params) | 908 return self.get_response('ResyncMFADevice', params) |
| 905 | 909 |
| 906 # | 910 # |
| 907 # Login Profiles | 911 # Login Profiles |
| 908 # | 912 # |
| 913 |
| 914 def get_login_profiles(self, user_name): |
| 915 """ |
| 916 Retrieves the login profile for the specified user. |
| 917 |
| 918 :type user_name: string |
| 919 :param user_name: The username of the user |
| 920 |
| 921 """ |
| 922 params = {'UserName' : user_name} |
| 923 return self.get_response('GetLoginProfile', params) |
| 909 | 924 |
| 910 def create_login_profile(self, user_name, password): | 925 def create_login_profile(self, user_name, password): |
| 911 """ | 926 """ |
| 912 Creates a login profile for the specified user, give the user the | 927 Creates a login profile for the specified user, give the user the |
| 913 ability to access AWS services and the AWS Management Console. | 928 ability to access AWS services and the AWS Management Console. |
| 914 | 929 |
| 915 :type user_name: string | 930 :type user_name: string |
| 916 :param user_name: The name of the new user | 931 :param user_name: The name of the user |
| 917 | 932 |
| 918 :type password: string | 933 :type password: string |
| 919 :param password: The new password for the user | 934 :param password: The new password for the user |
| 920 | 935 |
| 921 """ | 936 """ |
| 922 params = {'UserName' : user_name, | 937 params = {'UserName' : user_name, |
| 923 'Password' : password} | 938 'Password' : password} |
| 924 return self.get_response('CreateLoginProfile', params) | 939 return self.get_response('CreateLoginProfile', params) |
| 925 | 940 |
| 926 def delete_login_profile(self, user_name): | 941 def delete_login_profile(self, user_name): |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 978 def get_account_alias(self): | 993 def get_account_alias(self): |
| 979 """ | 994 """ |
| 980 Get the alias for the current account. | 995 Get the alias for the current account. |
| 981 | 996 |
| 982 This is referred to in the docs as list_account_aliases, | 997 This is referred to in the docs as list_account_aliases, |
| 983 but it seems you can only have one account alias currently. | 998 but it seems you can only have one account alias currently. |
| 984 | 999 |
| 985 For more information on account id aliases, please see | 1000 For more information on account id aliases, please see |
| 986 http://goo.gl/ToB7G | 1001 http://goo.gl/ToB7G |
| 987 """ | 1002 """ |
| 988 r = self.get_response('ListAccountAliases', {}) | 1003 return self.get_response('ListAccountAliases', {}, |
| 989 response = r.get('ListAccountAliasesResponse') | 1004 list_marker='AccountAliases') |
| 990 result = response.get('ListAccountAliasesResult') | |
| 991 aliases = result.get('AccountAliases') | |
| 992 return aliases.get('member', None) | |
| 993 | 1005 |
| 994 def get_signin_url(self, service='ec2'): | 1006 def get_signin_url(self, service='ec2'): |
| 995 """ | 1007 """ |
| 996 Get the URL where IAM users can use their login profile to sign in | 1008 Get the URL where IAM users can use their login profile to sign in |
| 997 to this account's console. | 1009 to this account's console. |
| 998 | 1010 |
| 999 :type service: string | 1011 :type service: string |
| 1000 :param service: Default service to go to in the console. | 1012 :param service: Default service to go to in the console. |
| 1001 """ | 1013 """ |
| 1002 alias = self.get_account_alias() | 1014 alias = self.get_account_alias() |
| 1003 if not alias: | 1015 if not alias: |
| 1004 raise Exception('No alias associated with this account. Please use
iam.create_account_alias() first.') | 1016 raise Exception('No alias associated with this account. Please use
iam.create_account_alias() first.') |
| 1005 | 1017 |
| 1006 return "https://%s.signin.aws.amazon.com/console/%s" % (alias, service) | 1018 return "https://%s.signin.aws.amazon.com/console/%s" % (alias, service) |
| 1019 |
| 1020 def get_account_summary(self): |
| 1021 """ |
| 1022 Get the alias for the current account. |
| 1023 |
| 1024 This is referred to in the docs as list_account_aliases, |
| 1025 but it seems you can only have one account alias currently. |
| 1026 |
| 1027 For more information on account id aliases, please see |
| 1028 http://goo.gl/ToB7G |
| 1029 """ |
| 1030 return self.get_object('GetAccountSummary', {}, SummaryMap) |
| 1031 |
| 1032 |
| OLD | NEW |