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 |