class ListSigningCertificates(IAMRequest): DESCRIPTION = "List a user's signing certificates" ARGS = [ arg_user(nargs='?', help='''user to list certificates for (default: current user)'''), Arg('-v', '--verbose', action='store_true', route_to=None, help="also show certificates' contents"), AS_ACCOUNT ] LIST_TAGS = ['Certificates'] def main(self): return PaginatedResponse(self, (None, ), ('Certificates', )) def prepare_for_page(self, page): # Pages are defined by markers self.params['Marker'] = page def get_next_page(self, response): if response.get('IsTruncatated') == 'true': return response['Marker'] def print_result(self, result): for cert in result.get('Certificates', []): print cert['CertificateId'] if self.args['verbose']: print cert['CertificateBody'] print cert['Status'] print cert.get('UserName') or ''
class GetUserPolicy(IAMRequest): DESCRIPTION = "Display a user's policy" ARGS = [ arg_user(help='user the poilcy is attached to (required)'), Arg('-p', '--policy-name', dest='PolicyName', metavar='POLICY', required=True, help='name of the policy to show (required)'), Arg('--pretty-print', action='store_true', route_to=None, help='reformat the policy for easier reading'), AS_ACCOUNT ] def print_result(self, result): policy_content = urllib.unquote(result['PolicyDocument']) if self.args['pretty_print']: try: policy_json = json.loads(policy_content) except ValueError: self.log.debug('JSON parse error', exc_info=True) raise ValueError( "policy '{0}' does not appear to be valid JSON".format( self.args['PolicyName'])) policy_content = json.dumps(policy_json, indent=4) print policy_content
class ListAccessKeys(IAMRequest): DESCRIPTION = "List a user's access keys" ARGS = [arg_user(nargs='?', help='user to list keys for (default: current user)'), AS_ACCOUNT] LIST_TAGS = ['AccessKeyMetadata'] def main(self): return PaginatedResponse(self, (None,), ('AccessKeyMetadata',)) def prepare_for_page(self, page): # Pages are defined by markers self.params['Marker'] = page # pylint: disable=no-self-use def get_next_page(self, response): if response.get('IsTruncated') == 'true': return response['Marker'] # pylint: enable=no-self-use # pylint: disable=no-self-use def print_result(self, result): for accesskey in result.get('AccessKeyMetadata', []): print accesskey.get('AccessKeyId') print accesskey.get('Status')
class ListMFADevices(IAMRequest): DESCRIPTION = "List a user's MFA devices" ARGS = [ arg_user(nargs='?', help='''user to list MFA devices for (default: current user)'''), AS_ACCOUNT ] LIST_TAGS = ['MFADevices'] def main(self): return PaginatedResponse(self, (None, ), ('MFADevices', )) def prepare_for_page(self, page): # Pages are defined by markers self.params['Marker'] = page # pylint: disable=no-self-use def get_next_page(self, response): if response.get('IsTruncated') == 'true': return response['Marker'] # pylint: enable=no-self-use # pylint: disable=no-self-use def print_result(self, result): for device in result.get('MFADevices', []): print device['SerialNumber']
class ListGroupsForUser(IAMRequest): DESCRIPTION = 'List all groups a user is a member of' ARGS = [ arg_user(help='user to list membership for (required)'), AS_ACCOUNT ] LIST_TAGS = ['Groups'] def main(self): return PaginatedResponse(self, (None, ), ('Groups', )) def prepare_for_page(self, page): # Pages are defined by markers self.params['Marker'] = page # pylint: disable=no-self-use def get_next_page(self, response): if response.get('IsTruncated') == 'true': return response['Marker'] # pylint: enable=no-self-use # pylint: disable=no-self-use def print_result(self, result): for group in result.get('Groups', []): print group['Arn']
class CreateSigningCertificate(IAMRequest): DESCRIPTION = '[Eucalyptus only] Create a new signing certificate' ARGS = [ arg_user(nargs='?', help='''user to create the signing certificate for (default: current user)'''), Arg('--out', metavar='FILE', route_to=None, help='file to write the certificate to (default: stdout)'), Arg('--keyout', metavar='FILE', route_to=None, help='file to write the private key to (default: stdout)'), AS_ACCOUNT ] def postprocess(self, result): if self.args['out']: with open(self.args['out'], 'w') as certfile: certfile.write(result['Certificate']['CertificateBody']) if self.args['keyout']: old_umask = os.umask(0o077) with open(self.args['keyout'], 'w') as keyfile: keyfile.write(result['Certificate']['PrivateKey']) os.umask(old_umask) def print_result(self, result): print result['Certificate']['CertificateId'] if not self.args['out']: print result['Certificate']['CertificateBody'] if not self.args['keyout']: print result['Certificate']['PrivateKey']
class AddUserPolicy(IAMRequest): DESCRIPTION = ('Add a new policy to a user. To add more complex policies ' 'than this tool supports, see euare-useruploadpolicy(1).') ARGS = [arg_user(help='user to attach the policy to (required)'), Arg('-p', '--policy-name', metavar='POLICY', required=True, help='name of the new policy (required)'), Arg('-e', '--effect', choices=('Allow', 'Deny'), required=True, help='whether the new policy should Allow or Deny (required)'), Arg('-a', '--action', dest='actions', action='append', required=True, help='''action(s) the policy should apply to (at least one required)'''), Arg('-r', '--resource', dest='resources', action='append', required=True, help='''resource(s) the policy should apply to (at least one required)'''), Arg('-o', '--output', action='store_true', help='display the newly-created policy'), AS_ACCOUNT] def main(self): policy = build_iam_policy(self.args['effect'], self.args['resources'], self.args['actions']) policy_doc = json.dumps(policy) req = PutUserPolicy.from_other( self, UserName=self.args['UserName'], PolicyName=self.args['policy_name'], PolicyDocument=policy_doc, DelegateAccount=self.params['DelegateAccount']) response = req.main() response['PolicyDocument'] = policy_doc return response def print_result(self, result): if self.args['output']: print result['PolicyDocument']
class DeactivateMFADevice(IAMRequest): DESCRIPTION = 'Deactivate an MFA device' ARGS = [arg_user( help='user owning the MFA device to deactivate (required)'), Arg('-s', '--serial-number', dest='SerialNumber', metavar='SERIAL', required=True, help='''serial number of the MFA device to deactivate (required)'''), AS_ACCOUNT]
class DeleteUserPolicy(IAMRequest): DESCRIPTION = 'Remove a policy from a user' ARGS = [ arg_user(help='user the policy is attached to (required)'), Arg('-p', '--policy-name', dest='PolicyName', metavar='POLICY', required=True, help='name of the policy to delete (required)'), AS_ACCOUNT ]
class GetUserInfo(IAMRequest, TabifyingMixin): DESCRIPTION = '[Eucalyptus only] Display information about a user' ARGS = [arg_user(nargs='?', help='''name of the user to describe (default: current user)'''), Arg('-k', '--info-key', dest='InfoKey', help='name of the piece of user info to show'), AS_ACCOUNT] LIST_TAGS = ['Infos'] def print_result(self, result): for info in result.get('Infos', []): print self.tabify((info.get('Key'), info.get('Value')))
class ResyncMFADevice(IAMRequest): DESCRIPTION = 'Re-synchronize an MFA device with the server' ARGS = [arg_user(help='''user owning the MFA device to re-synchronize (required)'''), Arg('-s', '--serial-number', dest='SerialNumber', metavar='SERIAL', required=True, help='serial number of the MFA device (required)'), Arg('-c1', dest='AuthenticationCode1', metavar='CODE', required=True, help='''an authentication code emitted by the MFA device (required)'''), Arg('-c2', dest='AuthenticationCode2', metavar='CODE', required=True, help='''a subsequent authentication code emitted by the MFA device (required)'''), AS_ACCOUNT]
class UpdateLoginProfile(IAMRequest): DESCRIPTION = "Update a user's password" ARGS = [ arg_user(help='name of the user to change a password for (required)'), Arg('-p', '--password', dest='Password', help='''the new password. If unspecified, the new password will be read from the console.'''), AS_ACCOUNT ] def configure(self): IAMRequest.configure(self) if self.args['Password'] is None: self.log.info('no password supplied; prompting') self.params['Password'] = prompt_for_password()
class UpdateUserInfo(IAMRequest): DESCRIPTION = "[Eucalyptus only] Update a user's information" ARGS = [ arg_user(nargs='?', help='user to update (default: current user)'), Arg('-k', '--info-key', dest='InfoKey', metavar='KEY', required=True, help='name of the info field to set (required)'), Arg('-i', '--info-value', dest='InfoValue', metavar='VALUE', help='value to set the info field to (omit to delete it)'), AS_ACCOUNT ]
class UploadSigningCertificate(IAMRequest): DESCRIPTION = 'Upload a signing certificate' ARGS = [arg_user(help='''user the signing certificate is for (default: current user)'''), MutuallyExclusiveArgList( Arg('-c', '--certificate-body', dest='CertificateBody', metavar='CERT_CONTENT', help='PEM-encoded contents of the new certificate'), Arg('-f', '--certificate-file', dest='CertificateBody', metavar='FILE', type=open, help='file containing the new certificate')) .required(), AS_ACCOUNT] # pylint: disable=no-self-use def print_result(self, result): print result.get('Certificate', {}).get('CertificateId')
class UpdateUser(IAMRequest): DESCRIPTION = 'Change the name and/or path of a user' ARGS = [arg_user(help='name of the user to update'), Arg('-n', '--new-user-name', dest='NewUserName', metavar='USER', help='new name for the user'), Arg('-p', '--new-path', dest='NewPath', metavar='PATH', help='new path for the user'), Arg('--enabled', dest='Enabled', choices=('true', 'false'), help='''[Eucalyptus only] 'true' to enable the user, or 'false' to disable the user'''), Arg('--pwd-expires', dest='PasswordExpiration', metavar='YYYY-MM-DDThh:mm:ssZ', help='''[Eucalyptus only] New password expiration date, in ISO8601 format'''), Arg('--reg-status', dest='RegStatus', choices=('REGISTERED', 'APPROVED', 'CONFIRMED'), help='''[Eucalyptus < 4.2 only] new registration status. Only CONFIRMED users may access the system.'''), AS_ACCOUNT]
class GetUser(IAMRequest): DESCRIPTION = "Display a user's ARN and GUID" ARGS = [ arg_user(nargs='?', help='''name of the user to describe (default: current user)'''), Arg('--show-extra', dest='ShowExtra', action='store_const', const='true', help='also display additional user info'), AS_ACCOUNT ] def print_result(self, result): print result['User']['Arn'] print result['User']['UserId'] if self.args['ShowExtra'] == 'true': for attr in ('CreateDate', 'Enabled', 'RegStatus', 'PasswordExpiration'): print result['User'].get(attr, '')
class GetLoginProfile(IAMRequest): DESCRIPTION = 'Verify that a user has a password' ARGS = [ arg_user(help='user owning the password to check (required)'), Arg('--verbose', action='store_true', route_to=None, help="print extra info about the user's password"), AS_ACCOUNT ] def print_result(self, result): # If we've managed to get to this point, we already know the user has # a login profile. user_name = result['LoginProfile'].get('UserName') print 'Login Profile Exists for User', user_name if self.args['verbose']: create_date = result['LoginProfile'].get('CreateDate') if create_date: print 'Creation date:', create_date must_change = result['LoginProfile'].get('MustChangePassword') if must_change: print 'Must change password:', must_change
class PutUserPolicy(IAMRequest): DESCRIPTION = 'Attach a policy to a user' ARGS = [ arg_user(help='user to attach the policy to (required)'), Arg('-p', '--policy-name', dest='PolicyName', metavar='POLICY', required=True, help='name of the policy (required)'), MutuallyExclusiveArgList( Arg('-o', '--policy-content', dest='PolicyDocument', metavar='POLICY_CONTENT', help='the policy to attach'), Arg('-f', '--policy-document', dest='PolicyDocument', metavar='FILE', type=open, help='file containing the policy to attach')).required(), AS_ACCOUNT ]
class EnableMFADevice(IAMRequest): DESCRIPTION = 'Enable an MFA device' ARGS = [ arg_user(help='user to enable the MFA device for (required)'), Arg('-s', '--serial-number', dest='SerialNumber', metavar='SERIAL', required=True, help='serial number of the MFA device to activate (required)'), Arg('-c1', dest='AuthenticationCode1', metavar='CODE', required=True, help='''an authentication code emitted by the MFA device (required)'''), Arg('-c2', dest='AuthenticationCode2', metavar='CODE', required=True, help='''a subsequent authentication code emitted by the MFA device (required)'''), AS_ACCOUNT ]
class CreateAccessKey(IAMRequest): DESCRIPTION = 'Create a new access key for a user' ARGS = [ arg_user(help='''user the new key will belong to (default: current user)'''), Arg('-w', '--write-config', action='store_true', route_to=None, help='''output access keys and region information in the form of a euca2ools.ini(5) configuration file instead of by themselves'''), Arg('-d', '--domain', route_to=None, help='''the DNS domain to use for region information in configuration file output (default: based on IAM URL)'''), Arg('-l', '--set-default-user', action='store_true', route_to=None, help='''set this user as the default user for the region in euca2ools.ini(5) configuration file output. This option is only useful when used with -w.'''), AS_ACCOUNT ] def postprocess(self, result): if self.args.get('write_config'): parsed = six.moves.urllib.parse.urlparse(self.service.endpoint) if not self.args.get('domain'): dnsname = parsed.netloc.split(':')[0] if all(label.isdigit() for label in dnsname.split('.')): msg = ('warning: IAM URL {0} refers to a specific IP; ' 'for a complete configuration file supply ' 'the region\'s DNS domain with -d/--domain'.format( self.service.endpoint)) print >> sys.stderr, msg else: self.args['domain'] = parsed.netloc.split('.', 1)[1] configfile = six.moves.configparser.SafeConfigParser() if self.args.get('domain'): if ':' not in self.args['domain'] and ':' in parsed.netloc: # Add the port self.args['domain'] += ':' + parsed.netloc.split(':')[1] # This uses self.config.region instead of # self.service.region_name because the latter is a global # service in AWS and thus frequently deferred with "use" # statements. That may eventually happen in eucalyptus # cloud federations as well. # # At some point an option that lets one choose a region # name at the command line may be useful, but until # someone asks for it let's not clutter it up for now. region_name = self.config.region or self.args['domain'] region_section = 'region {0}'.format(region_name.split(':')[0]) configfile.add_section(region_section) for service in sorted(euca2ools.util.generate_service_names()): url = '{scheme}://{service}.{domain}/'.format( scheme=parsed.scheme, domain=self.args['domain'], service=service) configfile.set(region_section, '{0}-url'.format(service), url) user_name = result['AccessKey'].get('UserName') or 'root' account_id = self.get_user_account_id() if account_id: user_name = '{0}:{1}'.format(account_id, user_name) user_section = 'user {0}'.format(user_name) configfile.add_section(user_section) configfile.set(user_section, 'key-id', result['AccessKey']['AccessKeyId']) configfile.set(user_section, 'secret-key', result['AccessKey']['SecretAccessKey']) if account_id: configfile.set(user_section, 'account-id', account_id) if self.args.get('set_default_user'): configfile.set(region_section, 'user', user_name) result['configfile'] = configfile def print_result(self, result): if self.args.get('write_config'): result['configfile'].write(sys.stdout) else: print result['AccessKey']['AccessKeyId'] print result['AccessKey']['SecretAccessKey'] def get_user_account_id(self): req = GetUser.from_other( self, UserName=self.params['UserName'], DelegateAccount=self.params.get('DelegateAccount')) try: response = req.main() except euca2ools.exceptions.AWSError as err: if err.status_code == 403: msg = ('warning: unable to retrieve account ID ({0})'.format( err.message)) print >> sys.stderr, msg return None raise arn = response['User']['Arn'] return arn.split(':')[4]
class CreateUser(IAMRequest): DESCRIPTION = 'Create a new user' ARGS = [ arg_user(help='name of the new user (required)'), Arg('-p', '--path', dest='Path', help='path for the new user (default: "/")'), Arg('-g', '--group-name', route_to=None, help='also add the new user to a group'), Arg('--verify', action='store_true', route_to=None, help='''ensure the group given with -g exists before doing anything'''), Arg('-k', '--create-accesskey', action='store_true', route_to=None, help='also create an access key for the new user and show it'), MutuallyExclusiveArgList( Arg('-v', '--verbose', action='store_true', route_to=None, help="show the new user's ARN and GUID"), Arg('-w', '--write-config', action='store_true', route_to=None, help='''output access keys and region information in the form of a euca2ools.ini(5) configuration file instead of by themselves (implies -k)''')), Arg('-d', '--domain', route_to=None, help='''the DNS domain to use for region information in configuration file output (default: based on IAM URL)'''), AS_ACCOUNT ] def preprocess(self): if self.args.get('verify') and self.args.get('group_name'): obj = GetGroup.from_other( self, GroupName=self.args['group_name'], DelegateAccount=self.params['DelegateAccount']) # This will blow up if the group does not exist. obj.main() def postprocess(self, result): if self.args.get('group_name'): obj = AddUserToGroup.from_other( self, UserName=self.args['UserName'], GroupName=self.args['group_name'], DelegateAccount=self.params['DelegateAccount']) obj.main() if self.args.get('create_accesskey') or self.args.get('write_config'): obj = CreateAccessKey.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount'], write_config=self.args.get('write_config'), domain=self.args.get('domain')) key_result = obj.main() result.update(key_result) def print_result(self, result): if self.args.get('write_config'): result['configfile'].write(sys.stdout) else: if self.args['verbose']: print result['User']['Arn'] print result['User']['UserId'] if 'AccessKey' in result: print result['AccessKey']['AccessKeyId'] print result['AccessKey']['SecretAccessKey']
class DeleteUser(IAMRequest): DESCRIPTION = 'Delete a user' ARGS = [ arg_user(help='name of the user to delete (required)'), Arg('-r', '--recursive', action='store_true', route_to=None, help='''remove all IAM resources associated with the user first'''), Arg('-R', '--recursive-euca', dest='IsRecursive', action='store_const', const='true', help=argparse.SUPPRESS), Arg('-p', '--pretend', action='store_true', route_to=None, help='''list the resources that would be deleted instead of actually deleting them. Implies -r.'''), AS_ACCOUNT ] def main(self): if self.args['recursive'] or self.args['pretend']: # Figure out what we'd have to delete req = ListAccessKeys.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) keys = req.main().get('AccessKeyMetadata', []) req = ListUserPolicies.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) policies = req.main().get('PolicyNames', []) req = ListSigningCertificates.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) certs = req.main().get('Certificates', []) req = ListGroupsForUser.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) groups = req.main().get('Groups', []) req = GetLoginProfile.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) try: # This will raise an exception if no login profile is found. req.main() has_login_profile = True except AWSError as err: if err.code == 'NoSuchEntity': # It doesn't exist has_login_profile = False else: # Something else went wrong; not our problem raise else: # Just in case keys = [] policies = [] certs = [] groups = [] has_login_profile = False if self.args['pretend']: return { 'keys': keys, 'policies': policies, 'certificates': certs, 'groups': groups, 'has_login_profile': has_login_profile } else: if self.args['recursive']: for key in keys: req = DeleteAccessKey.from_other( self, UserName=self.args['UserName'], AccessKeyId=key['AccessKeyId'], DelegateAccount=self.params['DelegateAccount']) req.main() for policy in policies: req = DeleteUserPolicy.from_other( self, UserName=self.args['UserName'], PolicyName=policy, DelegateAccount=self.params['DelegateAccount']) req.main() for cert in certs: req = DeleteSigningCertificate.from_other( self, UserName=self.args['UserName'], CertificateId=cert['CertificateId'], DelegateAccount=self.params['DelegateAccount']) req.main() for group in groups: req = RemoveUserFromGroup.from_other( self, user_names=[self.args['UserName']], GroupName=group['GroupName'], DelegateAccount=self.params['DelegateAccount']) req.main() if has_login_profile: req = DeleteLoginProfile.from_other( self, UserName=self.args['UserName'], DelegateAccount=self.params['DelegateAccount']) req.main() return self.send() def print_result(self, result): if self.args['pretend']: print 'accesskeys' for key in result['keys']: print '\t' + key['AccessKeyId'] print 'policies' for policy in result['policies']: print '\t' + policy print 'certificates' for cert in result['certificates']: print '\t' + cert['CertificateId'] print 'groups' for group in result['groups']: print '\t' + group['Arn']
class DeleteLoginProfile(IAMRequest): DESCRIPTION = "Delete a user's password" ARGS = [arg_user(help='''name of the user whose password should be deleted (required)'''), AS_ACCOUNT]
class ListUserPolicies(IAMRequest): DESCRIPTION = 'List one or all policies attached to a user' ARGS = [ arg_user(help='user owning the policies to list (required)'), Arg('-p', '--policy-name', metavar='POLICY', route_to=None, help='display a specific policy'), Arg('-v', '--verbose', action='store_true', route_to=None, help='''display the contents of the resulting policies (in addition to their names)'''), Arg('--pretty-print', action='store_true', route_to=None, help='''when printing the contents of policies, reformat them for easier reading'''), AS_ACCOUNT ] LIST_TAGS = ['PolicyNames'] def main(self): return PaginatedResponse(self, (None, ), ('PolicyNames', )) def prepare_for_page(self, page): # Pages are defined by markers self.params['Marker'] = page # pylint: disable=no-self-use def get_next_page(self, response): if response.get('IsTruncated') == 'true': return response['Marker'] # pylint: enable=no-self-use def print_result(self, result): if self.args.get('policy_name'): # Look for the specific policy the user asked for for policy_name in result.get('PolicyNames', []): if policy_name == self.args['policy_name']: if self.args['verbose']: self.print_policy(policy_name) else: print policy_name break else: for policy_name in result.get('PolicyNames', []): print policy_name if self.args['verbose']: self.print_policy(policy_name) # We already take care of pagination print 'IsTruncated: false' def print_policy(self, policy_name): req = GetUserPolicy.from_other( self, UserName=self.args['UserName'], PolicyName=policy_name, pretty_print=self.args['pretty_print'], DelegateAccount=self.params.get('DelegateAccount')) response = req.main() req.print_result(response)