Merge branch 'master' into jsondiff-1.1.2
This commit is contained in:
		
						commit
						0c5010989a
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -15,4 +15,4 @@ python_env | ||||
| .ropeproject/ | ||||
| .pytest_cache/ | ||||
| venv/ | ||||
| 
 | ||||
| .vscode/ | ||||
|  | ||||
| @ -23,6 +23,8 @@ matrix: | ||||
|       sudo: true | ||||
| before_install: | ||||
|   - export BOTO_CONFIG=/dev/null | ||||
|   - export AWS_SECRET_ACCESS_KEY=foobar_secret | ||||
|   - export AWS_ACCESS_KEY_ID=foobar_key | ||||
| install: | ||||
|   # We build moto first so the docker container doesn't try to compile it as well, also note we don't use | ||||
|   # -d for docker run so the logs show up in travis | ||||
| @ -32,8 +34,6 @@ install: | ||||
| 
 | ||||
|     if [ "$TEST_SERVER_MODE" = "true" ]; then | ||||
|       docker run --rm -t --name motoserver -e TEST_SERVER_MODE=true -e AWS_SECRET_ACCESS_KEY=server_secret -e AWS_ACCESS_KEY_ID=server_key -v `pwd`:/moto -p 5000:5000 -v /var/run/docker.sock:/var/run/docker.sock python:${TRAVIS_PYTHON_VERSION}-stretch /moto/travis_moto_server.sh & | ||||
|       export AWS_SECRET_ACCESS_KEY=foobar_secret | ||||
|       export AWS_ACCESS_KEY_ID=foobar_key | ||||
|     fi | ||||
|     travis_retry pip install boto==2.45.0 | ||||
|     travis_retry pip install boto3 | ||||
|  | ||||
| @ -1,6 +1,11 @@ | ||||
| Moto Changelog | ||||
| =================== | ||||
| 
 | ||||
| 1.3.7 | ||||
| ----- | ||||
| 
 | ||||
|     * Switch from mocking requests to using before-send for AWS calls | ||||
| 
 | ||||
| 1.3.6 | ||||
| ----- | ||||
| 
 | ||||
|  | ||||
| @ -259,7 +259,7 @@ It uses flask, which isn't a default dependency. You can install the | ||||
| server 'extra' package with: | ||||
| 
 | ||||
| ```python | ||||
| pip install moto[server] | ||||
| pip install "moto[server]" | ||||
| ``` | ||||
| 
 | ||||
| You can then start it running a service: | ||||
|  | ||||
| @ -3,7 +3,7 @@ import logging | ||||
| # logging.getLogger('boto').setLevel(logging.CRITICAL) | ||||
| 
 | ||||
| __title__ = 'moto' | ||||
| __version__ = '1.3.6' | ||||
| __version__ = '1.3.7' | ||||
| 
 | ||||
| from .acm import mock_acm  # flake8: noqa | ||||
| from .apigateway import mock_apigateway, mock_apigateway_deprecated  # flake8: noqa | ||||
|  | ||||
| @ -10,6 +10,7 @@ from boto3.session import Session | ||||
| import responses | ||||
| from moto.core import BaseBackend, BaseModel | ||||
| from .utils import create_id | ||||
| from moto.core.utils import path_url | ||||
| from .exceptions import StageNotFoundException, ApiKeyNotFoundException | ||||
| 
 | ||||
| STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}" | ||||
| @ -372,7 +373,8 @@ class RestAPI(BaseModel): | ||||
|         # TODO deal with no matching resource | ||||
| 
 | ||||
|     def resource_callback(self, request): | ||||
|         path_after_stage_name = '/'.join(request.path_url.split("/")[2:]) | ||||
|         path = path_url(request.url) | ||||
|         path_after_stage_name = '/'.join(path.split("/")[2:]) | ||||
|         if not path_after_stage_name: | ||||
|             path_after_stage_name = '/' | ||||
| 
 | ||||
|  | ||||
| @ -508,6 +508,15 @@ DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE = """<DescribeAutoScalingGroupsResponse xml | ||||
|         {% else %} | ||||
|           <LoadBalancerNames/> | ||||
|         {% endif %} | ||||
|         {% if group.target_group_arns %} | ||||
|           <TargetGroupARNs> | ||||
|           {% for target_group_arn in group.target_group_arns %} | ||||
|             <member>{{ target_group_arn }}</member> | ||||
|           {% endfor %} | ||||
|           </TargetGroupARNs> | ||||
|         {% else %} | ||||
|           <TargetGroupARNs/> | ||||
|         {% endif %} | ||||
|         <MinSize>{{ group.min_size }}</MinSize> | ||||
|         {% if group.vpc_zone_identifier %} | ||||
|           <VPCZoneIdentifier>{{ group.vpc_zone_identifier }}</VPCZoneIdentifier> | ||||
|  | ||||
| @ -7,7 +7,7 @@ try: | ||||
| except ImportError: | ||||
|     from urllib.parse import unquote | ||||
| 
 | ||||
| from moto.core.utils import amz_crc32, amzn_request_id | ||||
| from moto.core.utils import amz_crc32, amzn_request_id, path_url | ||||
| from moto.core.responses import BaseResponse | ||||
| from .models import lambda_backends | ||||
| 
 | ||||
| @ -94,7 +94,7 @@ class LambdaResponse(BaseResponse): | ||||
|             return self._add_policy(request, full_url, headers) | ||||
| 
 | ||||
|     def _add_policy(self, request, full_url, headers): | ||||
|         path = request.path if hasattr(request, 'path') else request.path_url | ||||
|         path = request.path if hasattr(request, 'path') else path_url(request.url) | ||||
|         function_name = path.split('/')[-2] | ||||
|         if self.lambda_backend.get_function(function_name): | ||||
|             policy = request.body.decode('utf8') | ||||
| @ -104,7 +104,7 @@ class LambdaResponse(BaseResponse): | ||||
|             return 404, {}, "{}" | ||||
| 
 | ||||
|     def _get_policy(self, request, full_url, headers): | ||||
|         path = request.path if hasattr(request, 'path') else request.path_url | ||||
|         path = request.path if hasattr(request, 'path') else path_url(request.url) | ||||
|         function_name = path.split('/')[-2] | ||||
|         if self.lambda_backend.get_function(function_name): | ||||
|             lambda_function = self.lambda_backend.get_function(function_name) | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| import datetime | ||||
| import functools | ||||
| import itertools | ||||
| import json | ||||
| import os | ||||
| import time | ||||
| @ -20,6 +22,39 @@ UserStatus = { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| def paginate(limit, start_arg="next_token", limit_arg="max_results"): | ||||
|     """Returns a limited result list, and an offset into list of remaining items | ||||
| 
 | ||||
|     Takes the next_token, and max_results kwargs given to a function and handles | ||||
|     the slicing of the results. The kwarg `next_token` is the offset into the | ||||
|     list to begin slicing from. `max_results` is the size of the result required | ||||
| 
 | ||||
|     If the max_results is not supplied then the `limit` parameter is used as a | ||||
|     default | ||||
| 
 | ||||
|     :param limit_arg: the name of argument in the decorated function that | ||||
|     controls amount of items returned | ||||
|     :param start_arg: the name of the argument in the decorated that provides | ||||
|     the starting offset | ||||
|     :param limit: A default maximum items to return | ||||
|     :return: a tuple containing a list of items, and the offset into the list | ||||
|     """ | ||||
|     default_start = 0 | ||||
| 
 | ||||
|     def outer_wrapper(func): | ||||
|         @functools.wraps(func) | ||||
|         def wrapper(*args, **kwargs): | ||||
|             start = int(default_start if kwargs.get(start_arg) is None else kwargs[start_arg]) | ||||
|             lim = int(limit if kwargs.get(limit_arg) is None else kwargs[limit_arg]) | ||||
|             stop = start + lim | ||||
|             result = func(*args, **kwargs) | ||||
|             limited_results = list(itertools.islice(result, start, stop)) | ||||
|             next_token = stop if stop < len(result) else None | ||||
|             return limited_results, next_token | ||||
|         return wrapper | ||||
|     return outer_wrapper | ||||
| 
 | ||||
| 
 | ||||
| class CognitoIdpUserPool(BaseModel): | ||||
| 
 | ||||
|     def __init__(self, region, name, extended_config): | ||||
| @ -242,7 +277,8 @@ class CognitoIdpBackend(BaseBackend): | ||||
|         self.user_pools[user_pool.id] = user_pool | ||||
|         return user_pool | ||||
| 
 | ||||
|     def list_user_pools(self): | ||||
|     @paginate(60) | ||||
|     def list_user_pools(self, max_results=None, next_token=None): | ||||
|         return self.user_pools.values() | ||||
| 
 | ||||
|     def describe_user_pool(self, user_pool_id): | ||||
| @ -289,7 +325,8 @@ class CognitoIdpBackend(BaseBackend): | ||||
|         user_pool.clients[user_pool_client.id] = user_pool_client | ||||
|         return user_pool_client | ||||
| 
 | ||||
|     def list_user_pool_clients(self, user_pool_id): | ||||
|     @paginate(60) | ||||
|     def list_user_pool_clients(self, user_pool_id, max_results=None, next_token=None): | ||||
|         user_pool = self.user_pools.get(user_pool_id) | ||||
|         if not user_pool: | ||||
|             raise ResourceNotFoundError(user_pool_id) | ||||
| @ -339,7 +376,8 @@ class CognitoIdpBackend(BaseBackend): | ||||
|         user_pool.identity_providers[name] = identity_provider | ||||
|         return identity_provider | ||||
| 
 | ||||
|     def list_identity_providers(self, user_pool_id): | ||||
|     @paginate(60) | ||||
|     def list_identity_providers(self, user_pool_id, max_results=None, next_token=None): | ||||
|         user_pool = self.user_pools.get(user_pool_id) | ||||
|         if not user_pool: | ||||
|             raise ResourceNotFoundError(user_pool_id) | ||||
| @ -387,7 +425,8 @@ class CognitoIdpBackend(BaseBackend): | ||||
| 
 | ||||
|         return user_pool.users[username] | ||||
| 
 | ||||
|     def list_users(self, user_pool_id): | ||||
|     @paginate(60, "pagination_token", "limit") | ||||
|     def list_users(self, user_pool_id, pagination_token=None, limit=None): | ||||
|         user_pool = self.user_pools.get(user_pool_id) | ||||
|         if not user_pool: | ||||
|             raise ResourceNotFoundError(user_pool_id) | ||||
|  | ||||
| @ -22,10 +22,17 @@ class CognitoIdpResponse(BaseResponse): | ||||
|         }) | ||||
| 
 | ||||
|     def list_user_pools(self): | ||||
|         user_pools = cognitoidp_backends[self.region].list_user_pools() | ||||
|         return json.dumps({ | ||||
|             "UserPools": [user_pool.to_json() for user_pool in user_pools] | ||||
|         }) | ||||
|         max_results = self._get_param("MaxResults") | ||||
|         next_token = self._get_param("NextToken", "0") | ||||
|         user_pools, next_token = cognitoidp_backends[self.region].list_user_pools( | ||||
|             max_results=max_results, next_token=next_token | ||||
|         ) | ||||
|         response = { | ||||
|             "UserPools": [user_pool.to_json() for user_pool in user_pools], | ||||
|         } | ||||
|         if next_token: | ||||
|             response["NextToken"] = str(next_token) | ||||
|         return json.dumps(response) | ||||
| 
 | ||||
|     def describe_user_pool(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
| @ -72,10 +79,16 @@ class CognitoIdpResponse(BaseResponse): | ||||
| 
 | ||||
|     def list_user_pool_clients(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
|         user_pool_clients = cognitoidp_backends[self.region].list_user_pool_clients(user_pool_id) | ||||
|         return json.dumps({ | ||||
|         max_results = self._get_param("MaxResults") | ||||
|         next_token = self._get_param("NextToken", "0") | ||||
|         user_pool_clients, next_token = cognitoidp_backends[self.region].list_user_pool_clients(user_pool_id, | ||||
|             max_results=max_results, next_token=next_token) | ||||
|         response = { | ||||
|             "UserPoolClients": [user_pool_client.to_json() for user_pool_client in user_pool_clients] | ||||
|         }) | ||||
|         } | ||||
|         if next_token: | ||||
|             response["NextToken"] = str(next_token) | ||||
|         return json.dumps(response) | ||||
| 
 | ||||
|     def describe_user_pool_client(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
| @ -110,10 +123,17 @@ class CognitoIdpResponse(BaseResponse): | ||||
| 
 | ||||
|     def list_identity_providers(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
|         identity_providers = cognitoidp_backends[self.region].list_identity_providers(user_pool_id) | ||||
|         return json.dumps({ | ||||
|         max_results = self._get_param("MaxResults") | ||||
|         next_token = self._get_param("NextToken", "0") | ||||
|         identity_providers, next_token = cognitoidp_backends[self.region].list_identity_providers( | ||||
|             user_pool_id, max_results=max_results, next_token=next_token | ||||
|         ) | ||||
|         response = { | ||||
|             "Providers": [identity_provider.to_json() for identity_provider in identity_providers] | ||||
|         }) | ||||
|         } | ||||
|         if next_token: | ||||
|             response["NextToken"] = str(next_token) | ||||
|         return json.dumps(response) | ||||
| 
 | ||||
|     def describe_identity_provider(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
| @ -155,10 +175,15 @@ class CognitoIdpResponse(BaseResponse): | ||||
| 
 | ||||
|     def list_users(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
|         users = cognitoidp_backends[self.region].list_users(user_pool_id) | ||||
|         return json.dumps({ | ||||
|             "Users": [user.to_json(extended=True) for user in users] | ||||
|         }) | ||||
|         limit = self._get_param("Limit") | ||||
|         token = self._get_param("PaginationToken") | ||||
|         users, token = cognitoidp_backends[self.region].list_users(user_pool_id, | ||||
|                                                                    limit=limit, | ||||
|                                                                    pagination_token=token) | ||||
|         response = {"Users": [user.to_json(extended=True) for user in users]} | ||||
|         if token: | ||||
|             response["PaginationToken"] = str(token) | ||||
|         return json.dumps(response) | ||||
| 
 | ||||
|     def admin_disable_user(self): | ||||
|         user_pool_id = self._get_param("UserPoolId") | ||||
|  | ||||
| @ -2,11 +2,14 @@ | ||||
| from __future__ import unicode_literals | ||||
| from __future__ import absolute_import | ||||
| 
 | ||||
| from collections import defaultdict | ||||
| import functools | ||||
| import inspect | ||||
| import re | ||||
| import six | ||||
| from io import BytesIO | ||||
| from collections import defaultdict | ||||
| from botocore.handlers import BUILTIN_HANDLERS | ||||
| from botocore.awsrequest import AWSResponse | ||||
| 
 | ||||
| from moto import settings | ||||
| import responses | ||||
| @ -233,7 +236,111 @@ class ResponsesMockAWS(BaseMockAWS): | ||||
|             pass | ||||
| 
 | ||||
| 
 | ||||
| MockAWS = ResponsesMockAWS | ||||
| BOTOCORE_HTTP_METHODS = [ | ||||
|     'GET', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT' | ||||
| ] | ||||
| 
 | ||||
| 
 | ||||
| class MockRawResponse(BytesIO): | ||||
|     def __init__(self, input): | ||||
|         if isinstance(input, six.text_type): | ||||
|             input = input.encode('utf-8') | ||||
|         super(MockRawResponse, self).__init__(input) | ||||
| 
 | ||||
|     def stream(self, **kwargs): | ||||
|         contents = self.read() | ||||
|         while contents: | ||||
|             yield contents | ||||
|             contents = self.read() | ||||
| 
 | ||||
| 
 | ||||
| class BotocoreStubber(object): | ||||
|     def __init__(self): | ||||
|         self.enabled = False | ||||
|         self.methods = defaultdict(list) | ||||
| 
 | ||||
|     def reset(self): | ||||
|         self.methods.clear() | ||||
| 
 | ||||
|     def register_response(self, method, pattern, response): | ||||
|         matchers = self.methods[method] | ||||
|         matchers.append((pattern, response)) | ||||
| 
 | ||||
|     def __call__(self, event_name, request, **kwargs): | ||||
|         if not self.enabled: | ||||
|             return None | ||||
| 
 | ||||
|         response = None | ||||
|         response_callback = None | ||||
|         found_index = None | ||||
|         matchers = self.methods.get(request.method) | ||||
| 
 | ||||
|         base_url = request.url.split('?', 1)[0] | ||||
|         for i, (pattern, callback) in enumerate(matchers): | ||||
|             if pattern.match(base_url): | ||||
|                 if found_index is None: | ||||
|                     found_index = i | ||||
|                     response_callback = callback | ||||
|                 else: | ||||
|                     matchers.pop(found_index) | ||||
|                     break | ||||
| 
 | ||||
|         if response_callback is not None: | ||||
|             for header, value in request.headers.items(): | ||||
|                 if isinstance(value, six.binary_type): | ||||
|                     request.headers[header] = value.decode('utf-8') | ||||
|             status, headers, body = response_callback(request, request.url, request.headers) | ||||
|             body = MockRawResponse(body) | ||||
|             response = AWSResponse(request.url, status, headers, body) | ||||
| 
 | ||||
|         return response | ||||
| 
 | ||||
| 
 | ||||
| botocore_stubber = BotocoreStubber() | ||||
| BUILTIN_HANDLERS.append(('before-send', botocore_stubber)) | ||||
| 
 | ||||
| 
 | ||||
| class BotocoreEventMockAWS(BaseMockAWS): | ||||
|     def reset(self): | ||||
|         botocore_stubber.reset() | ||||
|         responses_mock.reset() | ||||
| 
 | ||||
|     def enable_patching(self): | ||||
|         botocore_stubber.enabled = True | ||||
|         for method in BOTOCORE_HTTP_METHODS: | ||||
|             for backend in self.backends_for_urls.values(): | ||||
|                 for key, value in backend.urls.items(): | ||||
|                     pattern = re.compile(key) | ||||
|                     botocore_stubber.register_response(method, pattern, value) | ||||
| 
 | ||||
|         if not hasattr(responses_mock, '_patcher') or not hasattr(responses_mock._patcher, 'target'): | ||||
|             responses_mock.start() | ||||
| 
 | ||||
|         for method in RESPONSES_METHODS: | ||||
|             # for backend in default_backends.values(): | ||||
|             for backend in self.backends_for_urls.values(): | ||||
|                 for key, value in backend.urls.items(): | ||||
|                     responses_mock.add( | ||||
|                         CallbackResponse( | ||||
|                             method=method, | ||||
|                             url=re.compile(key), | ||||
|                             callback=convert_flask_to_responses_response(value), | ||||
|                             stream=True, | ||||
|                             match_querystring=False, | ||||
|                         ) | ||||
|                     ) | ||||
| 
 | ||||
|     def disable_patching(self): | ||||
|         botocore_stubber.enabled = False | ||||
|         self.reset() | ||||
| 
 | ||||
|         try: | ||||
|             responses_mock.stop() | ||||
|         except RuntimeError: | ||||
|             pass | ||||
| 
 | ||||
| 
 | ||||
| MockAWS = BotocoreEventMockAWS | ||||
| 
 | ||||
| 
 | ||||
| class ServerModeMockAWS(BaseMockAWS): | ||||
|  | ||||
| @ -8,6 +8,7 @@ import random | ||||
| import re | ||||
| import six | ||||
| import string | ||||
| from six.moves.urllib.parse import urlparse | ||||
| 
 | ||||
| 
 | ||||
| REQUEST_ID_LONG = string.digits + string.ascii_uppercase | ||||
| @ -286,3 +287,13 @@ def amzn_request_id(f): | ||||
|         return status, headers, body | ||||
| 
 | ||||
|     return _wrapper | ||||
| 
 | ||||
| 
 | ||||
| def path_url(url): | ||||
|     parsed_url = urlparse(url) | ||||
|     path = parsed_url.path | ||||
|     if not path: | ||||
|         path = '/' | ||||
|     if parsed_url.query: | ||||
|         path = path + '?' + parsed_url.query | ||||
|     return path | ||||
|  | ||||
| @ -8,4 +8,6 @@ class ServiceNotFoundException(RESTError): | ||||
|     def __init__(self, service_name): | ||||
|         super(ServiceNotFoundException, self).__init__( | ||||
|             error_type="ServiceNotFoundException", | ||||
|             message="The service {0} does not exist".format(service_name)) | ||||
|             message="The service {0} does not exist".format(service_name), | ||||
|             template='error_json', | ||||
|         ) | ||||
|  | ||||
| @ -613,13 +613,11 @@ DESCRIBE_STEP_TEMPLATE = """<DescribeStepResponse xmlns="http://elasticmapreduce | ||||
|       <Id>{{ step.id }}</Id> | ||||
|       <Name>{{ step.name | escape }}</Name> | ||||
|       <Status> | ||||
| <!-- does not exist for botocore 1.4.28 | ||||
|         <FailureDetails> | ||||
|           <Reason/> | ||||
|           <Message/> | ||||
|           <LogFile/> | ||||
|         </FailureDetails> | ||||
| --> | ||||
|         <State>{{ step.state }}</State> | ||||
|         <StateChangeReason>{{ step.state_change_reason }}</StateChangeReason> | ||||
|         <Timeline> | ||||
|  | ||||
| @ -24,3 +24,11 @@ class IAMReportNotPresentException(RESTError): | ||||
|     def __init__(self, message): | ||||
|         super(IAMReportNotPresentException, self).__init__( | ||||
|             "ReportNotPresent", message) | ||||
| 
 | ||||
| 
 | ||||
| class MalformedCertificate(RESTError): | ||||
|     code = 400 | ||||
| 
 | ||||
|     def __init__(self, cert): | ||||
|         super(MalformedCertificate, self).__init__( | ||||
|             'MalformedCertificate', 'Certificate {cert} is malformed'.format(cert=cert)) | ||||
|  | ||||
| @ -1,14 +1,18 @@ | ||||
| from __future__ import unicode_literals | ||||
| import base64 | ||||
| import sys | ||||
| from datetime import datetime | ||||
| import json | ||||
| 
 | ||||
| from cryptography import x509 | ||||
| from cryptography.hazmat.backends import default_backend | ||||
| 
 | ||||
| import pytz | ||||
| from moto.core import BaseBackend, BaseModel | ||||
| from moto.core.utils import iso_8601_datetime_without_milliseconds | ||||
| 
 | ||||
| from .aws_managed_policies import aws_managed_policies_data | ||||
| from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException | ||||
| from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, MalformedCertificate | ||||
| from .utils import random_access_key, random_alphanumeric, random_resource_id, random_policy_id | ||||
| 
 | ||||
| ACCOUNT_ID = 123456789012 | ||||
| @ -117,6 +121,7 @@ class Role(BaseModel): | ||||
|         self.path = path | ||||
|         self.policies = {} | ||||
|         self.managed_policies = {} | ||||
|         self.create_date = datetime.now(pytz.utc) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): | ||||
| @ -168,6 +173,7 @@ class InstanceProfile(BaseModel): | ||||
|         self.name = name | ||||
|         self.path = path | ||||
|         self.roles = roles if roles else [] | ||||
|         self.create_date = datetime.now(pytz.utc) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): | ||||
| @ -213,6 +219,16 @@ class Certificate(BaseModel): | ||||
|         return "arn:aws:iam::{0}:server-certificate{1}{2}".format(ACCOUNT_ID, self.path, self.cert_name) | ||||
| 
 | ||||
| 
 | ||||
| class SigningCertificate(BaseModel): | ||||
| 
 | ||||
|     def __init__(self, id, user_name, body): | ||||
|         self.id = id | ||||
|         self.user_name = user_name | ||||
|         self.body = body | ||||
|         self.upload_date = datetime.strftime(datetime.utcnow(), "%Y-%m-%d-%H-%M-%S") | ||||
|         self.status = 'Active' | ||||
| 
 | ||||
| 
 | ||||
| class AccessKey(BaseModel): | ||||
| 
 | ||||
|     def __init__(self, user_name): | ||||
| @ -297,6 +313,7 @@ class User(BaseModel): | ||||
|         self.access_keys = [] | ||||
|         self.password = None | ||||
|         self.password_reset_required = False | ||||
|         self.signing_certificates = {} | ||||
| 
 | ||||
|     @property | ||||
|     def arn(self): | ||||
| @ -765,6 +782,48 @@ class IAMBackend(BaseBackend): | ||||
| 
 | ||||
|         return users | ||||
| 
 | ||||
|     def upload_signing_certificate(self, user_name, body): | ||||
|         user = self.get_user(user_name) | ||||
|         cert_id = random_resource_id(size=32) | ||||
| 
 | ||||
|         # Validate the signing cert: | ||||
|         try: | ||||
|             if sys.version_info < (3, 0): | ||||
|                 data = bytes(body) | ||||
|             else: | ||||
|                 data = bytes(body, 'utf8') | ||||
| 
 | ||||
|             x509.load_pem_x509_certificate(data, default_backend()) | ||||
| 
 | ||||
|         except Exception: | ||||
|             raise MalformedCertificate(body) | ||||
| 
 | ||||
|         user.signing_certificates[cert_id] = SigningCertificate(cert_id, user_name, body) | ||||
| 
 | ||||
|         return user.signing_certificates[cert_id] | ||||
| 
 | ||||
|     def delete_signing_certificate(self, user_name, cert_id): | ||||
|         user = self.get_user(user_name) | ||||
| 
 | ||||
|         try: | ||||
|             del user.signing_certificates[cert_id] | ||||
|         except KeyError: | ||||
|             raise IAMNotFoundException("The Certificate with id {id} cannot be found.".format(id=cert_id)) | ||||
| 
 | ||||
|     def list_signing_certificates(self, user_name): | ||||
|         user = self.get_user(user_name) | ||||
| 
 | ||||
|         return list(user.signing_certificates.values()) | ||||
| 
 | ||||
|     def update_signing_certificate(self, user_name, cert_id, status): | ||||
|         user = self.get_user(user_name) | ||||
| 
 | ||||
|         try: | ||||
|             user.signing_certificates[cert_id].status = status | ||||
| 
 | ||||
|         except KeyError: | ||||
|             raise IAMNotFoundException("The Certificate with id {id} cannot be found.".format(id=cert_id)) | ||||
| 
 | ||||
|     def create_login_profile(self, user_name, password): | ||||
|         # This does not currently deal with PasswordPolicyViolation. | ||||
|         user = self.get_user(user_name) | ||||
|  | ||||
| @ -201,7 +201,7 @@ class IamResponse(BaseResponse): | ||||
| 
 | ||||
|     def create_instance_profile(self): | ||||
|         profile_name = self._get_param('InstanceProfileName') | ||||
|         path = self._get_param('Path') | ||||
|         path = self._get_param('Path', '/') | ||||
| 
 | ||||
|         profile = iam_backend.create_instance_profile( | ||||
|             profile_name, path, role_ids=[]) | ||||
| @ -552,6 +552,38 @@ class IamResponse(BaseResponse): | ||||
|             roles=account_details['roles'] | ||||
|         ) | ||||
| 
 | ||||
|     def upload_signing_certificate(self): | ||||
|         user_name = self._get_param('UserName') | ||||
|         cert_body = self._get_param('CertificateBody') | ||||
| 
 | ||||
|         cert = iam_backend.upload_signing_certificate(user_name, cert_body) | ||||
|         template = self.response_template(UPLOAD_SIGNING_CERTIFICATE_TEMPLATE) | ||||
|         return template.render(cert=cert) | ||||
| 
 | ||||
|     def update_signing_certificate(self): | ||||
|         user_name = self._get_param('UserName') | ||||
|         cert_id = self._get_param('CertificateId') | ||||
|         status = self._get_param('Status') | ||||
| 
 | ||||
|         iam_backend.update_signing_certificate(user_name, cert_id, status) | ||||
|         template = self.response_template(UPDATE_SIGNING_CERTIFICATE_TEMPLATE) | ||||
|         return template.render() | ||||
| 
 | ||||
|     def delete_signing_certificate(self): | ||||
|         user_name = self._get_param('UserName') | ||||
|         cert_id = self._get_param('CertificateId') | ||||
| 
 | ||||
|         iam_backend.delete_signing_certificate(user_name, cert_id) | ||||
|         template = self.response_template(DELETE_SIGNING_CERTIFICATE_TEMPLATE) | ||||
|         return template.render() | ||||
| 
 | ||||
|     def list_signing_certificates(self): | ||||
|         user_name = self._get_param('UserName') | ||||
| 
 | ||||
|         certs = iam_backend.list_signing_certificates(user_name) | ||||
|         template = self.response_template(LIST_SIGNING_CERTIFICATES_TEMPLATE) | ||||
|         return template.render(user_name=user_name, certificates=certs) | ||||
| 
 | ||||
| 
 | ||||
| ATTACH_ROLE_POLICY_TEMPLATE = """<AttachRolePolicyResponse> | ||||
|   <ResponseMetadata> | ||||
| @ -734,7 +766,7 @@ CREATE_INSTANCE_PROFILE_TEMPLATE = """<CreateInstanceProfileResponse xmlns="http | ||||
|       <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|       <Path>{{ profile.path }}</Path> | ||||
|       <Arn>{{ profile.arn }}</Arn> | ||||
|       <CreateDate>2012-05-09T16:11:10.222Z</CreateDate> | ||||
|       <CreateDate>{{ profile.create_date }}</CreateDate> | ||||
|     </InstanceProfile> | ||||
|   </CreateInstanceProfileResult> | ||||
|   <ResponseMetadata> | ||||
| @ -753,7 +785,7 @@ GET_INSTANCE_PROFILE_TEMPLATE = """<GetInstanceProfileResponse xmlns="https://ia | ||||
|           <Arn>{{ role.arn }}</Arn> | ||||
|           <RoleName>{{ role.name }}</RoleName> | ||||
|           <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|           <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|           <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|           <RoleId>{{ role.id }}</RoleId> | ||||
|         </member> | ||||
|         {% endfor %} | ||||
| @ -761,7 +793,7 @@ GET_INSTANCE_PROFILE_TEMPLATE = """<GetInstanceProfileResponse xmlns="https://ia | ||||
|       <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|       <Path>{{ profile.path }}</Path> | ||||
|       <Arn>{{ profile.arn }}</Arn> | ||||
|       <CreateDate>2012-05-09T16:11:10Z</CreateDate> | ||||
|       <CreateDate>{{ profile.create_date }}</CreateDate> | ||||
|     </InstanceProfile> | ||||
|   </GetInstanceProfileResult> | ||||
|   <ResponseMetadata> | ||||
| @ -776,7 +808,7 @@ CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/d | ||||
|       <Arn>{{ role.arn }}</Arn> | ||||
|       <RoleName>{{ role.name }}</RoleName> | ||||
|       <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|       <CreateDate>2012-05-08T23:34:01.495Z</CreateDate> | ||||
|       <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|       <RoleId>{{ role.id }}</RoleId> | ||||
|     </Role> | ||||
|   </CreateRoleResult> | ||||
| @ -803,7 +835,7 @@ GET_ROLE_TEMPLATE = """<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/201 | ||||
|       <Arn>{{ role.arn }}</Arn> | ||||
|       <RoleName>{{ role.name }}</RoleName> | ||||
|       <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|       <CreateDate>2012-05-08T23:34:01Z</CreateDate> | ||||
|       <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|       <RoleId>{{ role.id }}</RoleId> | ||||
|     </Role> | ||||
|   </GetRoleResult> | ||||
| @ -834,7 +866,7 @@ LIST_ROLES_TEMPLATE = """<ListRolesResponse xmlns="https://iam.amazonaws.com/doc | ||||
|         <Arn>{{ role.arn }}</Arn> | ||||
|         <RoleName>{{ role.name }}</RoleName> | ||||
|         <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|         <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|         <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|         <RoleId>{{ role.id }}</RoleId> | ||||
|       </member> | ||||
|       {% endfor %} | ||||
| @ -865,7 +897,7 @@ CREATE_POLICY_VERSION_TEMPLATE = """<CreatePolicyVersionResponse xmlns="https:// | ||||
|       <Document>{{ policy_version.document }}</Document> | ||||
|       <VersionId>{{ policy_version.version_id }}</VersionId> | ||||
|       <IsDefaultVersion>{{ policy_version.is_default }}</IsDefaultVersion> | ||||
|       <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|       <CreateDate>{{ policy_version.create_datetime }}</CreateDate> | ||||
|     </PolicyVersion> | ||||
|   </CreatePolicyVersionResult> | ||||
|   <ResponseMetadata> | ||||
| @ -879,7 +911,7 @@ GET_POLICY_VERSION_TEMPLATE = """<GetPolicyVersionResponse xmlns="https://iam.am | ||||
|       <Document>{{ policy_version.document }}</Document> | ||||
|       <VersionId>{{ policy_version.version_id }}</VersionId> | ||||
|       <IsDefaultVersion>{{ policy_version.is_default }}</IsDefaultVersion> | ||||
|       <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|       <CreateDate>{{ policy_version.create_datetime }}</CreateDate> | ||||
|     </PolicyVersion> | ||||
|   </GetPolicyVersionResult> | ||||
|   <ResponseMetadata> | ||||
| @ -896,7 +928,7 @@ LIST_POLICY_VERSIONS_TEMPLATE = """<ListPolicyVersionsResponse xmlns="https://ia | ||||
|         <Document>{{ policy_version.document }}</Document> | ||||
|         <VersionId>{{ policy_version.version_id }}</VersionId> | ||||
|         <IsDefaultVersion>{{ policy_version.is_default }}</IsDefaultVersion> | ||||
|         <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|         <CreateDate>{{ policy_version.create_datetime }}</CreateDate> | ||||
|       </member> | ||||
|       {% endfor %} | ||||
|     </Versions> | ||||
| @ -912,7 +944,7 @@ LIST_INSTANCE_PROFILES_TEMPLATE = """<ListInstanceProfilesResponse xmlns="https: | ||||
|     <InstanceProfiles> | ||||
|       {% for instance in instance_profiles %} | ||||
|       <member> | ||||
|         <Id>{{ instance.id }}</Id> | ||||
|         <InstanceProfileId>{{ instance.id }}</InstanceProfileId> | ||||
|         <Roles> | ||||
|           {% for role in instance.roles %} | ||||
|           <member> | ||||
| @ -920,7 +952,7 @@ LIST_INSTANCE_PROFILES_TEMPLATE = """<ListInstanceProfilesResponse xmlns="https: | ||||
|             <Arn>{{ role.arn }}</Arn> | ||||
|             <RoleName>{{ role.name }}</RoleName> | ||||
|             <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|             <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|             <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|             <RoleId>{{ role.id }}</RoleId> | ||||
|           </member> | ||||
|           {% endfor %} | ||||
| @ -928,7 +960,7 @@ LIST_INSTANCE_PROFILES_TEMPLATE = """<ListInstanceProfilesResponse xmlns="https: | ||||
|         <InstanceProfileName>{{ instance.name }}</InstanceProfileName> | ||||
|         <Path>{{ instance.path }}</Path> | ||||
|         <Arn>{{ instance.arn }}</Arn> | ||||
|         <CreateDate>2012-05-09T16:27:03Z</CreateDate> | ||||
|         <CreateDate>{{ instance.create_date }}</CreateDate> | ||||
|       </member> | ||||
|       {% endfor %} | ||||
|     </InstanceProfiles> | ||||
| @ -1199,8 +1231,8 @@ LIST_USER_POLICIES_TEMPLATE = """<ListUserPoliciesResponse> | ||||
|          <member>{{ policy }}</member> | ||||
|         {% endfor %} | ||||
|       </PolicyNames> | ||||
|       <IsTruncated>false</IsTruncated> | ||||
|    </ListUserPoliciesResult> | ||||
|    <IsTruncated>false</IsTruncated> | ||||
|    <ResponseMetadata> | ||||
|       <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> | ||||
|    </ResponseMetadata> | ||||
| @ -1277,23 +1309,23 @@ LIST_INSTANCE_PROFILES_FOR_ROLE_TEMPLATE = """<ListInstanceProfilesForRoleRespon | ||||
|   <InstanceProfiles> | ||||
|     {% for profile in instance_profiles %} | ||||
|     <member> | ||||
|     <Id>{{ profile.id }}</Id> | ||||
|       <Roles> | ||||
|         {% for role in profile.roles %} | ||||
|         <member> | ||||
|           <Path>{{ role.path }}</Path> | ||||
|           <Arn>{{ role.arn }}</Arn> | ||||
|           <RoleName>{{ role.name }}</RoleName> | ||||
|           <AssumeRolePolicyDocument>{{ role.assume_policy_document }}</AssumeRolePolicyDocument> | ||||
|           <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|           <RoleId>{{ role.id }}</RoleId> | ||||
|         </member> | ||||
|         {% endfor %} | ||||
|       </Roles> | ||||
|       <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|       <Path>{{ profile.path }}</Path> | ||||
|       <Arn>{{ profile.arn }}</Arn> | ||||
|       <CreateDate>2012-05-09T16:27:11Z</CreateDate> | ||||
|     <InstanceProfileId>{{ profile.id }}</InstanceProfileId> | ||||
|     <Roles> | ||||
|       {% for role in profile.roles %} | ||||
|       <member> | ||||
|         <Path>{{ role.path }}</Path> | ||||
|         <Arn>{{ role.arn }}</Arn> | ||||
|         <RoleName>{{ role.name }}</RoleName> | ||||
|         <AssumeRolePolicyDocument>{{ role.assume_policy_document }}</AssumeRolePolicyDocument> | ||||
|         <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|         <RoleId>{{ role.id }}</RoleId> | ||||
|       </member> | ||||
|       {% endfor %} | ||||
|     </Roles> | ||||
|     <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|     <Path>{{ profile.path }}</Path> | ||||
|     <Arn>{{ profile.arn }}</Arn> | ||||
|     <CreateDate>{{ profile.create_date }}</CreateDate> | ||||
|     </member> | ||||
|     {% endfor %} | ||||
|   </InstanceProfiles> | ||||
| @ -1382,7 +1414,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|         <Path>{{ user.path }}</Path> | ||||
|         <UserName>{{ user.name }}</UserName> | ||||
|         <Arn>{{ user.arn }}</Arn> | ||||
|         <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|         <CreateDate>{{ user.created_iso_8601 }}</CreateDate> | ||||
|       </member> | ||||
|     {% endfor %} | ||||
|     </UserDetailList> | ||||
| @ -1401,7 +1433,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|         <GroupName>{{ group.name }}</GroupName> | ||||
|         <Path>{{ group.path }}</Path> | ||||
|         <Arn>{{ group.arn }}</Arn> | ||||
|         <CreateDate>2012-05-09T16:27:11Z</CreateDate> | ||||
|         <CreateDate>{{ group.create_date }}</CreateDate> | ||||
|         <GroupPolicyList/> | ||||
|       </member> | ||||
|     {% endfor %} | ||||
| @ -1421,23 +1453,23 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|         <InstanceProfileList> | ||||
|             {% for profile in instance_profiles %} | ||||
|             <member> | ||||
|             <Id>{{ profile.id }}</Id> | ||||
|               <Roles> | ||||
|                 {% for role in profile.roles %} | ||||
|                 <member> | ||||
|                   <Path>{{ role.path }}</Path> | ||||
|                   <Arn>{{ role.arn }}</Arn> | ||||
|                   <RoleName>{{ role.name }}</RoleName> | ||||
|                   <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|                   <CreateDate>2012-05-09T15:45:35Z</CreateDate> | ||||
|                   <RoleId>{{ role.id }}</RoleId> | ||||
|                 </member> | ||||
|                 {% endfor %} | ||||
|               </Roles> | ||||
|               <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|               <Path>{{ profile.path }}</Path> | ||||
|               <Arn>{{ profile.arn }}</Arn> | ||||
|               <CreateDate>2012-05-09T16:27:11Z</CreateDate> | ||||
|             <InstanceProfileId>{{ profile.id }}</InstanceProfileId> | ||||
|             <Roles> | ||||
|               {% for role in profile.roles %} | ||||
|               <member> | ||||
|                 <Path>{{ role.path }}</Path> | ||||
|                 <Arn>{{ role.arn }}</Arn> | ||||
|                 <RoleName>{{ role.name }}</RoleName> | ||||
|                 <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|                 <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|                 <RoleId>{{ role.id }}</RoleId> | ||||
|               </member> | ||||
|               {% endfor %} | ||||
|             </Roles> | ||||
|             <InstanceProfileName>{{ profile.name }}</InstanceProfileName> | ||||
|             <Path>{{ profile.path }}</Path> | ||||
|             <Arn>{{ profile.arn }}</Arn> | ||||
|             <CreateDate>{{ profile.create_date }}</CreateDate> | ||||
|             </member> | ||||
|             {% endfor %} | ||||
|         </InstanceProfileList> | ||||
| @ -1445,7 +1477,7 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|         <Arn>{{ role.arn }}</Arn> | ||||
|         <RoleName>{{ role.name }}</RoleName> | ||||
|         <AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument> | ||||
|         <CreateDate>2014-07-30T17:09:20Z</CreateDate> | ||||
|         <CreateDate>{{ role.create_date }}</CreateDate> | ||||
|         <RoleId>{{ role.id }}</RoleId> | ||||
|       </member> | ||||
|     {% endfor %} | ||||
| @ -1474,9 +1506,9 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|         </PolicyVersionList> | ||||
|         <Arn>{{ policy.arn }}</Arn> | ||||
|         <AttachmentCount>1</AttachmentCount> | ||||
|         <CreateDate>2012-05-09T16:27:11Z</CreateDate> | ||||
|         <CreateDate>{{ policy.create_datetime }}</CreateDate> | ||||
|         <IsAttachable>true</IsAttachable> | ||||
|         <UpdateDate>2012-05-09T16:27:11Z</UpdateDate> | ||||
|         <UpdateDate>{{ policy.update_datetime }}</UpdateDate> | ||||
|       </member> | ||||
|     {% endfor %} | ||||
|     </Policies> | ||||
| @ -1485,3 +1517,53 @@ GET_ACCOUNT_AUTHORIZATION_DETAILS_TEMPLATE = """<GetAccountAuthorizationDetailsR | ||||
|     <RequestId>92e79ae7-7399-11e4-8c85-4b53eEXAMPLE</RequestId> | ||||
|   </ResponseMetadata> | ||||
| </GetAccountAuthorizationDetailsResponse>""" | ||||
| 
 | ||||
| 
 | ||||
| UPLOAD_SIGNING_CERTIFICATE_TEMPLATE = """<UploadSigningCertificateResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> | ||||
|   <UploadSigningCertificateResult> | ||||
|     <Certificate> | ||||
|       <UserName>{{ cert.user_name }}</UserName> | ||||
|       <CertificateId>{{ cert.id }}</CertificateId> | ||||
|       <CertificateBody>{{ cert.body }}</CertificateBody> | ||||
|       <Status>{{ cert.status }}</Status> | ||||
|     </Certificate> | ||||
|  </UploadSigningCertificateResult> | ||||
|  <ResponseMetadata> | ||||
|     <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> | ||||
|  </ResponseMetadata> | ||||
| </UploadSigningCertificateResponse>""" | ||||
| 
 | ||||
| 
 | ||||
| UPDATE_SIGNING_CERTIFICATE_TEMPLATE = """<UpdateSigningCertificateResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> | ||||
|  <ResponseMetadata> | ||||
|     <RequestId>EXAMPLE8-90ab-cdef-fedc-ba987EXAMPLE</RequestId> | ||||
|  </ResponseMetadata> | ||||
| </UpdateSigningCertificateResponse>""" | ||||
| 
 | ||||
| 
 | ||||
| DELETE_SIGNING_CERTIFICATE_TEMPLATE = """<DeleteSigningCertificateResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> | ||||
|   <ResponseMetadata> | ||||
|     <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> | ||||
|   </ResponseMetadata> | ||||
| </DeleteSigningCertificateResponse>""" | ||||
| 
 | ||||
| 
 | ||||
| LIST_SIGNING_CERTIFICATES_TEMPLATE = """<ListSigningCertificatesResponse> | ||||
|   <ListSigningCertificatesResult> | ||||
|     <UserName>{{ user_name }}</UserName> | ||||
|     <Certificates> | ||||
|        {% for cert in certificates %} | ||||
|        <member> | ||||
|           <UserName>{{ user_name }}</UserName> | ||||
|           <CertificateId>{{ cert.id }}</CertificateId> | ||||
|           <CertificateBody>{{ cert.body }}</CertificateBody> | ||||
|           <Status>{{ cert.status }}</Status> | ||||
|        </member> | ||||
|        {% endfor %} | ||||
|     </Certificates> | ||||
|     <IsTruncated>false</IsTruncated> | ||||
|  </ListSigningCertificatesResult> | ||||
|  <ResponseMetadata> | ||||
|     <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> | ||||
|  </ResponseMetadata> | ||||
| </ListSigningCertificatesResponse>""" | ||||
|  | ||||
| @ -12,8 +12,7 @@ def random_alphanumeric(length): | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def random_resource_id(): | ||||
|     size = 20 | ||||
| def random_resource_id(size=20): | ||||
|     chars = list(range(10)) + list(string.ascii_lowercase) | ||||
| 
 | ||||
|     return ''.join(six.text_type(random.choice(chars)) for x in range(size)) | ||||
|  | ||||
| @ -178,3 +178,13 @@ class InvalidStorageClass(S3ClientError): | ||||
|             "InvalidStorageClass", | ||||
|             "The storage class you specified is not valid", | ||||
|             *args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class DuplicateTagKeys(S3ClientError): | ||||
|     code = 400 | ||||
| 
 | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super(DuplicateTagKeys, self).__init__( | ||||
|             "InvalidTag", | ||||
|             "Cannot provide multiple Tags with the same key", | ||||
|             *args, **kwargs) | ||||
|  | ||||
| @ -15,7 +15,7 @@ from bisect import insort | ||||
| from moto.core import BaseBackend, BaseModel | ||||
| from moto.core.utils import iso_8601_datetime_with_milliseconds, rfc_1123_datetime | ||||
| from .exceptions import BucketAlreadyExists, MissingBucket, InvalidPart, EntityTooSmall, MissingKey, \ | ||||
|     InvalidNotificationDestination, MalformedXML, InvalidStorageClass | ||||
|     InvalidNotificationDestination, MalformedXML, InvalidStorageClass, DuplicateTagKeys | ||||
| from .utils import clean_key_name, _VersionedKeyStore | ||||
| 
 | ||||
| UPLOAD_ID_BYTES = 43 | ||||
| @ -773,6 +773,9 @@ class S3Backend(BaseBackend): | ||||
|         return key | ||||
| 
 | ||||
|     def put_bucket_tagging(self, bucket_name, tagging): | ||||
|         tag_keys = [tag.key for tag in tagging.tag_set.tags] | ||||
|         if len(tag_keys) != len(set(tag_keys)): | ||||
|             raise DuplicateTagKeys() | ||||
|         bucket = self.get_bucket(bucket_name) | ||||
|         bucket.set_tags(tagging) | ||||
| 
 | ||||
|  | ||||
| @ -10,6 +10,7 @@ import xmltodict | ||||
| 
 | ||||
| from moto.packages.httpretty.core import HTTPrettyRequest | ||||
| from moto.core.responses import _TemplateEnvironmentMixin | ||||
| from moto.core.utils import path_url | ||||
| 
 | ||||
| from moto.s3bucket_path.utils import bucket_name_from_url as bucketpath_bucket_name_from_url, \ | ||||
|     parse_key_name as bucketpath_parse_key_name, is_delete_keys as bucketpath_is_delete_keys | ||||
| @ -487,7 +488,7 @@ class ResponseObject(_TemplateEnvironmentMixin): | ||||
|         if isinstance(request, HTTPrettyRequest): | ||||
|             path = request.path | ||||
|         else: | ||||
|             path = request.full_path if hasattr(request, 'full_path') else request.path_url | ||||
|             path = request.full_path if hasattr(request, 'full_path') else path_url(request.url) | ||||
| 
 | ||||
|         if self.is_delete_keys(request, path, bucket_name): | ||||
|             return self._bucket_response_delete_keys(request, body, bucket_name, headers) | ||||
| @ -708,7 +709,10 @@ class ResponseObject(_TemplateEnvironmentMixin): | ||||
|             # Copy key | ||||
|             # you can have a quoted ?version=abc with a version Id, so work on | ||||
|             # we need to parse the unquoted string first | ||||
|             src_key_parsed = urlparse(request.headers.get("x-amz-copy-source")) | ||||
|             src_key = request.headers.get("x-amz-copy-source") | ||||
|             if isinstance(src_key, six.binary_type): | ||||
|                 src_key = src_key.decode('utf-8') | ||||
|             src_key_parsed = urlparse(src_key) | ||||
|             src_bucket, src_key = unquote(src_key_parsed.path).\ | ||||
|                 lstrip("/").split("/", 1) | ||||
|             src_version_id = parse_qs(src_key_parsed.query).get( | ||||
|  | ||||
| @ -2,6 +2,7 @@ from __future__ import unicode_literals | ||||
| 
 | ||||
| import time | ||||
| import json | ||||
| import uuid | ||||
| 
 | ||||
| import boto3 | ||||
| 
 | ||||
| @ -18,10 +19,6 @@ class SecretsManager(BaseModel): | ||||
| 
 | ||||
|     def __init__(self, region_name, **kwargs): | ||||
|         self.region = region_name | ||||
|         self.secret_id = kwargs.get('secret_id', '') | ||||
|         self.version_id = kwargs.get('version_id', '') | ||||
|         self.version_stage = kwargs.get('version_stage', '') | ||||
|         self.secret_string = '' | ||||
| 
 | ||||
| 
 | ||||
| class SecretsManagerBackend(BaseBackend): | ||||
| @ -29,14 +26,7 @@ class SecretsManagerBackend(BaseBackend): | ||||
|     def __init__(self, region_name=None, **kwargs): | ||||
|         super(SecretsManagerBackend, self).__init__() | ||||
|         self.region = region_name | ||||
|         self.secret_id = kwargs.get('secret_id', '') | ||||
|         self.name = kwargs.get('name', '') | ||||
|         self.createdate = int(time.time()) | ||||
|         self.secret_string = '' | ||||
|         self.rotation_enabled = False | ||||
|         self.rotation_lambda_arn = '' | ||||
|         self.auto_rotate_after_days = 0 | ||||
|         self.version_id = '' | ||||
|         self.secrets = {} | ||||
| 
 | ||||
|     def reset(self): | ||||
|         region_name = self.region | ||||
| @ -44,36 +34,50 @@ class SecretsManagerBackend(BaseBackend): | ||||
|         self.__init__(region_name) | ||||
| 
 | ||||
|     def _is_valid_identifier(self, identifier): | ||||
|         return identifier in (self.name, self.secret_id) | ||||
|         return identifier in self.secrets | ||||
| 
 | ||||
|     def get_secret_value(self, secret_id, version_id, version_stage): | ||||
| 
 | ||||
|         if not self._is_valid_identifier(secret_id): | ||||
|             raise ResourceNotFoundException() | ||||
| 
 | ||||
|         secret = self.secrets[secret_id] | ||||
| 
 | ||||
|         response = json.dumps({ | ||||
|             "ARN": secret_arn(self.region, self.secret_id), | ||||
|             "Name": self.name, | ||||
|             "VersionId": "A435958A-D821-4193-B719-B7769357AER4", | ||||
|             "SecretString": self.secret_string, | ||||
|             "ARN": secret_arn(self.region, secret['secret_id']), | ||||
|             "Name": secret['name'], | ||||
|             "VersionId": secret['version_id'], | ||||
|             "SecretString": secret['secret_string'], | ||||
|             "VersionStages": [ | ||||
|                 "AWSCURRENT", | ||||
|             ], | ||||
|             "CreatedDate": "2018-05-23 13:16:57.198000" | ||||
|             "CreatedDate": secret['createdate'] | ||||
|         }) | ||||
| 
 | ||||
|         return response | ||||
| 
 | ||||
|     def create_secret(self, name, secret_string, **kwargs): | ||||
|     def create_secret(self, name, secret_string, tags, **kwargs): | ||||
| 
 | ||||
|         self.secret_string = secret_string | ||||
|         self.secret_id = name | ||||
|         self.name = name | ||||
|         generated_version_id = str(uuid.uuid4()) | ||||
| 
 | ||||
|         secret = { | ||||
|             'secret_string': secret_string, | ||||
|             'secret_id': name, | ||||
|             'name': name, | ||||
|             'createdate': int(time.time()), | ||||
|             'rotation_enabled': False, | ||||
|             'rotation_lambda_arn': '', | ||||
|             'auto_rotate_after_days': 0, | ||||
|             'version_id': generated_version_id, | ||||
|             'tags': tags | ||||
|         } | ||||
| 
 | ||||
|         self.secrets[name] = secret | ||||
| 
 | ||||
|         response = json.dumps({ | ||||
|             "ARN": secret_arn(self.region, name), | ||||
|             "Name": self.name, | ||||
|             "VersionId": "A435958A-D821-4193-B719-B7769357AER4", | ||||
|             "Name": name, | ||||
|             "VersionId": generated_version_id, | ||||
|         }) | ||||
| 
 | ||||
|         return response | ||||
| @ -82,26 +86,23 @@ class SecretsManagerBackend(BaseBackend): | ||||
|         if not self._is_valid_identifier(secret_id): | ||||
|             raise ResourceNotFoundException | ||||
| 
 | ||||
|         secret = self.secrets[secret_id] | ||||
| 
 | ||||
|         response = json.dumps({ | ||||
|             "ARN": secret_arn(self.region, self.secret_id), | ||||
|             "Name": self.name, | ||||
|             "ARN": secret_arn(self.region, secret['secret_id']), | ||||
|             "Name": secret['name'], | ||||
|             "Description": "", | ||||
|             "KmsKeyId": "", | ||||
|             "RotationEnabled": self.rotation_enabled, | ||||
|             "RotationLambdaARN": self.rotation_lambda_arn, | ||||
|             "RotationEnabled": secret['rotation_enabled'], | ||||
|             "RotationLambdaARN": secret['rotation_lambda_arn'], | ||||
|             "RotationRules": { | ||||
|                 "AutomaticallyAfterDays": self.auto_rotate_after_days | ||||
|                 "AutomaticallyAfterDays": secret['auto_rotate_after_days'] | ||||
|             }, | ||||
|             "LastRotatedDate": None, | ||||
|             "LastChangedDate": None, | ||||
|             "LastAccessedDate": None, | ||||
|             "DeletedDate": None, | ||||
|             "Tags": [ | ||||
|                 { | ||||
|                     "Key": "", | ||||
|                     "Value": "" | ||||
|                 }, | ||||
|             ] | ||||
|             "Tags": secret['tags'] | ||||
|         }) | ||||
| 
 | ||||
|         return response | ||||
| @ -141,17 +142,19 @@ class SecretsManagerBackend(BaseBackend): | ||||
|                     ) | ||||
|                     raise InvalidParameterException(msg) | ||||
| 
 | ||||
|         self.version_id = client_request_token or '' | ||||
|         self.rotation_lambda_arn = rotation_lambda_arn or '' | ||||
|         secret = self.secrets[secret_id] | ||||
| 
 | ||||
|         secret['version_id'] = client_request_token or '' | ||||
|         secret['rotation_lambda_arn'] = rotation_lambda_arn or '' | ||||
|         if rotation_rules: | ||||
|             self.auto_rotate_after_days = rotation_rules.get(rotation_days, 0) | ||||
|         if self.auto_rotate_after_days > 0: | ||||
|             self.rotation_enabled = True | ||||
|             secret['auto_rotate_after_days'] = rotation_rules.get(rotation_days, 0) | ||||
|         if secret['auto_rotate_after_days'] > 0: | ||||
|             secret['rotation_enabled'] = True | ||||
| 
 | ||||
|         response = json.dumps({ | ||||
|             "ARN": secret_arn(self.region, self.secret_id), | ||||
|             "Name": self.name, | ||||
|             "VersionId": self.version_id | ||||
|             "ARN": secret_arn(self.region, secret['secret_id']), | ||||
|             "Name": secret['name'], | ||||
|             "VersionId": secret['version_id'] | ||||
|         }) | ||||
| 
 | ||||
|         return response | ||||
|  | ||||
| @ -19,9 +19,11 @@ class SecretsManagerResponse(BaseResponse): | ||||
|     def create_secret(self): | ||||
|         name = self._get_param('Name') | ||||
|         secret_string = self._get_param('SecretString') | ||||
|         tags = self._get_param('Tags', if_none=[]) | ||||
|         return secretsmanager_backends[self.region].create_secret( | ||||
|             name=name, | ||||
|             secret_string=secret_string | ||||
|             secret_string=secret_string, | ||||
|             tags=tags | ||||
|         ) | ||||
| 
 | ||||
|     def get_random_password(self): | ||||
|  | ||||
| @ -52,8 +52,9 @@ def random_password(password_length, exclude_characters, exclude_numbers, | ||||
| 
 | ||||
| 
 | ||||
| def secret_arn(region, secret_id): | ||||
|     return "arn:aws:secretsmanager:{0}:1234567890:secret:{1}-rIjad".format( | ||||
|         region, secret_id) | ||||
|     id_string = ''.join(random.choice(string.ascii_letters) for _ in range(5)) | ||||
|     return "arn:aws:secretsmanager:{0}:1234567890:secret:{1}-{2}".format( | ||||
|         region, secret_id, id_string) | ||||
| 
 | ||||
| 
 | ||||
| def _exclude_characters(password, exclude_characters): | ||||
|  | ||||
| @ -8,7 +8,7 @@ freezegun | ||||
| flask | ||||
| boto>=2.45.0 | ||||
| boto3>=1.4.4 | ||||
| botocore>=1.8.36 | ||||
| botocore>=1.12.13 | ||||
| six>=1.9 | ||||
| prompt-toolkit==1.0.14 | ||||
| click==6.7 | ||||
|  | ||||
							
								
								
									
										23
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								setup.py
									
									
									
									
									
								
							| @ -1,15 +1,28 @@ | ||||
| #!/usr/bin/env python | ||||
| from __future__ import unicode_literals | ||||
| import codecs | ||||
| import os | ||||
| import re | ||||
| import setuptools | ||||
| from setuptools import setup, find_packages | ||||
| import sys | ||||
| 
 | ||||
| 
 | ||||
| # Borrowed from pip at https://github.com/pypa/pip/blob/62c27dee45625e1b63d1e023b0656310f276e050/setup.py#L11-L15 | ||||
| here = os.path.abspath(os.path.dirname(__file__)) | ||||
| 
 | ||||
| def read(*parts): | ||||
|     # intentionally *not* adding an encoding option to open, See: | ||||
|     #   https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690 | ||||
|     with codecs.open(os.path.join(here, *parts), 'r') as fp: | ||||
|         return fp.read() | ||||
| 
 | ||||
| 
 | ||||
| install_requires = [ | ||||
|     "Jinja2>=2.7.3", | ||||
|     "boto>=2.36.0", | ||||
|     "boto3>=1.6.16,<1.8", | ||||
|     "botocore>=1.9.16,<1.11", | ||||
|     "boto3>=1.6.16", | ||||
|     "botocore>=1.12.13", | ||||
|     "cryptography>=2.3.0", | ||||
|     "requests>=2.5", | ||||
|     "xmltodict", | ||||
| @ -22,7 +35,7 @@ install_requires = [ | ||||
|     "mock", | ||||
|     "docker>=2.5.1", | ||||
|     "jsondiff==1.1.2", | ||||
|     "aws-xray-sdk<0.96,>=0.93", | ||||
|     "aws-xray-sdk!=0.96,>=0.93", | ||||
|     "responses>=0.9.0", | ||||
| ] | ||||
| 
 | ||||
| @ -40,9 +53,11 @@ else: | ||||
| 
 | ||||
| setup( | ||||
|     name='moto', | ||||
|     version='1.3.6', | ||||
|     version='1.3.7', | ||||
|     description='A library that allows your python tests to easily' | ||||
|                 ' mock out the boto library', | ||||
|     long_description=read('README.md'), | ||||
|     long_description_content_type='text/markdown', | ||||
|     author='Steve Pulec', | ||||
|     author_email='spulec@gmail.com', | ||||
|     url='https://github.com/spulec/moto', | ||||
|  | ||||
| @ -41,6 +41,56 @@ def test_list_user_pools(): | ||||
|     result["UserPools"][0]["Name"].should.equal(name) | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pools_returns_max_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| 
 | ||||
|     # Given 10 user pools | ||||
|     pool_count = 10 | ||||
|     for i in range(pool_count): | ||||
|         conn.create_user_pool(PoolName=str(uuid.uuid4())) | ||||
| 
 | ||||
|     max_results = 5 | ||||
|     result = conn.list_user_pools(MaxResults=max_results) | ||||
|     result["UserPools"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pools_returns_next_tokens(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| 
 | ||||
|     # Given 10 user pool clients | ||||
|     pool_count = 10 | ||||
|     for i in range(pool_count): | ||||
|         conn.create_user_pool(PoolName=str(uuid.uuid4())) | ||||
| 
 | ||||
|     max_results = 5 | ||||
|     result = conn.list_user_pools(MaxResults=max_results) | ||||
|     result["UserPools"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
|     next_token = result["NextToken"] | ||||
|     result_2 = conn.list_user_pools(MaxResults=max_results, NextToken=next_token) | ||||
|     result_2["UserPools"].should.have.length_of(max_results) | ||||
|     result_2.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pools_when_max_items_more_than_total_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| 
 | ||||
|     # Given 10 user pool clients | ||||
|     pool_count = 10 | ||||
|     for i in range(pool_count): | ||||
|         conn.create_user_pool(PoolName=str(uuid.uuid4())) | ||||
| 
 | ||||
|     max_results = pool_count + 5 | ||||
|     result = conn.list_user_pools(MaxResults=max_results) | ||||
|     result["UserPools"].should.have.length_of(pool_count) | ||||
|     result.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_describe_user_pool(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| @ -140,6 +190,67 @@ def test_list_user_pool_clients(): | ||||
|     result["UserPoolClients"][0]["ClientName"].should.equal(client_name) | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pool_clients_returns_max_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 user pool clients | ||||
|     client_count = 10 | ||||
|     for i in range(client_count): | ||||
|         client_name = str(uuid.uuid4()) | ||||
|         conn.create_user_pool_client(UserPoolId=user_pool_id, | ||||
|                                      ClientName=client_name) | ||||
|     max_results = 5 | ||||
|     result = conn.list_user_pool_clients(UserPoolId=user_pool_id, | ||||
|                                          MaxResults=max_results) | ||||
|     result["UserPoolClients"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pool_clients_returns_next_tokens(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 user pool clients | ||||
|     client_count = 10 | ||||
|     for i in range(client_count): | ||||
|         client_name = str(uuid.uuid4()) | ||||
|         conn.create_user_pool_client(UserPoolId=user_pool_id, | ||||
|                                      ClientName=client_name) | ||||
|     max_results = 5 | ||||
|     result = conn.list_user_pool_clients(UserPoolId=user_pool_id, | ||||
|                                          MaxResults=max_results) | ||||
|     result["UserPoolClients"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
|     next_token = result["NextToken"] | ||||
|     result_2 = conn.list_user_pool_clients(UserPoolId=user_pool_id, | ||||
|                                            MaxResults=max_results, | ||||
|                                            NextToken=next_token) | ||||
|     result_2["UserPoolClients"].should.have.length_of(max_results) | ||||
|     result_2.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_user_pool_clients_when_max_items_more_than_total_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 user pool clients | ||||
|     client_count = 10 | ||||
|     for i in range(client_count): | ||||
|         client_name = str(uuid.uuid4()) | ||||
|         conn.create_user_pool_client(UserPoolId=user_pool_id, | ||||
|                                      ClientName=client_name) | ||||
|     max_results = client_count + 5 | ||||
|     result = conn.list_user_pool_clients(UserPoolId=user_pool_id, | ||||
|                                          MaxResults=max_results) | ||||
|     result["UserPoolClients"].should.have.length_of(client_count) | ||||
|     result.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_describe_user_pool_client(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| @ -264,6 +375,83 @@ def test_list_identity_providers(): | ||||
|     result["Providers"][0]["ProviderType"].should.equal(provider_type) | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_identity_providers_returns_max_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 identity providers linked to a user pool | ||||
|     identity_provider_count = 10 | ||||
|     for i in range(identity_provider_count): | ||||
|         provider_name = str(uuid.uuid4()) | ||||
|         provider_type = "Facebook" | ||||
|         conn.create_identity_provider( | ||||
|             UserPoolId=user_pool_id, | ||||
|             ProviderName=provider_name, | ||||
|             ProviderType=provider_type, | ||||
|             ProviderDetails={}, | ||||
|         ) | ||||
| 
 | ||||
|     max_results = 5 | ||||
|     result = conn.list_identity_providers(UserPoolId=user_pool_id, | ||||
|                                           MaxResults=max_results) | ||||
|     result["Providers"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_identity_providers_returns_next_tokens(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 identity providers linked to a user pool | ||||
|     identity_provider_count = 10 | ||||
|     for i in range(identity_provider_count): | ||||
|         provider_name = str(uuid.uuid4()) | ||||
|         provider_type = "Facebook" | ||||
|         conn.create_identity_provider( | ||||
|             UserPoolId=user_pool_id, | ||||
|             ProviderName=provider_name, | ||||
|             ProviderType=provider_type, | ||||
|             ProviderDetails={}, | ||||
|         ) | ||||
| 
 | ||||
|     max_results = 5 | ||||
|     result = conn.list_identity_providers(UserPoolId=user_pool_id, MaxResults=max_results) | ||||
|     result["Providers"].should.have.length_of(max_results) | ||||
|     result.should.have.key("NextToken") | ||||
| 
 | ||||
|     next_token = result["NextToken"] | ||||
|     result_2 = conn.list_identity_providers(UserPoolId=user_pool_id, | ||||
|                                            MaxResults=max_results, | ||||
|                                            NextToken=next_token) | ||||
|     result_2["Providers"].should.have.length_of(max_results) | ||||
|     result_2.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_identity_providers_when_max_items_more_than_total_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 identity providers linked to a user pool | ||||
|     identity_provider_count = 10 | ||||
|     for i in range(identity_provider_count): | ||||
|         provider_name = str(uuid.uuid4()) | ||||
|         provider_type = "Facebook" | ||||
|         conn.create_identity_provider( | ||||
|             UserPoolId=user_pool_id, | ||||
|             ProviderName=provider_name, | ||||
|             ProviderType=provider_type, | ||||
|             ProviderDetails={}, | ||||
|         ) | ||||
| 
 | ||||
|     max_results = identity_provider_count + 5 | ||||
|     result = conn.list_identity_providers(UserPoolId=user_pool_id, MaxResults=max_results) | ||||
|     result["Providers"].should.have.length_of(identity_provider_count) | ||||
|     result.shouldnt.have.key("NextToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_describe_identity_providers(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
| @ -396,6 +584,62 @@ def test_list_users(): | ||||
|     result["Users"][0]["Username"].should.equal(username) | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_users_returns_limit_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 users | ||||
|     user_count = 10 | ||||
|     for i in range(user_count): | ||||
|         conn.admin_create_user(UserPoolId=user_pool_id, | ||||
|                                Username=str(uuid.uuid4())) | ||||
|     max_results = 5 | ||||
|     result = conn.list_users(UserPoolId=user_pool_id, Limit=max_results) | ||||
|     result["Users"].should.have.length_of(max_results) | ||||
|     result.should.have.key("PaginationToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_users_returns_pagination_tokens(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 users | ||||
|     user_count = 10 | ||||
|     for i in range(user_count): | ||||
|         conn.admin_create_user(UserPoolId=user_pool_id, | ||||
|                                Username=str(uuid.uuid4())) | ||||
| 
 | ||||
|     max_results = 5 | ||||
|     result = conn.list_users(UserPoolId=user_pool_id, Limit=max_results) | ||||
|     result["Users"].should.have.length_of(max_results) | ||||
|     result.should.have.key("PaginationToken") | ||||
| 
 | ||||
|     next_token = result["PaginationToken"] | ||||
|     result_2 = conn.list_users(UserPoolId=user_pool_id, | ||||
|                                Limit=max_results, PaginationToken=next_token) | ||||
|     result_2["Users"].should.have.length_of(max_results) | ||||
|     result_2.shouldnt.have.key("PaginationToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_list_users_when_limit_more_than_total_items(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|     user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"] | ||||
| 
 | ||||
|     # Given 10 users | ||||
|     user_count = 10 | ||||
|     for i in range(user_count): | ||||
|         conn.admin_create_user(UserPoolId=user_pool_id, | ||||
|                                Username=str(uuid.uuid4())) | ||||
| 
 | ||||
|     max_results = user_count + 5 | ||||
|     result = conn.list_users(UserPoolId=user_pool_id, Limit=max_results) | ||||
|     result["Users"].should.have.length_of(user_count) | ||||
|     result.shouldnt.have.key("PaginationToken") | ||||
| 
 | ||||
| 
 | ||||
| @mock_cognitoidp | ||||
| def test_admin_disable_user(): | ||||
|     conn = boto3.client("cognito-idp", "us-west-2") | ||||
|  | ||||
| @ -631,7 +631,22 @@ def test_delete_service(): | ||||
|     response['service']['schedulingStrategy'].should.equal('REPLICA') | ||||
|     response['service']['taskDefinition'].should.equal( | ||||
|         'arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1') | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| @mock_ecs | ||||
| def test_update_non_existant_service(): | ||||
|     client = boto3.client('ecs', region_name='us-east-1') | ||||
|     try: | ||||
|         client.update_service( | ||||
|             cluster="my-clustet", | ||||
|             service="my-service", | ||||
|             desiredCount=0, | ||||
|         ) | ||||
|     except ClientError as exc: | ||||
|         error_code = exc.response['Error']['Code'] | ||||
|         error_code.should.equal('ServiceNotFoundException') | ||||
|     else: | ||||
|         raise Exception("Didn't raise ClientError") | ||||
| 
 | ||||
| 
 | ||||
| @mock_ec2 | ||||
|  | ||||
| @ -14,6 +14,19 @@ from nose.tools import raises | ||||
| from tests.helpers import requires_boto_gte | ||||
| 
 | ||||
| 
 | ||||
| MOCK_CERT = """-----BEGIN CERTIFICATE----- | ||||
| MIIBpzCCARACCQCY5yOdxCTrGjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQKDAxt | ||||
| b3RvIHRlc3RpbmcwIBcNMTgxMTA1MTkwNTIwWhgPMjI5MjA4MTkxOTA1MjBaMBcx | ||||
| FTATBgNVBAoMDG1vdG8gdGVzdGluZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC | ||||
| gYEA1Jn3g2h7LD3FLqdpcYNbFXCS4V4eDpuTCje9vKFcC3pi/01147X3zdfPy8Mt | ||||
| ZhKxcREOwm4NXykh23P9KW7fBovpNwnbYsbPqj8Hf1ZaClrgku1arTVhEnKjx8zO | ||||
| vaR/bVLCss4uE0E0VM1tJn/QGQsfthFsjuHtwx8uIWz35tUCAwEAATANBgkqhkiG | ||||
| 9w0BAQsFAAOBgQBWdOQ7bDc2nWkUhFjZoNIZrqjyNdjlMUndpwREVD7FQ/DuxJMj | ||||
| FyDHrtlrS80dPUQWNYHw++oACDpWO01LGLPPrGmuO/7cOdojPEd852q5gd+7W9xt | ||||
| 8vUH+pBa6IBLbvBp+szli51V3TLSWcoyy4ceJNQU2vCkTLoFdS0RLd/7tQ== | ||||
| -----END CERTIFICATE-----""" | ||||
| 
 | ||||
| 
 | ||||
| @mock_iam_deprecated() | ||||
| def test_get_all_server_certs(): | ||||
|     conn = boto.connect_iam() | ||||
| @ -108,6 +121,10 @@ def test_create_role_and_instance_profile(): | ||||
| 
 | ||||
|     conn.list_roles().roles[0].role_name.should.equal('my-role') | ||||
| 
 | ||||
|     # Test with an empty path: | ||||
|     profile = conn.create_instance_profile('my-other-profile') | ||||
|     profile.path.should.equal('/') | ||||
| 
 | ||||
| 
 | ||||
| @mock_iam_deprecated() | ||||
| def test_remove_role_from_instance_profile(): | ||||
| @ -700,10 +717,10 @@ def test_get_account_authorization_details(): | ||||
|     import json | ||||
|     conn = boto3.client('iam', region_name='us-east-1') | ||||
|     conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/") | ||||
|     conn.create_user(Path='/', UserName='testCloudAuxUser') | ||||
|     conn.create_group(Path='/', GroupName='testCloudAuxGroup') | ||||
|     conn.create_user(Path='/', UserName='testUser') | ||||
|     conn.create_group(Path='/', GroupName='testGroup') | ||||
|     conn.create_policy( | ||||
|         PolicyName='testCloudAuxPolicy', | ||||
|         PolicyName='testPolicy', | ||||
|         Path='/', | ||||
|         PolicyDocument=json.dumps({ | ||||
|             "Version": "2012-10-17", | ||||
| @ -715,46 +732,110 @@ def test_get_account_authorization_details(): | ||||
|                 } | ||||
|             ] | ||||
|         }), | ||||
|         Description='Test CloudAux Policy' | ||||
|         Description='Test Policy' | ||||
|     ) | ||||
| 
 | ||||
|     conn.create_instance_profile(InstanceProfileName='ipn') | ||||
|     conn.add_role_to_instance_profile(InstanceProfileName='ipn', RoleName='my-role') | ||||
| 
 | ||||
|     result = conn.get_account_authorization_details(Filter=['Role']) | ||||
|     len(result['RoleDetailList']) == 1 | ||||
|     len(result['UserDetailList']) == 0 | ||||
|     len(result['GroupDetailList']) == 0 | ||||
|     len(result['Policies']) == 0 | ||||
|     assert len(result['RoleDetailList']) == 1 | ||||
|     assert len(result['UserDetailList']) == 0 | ||||
|     assert len(result['GroupDetailList']) == 0 | ||||
|     assert len(result['Policies']) == 0 | ||||
|     assert len(result['RoleDetailList'][0]['InstanceProfileList']) == 1 | ||||
| 
 | ||||
|     result = conn.get_account_authorization_details(Filter=['User']) | ||||
|     len(result['RoleDetailList']) == 0 | ||||
|     len(result['UserDetailList']) == 1 | ||||
|     len(result['GroupDetailList']) == 0 | ||||
|     len(result['Policies']) == 0 | ||||
|     assert len(result['RoleDetailList']) == 0 | ||||
|     assert len(result['UserDetailList']) == 1 | ||||
|     assert len(result['GroupDetailList']) == 0 | ||||
|     assert len(result['Policies']) == 0 | ||||
| 
 | ||||
|     result = conn.get_account_authorization_details(Filter=['Group']) | ||||
|     len(result['RoleDetailList']) == 0 | ||||
|     len(result['UserDetailList']) == 0 | ||||
|     len(result['GroupDetailList']) == 1 | ||||
|     len(result['Policies']) == 0 | ||||
|     assert len(result['RoleDetailList']) == 0 | ||||
|     assert len(result['UserDetailList']) == 0 | ||||
|     assert len(result['GroupDetailList']) == 1 | ||||
|     assert len(result['Policies']) == 0 | ||||
| 
 | ||||
|     result = conn.get_account_authorization_details(Filter=['LocalManagedPolicy']) | ||||
|     len(result['RoleDetailList']) == 0 | ||||
|     len(result['UserDetailList']) == 0 | ||||
|     len(result['GroupDetailList']) == 0 | ||||
|     len(result['Policies']) == 1 | ||||
|     assert len(result['RoleDetailList']) == 0 | ||||
|     assert len(result['UserDetailList']) == 0 | ||||
|     assert len(result['GroupDetailList']) == 0 | ||||
|     assert len(result['Policies']) == 1 | ||||
| 
 | ||||
|     # Check for greater than 1 since this should always be greater than one but might change. | ||||
|     # See iam/aws_managed_policies.py | ||||
|     result = conn.get_account_authorization_details(Filter=['AWSManagedPolicy']) | ||||
|     len(result['RoleDetailList']) == 0 | ||||
|     len(result['UserDetailList']) == 0 | ||||
|     len(result['GroupDetailList']) == 0 | ||||
|     len(result['Policies']) > 1 | ||||
|     assert len(result['RoleDetailList']) == 0 | ||||
|     assert len(result['UserDetailList']) == 0 | ||||
|     assert len(result['GroupDetailList']) == 0 | ||||
|     assert len(result['Policies']) > 1 | ||||
| 
 | ||||
|     result = conn.get_account_authorization_details() | ||||
|     len(result['RoleDetailList']) == 1 | ||||
|     len(result['UserDetailList']) == 1 | ||||
|     len(result['GroupDetailList']) == 1 | ||||
|     len(result['Policies']) > 1 | ||||
|     assert len(result['RoleDetailList']) == 1 | ||||
|     assert len(result['UserDetailList']) == 1 | ||||
|     assert len(result['GroupDetailList']) == 1 | ||||
|     assert len(result['Policies']) > 1 | ||||
| 
 | ||||
| 
 | ||||
| @mock_iam | ||||
| def test_signing_certs(): | ||||
|     client = boto3.client('iam', region_name='us-east-1') | ||||
| 
 | ||||
|     # Create the IAM user first: | ||||
|     client.create_user(UserName='testing') | ||||
| 
 | ||||
|     # Upload the cert: | ||||
|     resp = client.upload_signing_certificate(UserName='testing', CertificateBody=MOCK_CERT)['Certificate'] | ||||
|     cert_id = resp['CertificateId'] | ||||
| 
 | ||||
|     assert resp['UserName'] == 'testing' | ||||
|     assert resp['Status'] == 'Active' | ||||
|     assert resp['CertificateBody'] == MOCK_CERT | ||||
|     assert resp['CertificateId'] | ||||
| 
 | ||||
|     # Upload a the cert with an invalid body: | ||||
|     with assert_raises(ClientError) as ce: | ||||
|         client.upload_signing_certificate(UserName='testing', CertificateBody='notacert') | ||||
|     assert ce.exception.response['Error']['Code'] == 'MalformedCertificate' | ||||
| 
 | ||||
|     # Upload with an invalid user: | ||||
|     with assert_raises(ClientError): | ||||
|         client.upload_signing_certificate(UserName='notauser', CertificateBody=MOCK_CERT) | ||||
| 
 | ||||
|     # Update: | ||||
|     client.update_signing_certificate(UserName='testing', CertificateId=cert_id, Status='Inactive') | ||||
| 
 | ||||
|     with assert_raises(ClientError): | ||||
|         client.update_signing_certificate(UserName='notauser', CertificateId=cert_id, Status='Inactive') | ||||
| 
 | ||||
|     with assert_raises(ClientError) as ce: | ||||
|         client.update_signing_certificate(UserName='testing', CertificateId='x' * 32, Status='Inactive') | ||||
| 
 | ||||
|     assert ce.exception.response['Error']['Message'] == 'The Certificate with id {id} cannot be found.'.format( | ||||
|         id='x' * 32) | ||||
| 
 | ||||
|     # List the certs: | ||||
|     resp = client.list_signing_certificates(UserName='testing')['Certificates'] | ||||
|     assert len(resp) == 1 | ||||
|     assert resp[0]['CertificateBody'] == MOCK_CERT | ||||
|     assert resp[0]['Status'] == 'Inactive'  # Changed with the update call above. | ||||
| 
 | ||||
|     with assert_raises(ClientError): | ||||
|         client.list_signing_certificates(UserName='notauser') | ||||
| 
 | ||||
|     # Delete: | ||||
|     client.delete_signing_certificate(UserName='testing', CertificateId=cert_id) | ||||
| 
 | ||||
|     with assert_raises(ClientError): | ||||
|         client.delete_signing_certificate(UserName='notauser', CertificateId=cert_id) | ||||
| 
 | ||||
|     with assert_raises(ClientError) as ce: | ||||
|         client.delete_signing_certificate(UserName='testing', CertificateId=cert_id) | ||||
| 
 | ||||
|     assert ce.exception.response['Error']['Message'] == 'The Certificate with id {id} cannot be found.'.format( | ||||
|         id=cert_id) | ||||
| 
 | ||||
|     # Verify that it's not in the list: | ||||
|     resp = client.list_signing_certificates(UserName='testing') | ||||
|     assert not resp['Certificates'] | ||||
|  | ||||
| @ -1553,6 +1553,24 @@ def test_boto3_put_bucket_tagging(): | ||||
|     }) | ||||
|     resp['ResponseMetadata']['HTTPStatusCode'].should.equal(200) | ||||
| 
 | ||||
|     # With duplicate tag keys: | ||||
|     with assert_raises(ClientError) as err: | ||||
|         resp = s3.put_bucket_tagging(Bucket=bucket_name, | ||||
|                                      Tagging={ | ||||
|                                          "TagSet": [ | ||||
|                                              { | ||||
|                                                  "Key": "TagOne", | ||||
|                                                  "Value": "ValueOne" | ||||
|                                              }, | ||||
|                                              { | ||||
|                                                  "Key": "TagOne", | ||||
|                                                  "Value": "ValueOneAgain" | ||||
|                                              } | ||||
|                                          ] | ||||
|                                      }) | ||||
|     e = err.exception | ||||
|     e.response["Error"]["Code"].should.equal("InvalidTag") | ||||
|     e.response["Error"]["Message"].should.equal("Cannot provide multiple Tags with the same key") | ||||
| 
 | ||||
| @mock_s3 | ||||
| def test_boto3_get_bucket_tagging(): | ||||
|  | ||||
| @ -39,12 +39,28 @@ def test_create_secret(): | ||||
|     conn = boto3.client('secretsmanager', region_name='us-east-1') | ||||
| 
 | ||||
|     result = conn.create_secret(Name='test-secret', SecretString="foosecret") | ||||
|     assert result['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad') | ||||
|     assert result['ARN'] | ||||
|     assert result['Name'] == 'test-secret' | ||||
|     secret = conn.get_secret_value(SecretId='test-secret') | ||||
|     assert secret['SecretString'] == 'foosecret' | ||||
| 
 | ||||
| @mock_secretsmanager | ||||
| def test_create_secret_with_tags(): | ||||
|     conn = boto3.client('secretsmanager', region_name='us-east-1') | ||||
|     secret_name = 'test-secret-with-tags' | ||||
| 
 | ||||
|     result = conn.create_secret( | ||||
|         Name=secret_name, | ||||
|         SecretString="foosecret", | ||||
|         Tags=[{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}] | ||||
|     ) | ||||
|     assert result['ARN'] | ||||
|     assert result['Name'] == secret_name  | ||||
|     secret_value = conn.get_secret_value(SecretId=secret_name) | ||||
|     assert secret_value['SecretString'] == 'foosecret' | ||||
|     secret_details = conn.describe_secret(SecretId=secret_name) | ||||
|     assert secret_details['Tags'] == [{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}] | ||||
| 
 | ||||
| @mock_secretsmanager | ||||
| def test_get_random_password_default_length(): | ||||
|     conn = boto3.client('secretsmanager', region_name='us-west-2') | ||||
| @ -159,10 +175,17 @@ def test_describe_secret(): | ||||
|     conn.create_secret(Name='test-secret', | ||||
|                        SecretString='foosecret') | ||||
|      | ||||
|     conn.create_secret(Name='test-secret-2', | ||||
|                        SecretString='barsecret') | ||||
|      | ||||
|     secret_description = conn.describe_secret(SecretId='test-secret') | ||||
|     secret_description_2 = conn.describe_secret(SecretId='test-secret-2') | ||||
| 
 | ||||
|     assert secret_description   # Returned dict is not empty | ||||
|     assert secret_description['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-west-2:1234567890:secret:test-secret-rIjad') | ||||
|     assert secret_description['Name'] == ('test-secret') | ||||
|     assert secret_description['ARN'] != '' # Test arn not empty | ||||
|     assert secret_description_2['Name'] == ('test-secret-2') | ||||
|     assert secret_description_2['ARN'] != '' # Test arn not empty | ||||
| 
 | ||||
| @mock_secretsmanager | ||||
| def test_describe_secret_that_does_not_exist(): | ||||
| @ -190,9 +213,7 @@ def test_rotate_secret(): | ||||
|     rotated_secret = conn.rotate_secret(SecretId=secret_name) | ||||
| 
 | ||||
|     assert rotated_secret | ||||
|     assert rotated_secret['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-west-2:1234567890:secret:test-secret-rIjad' | ||||
|     ) | ||||
|     assert rotated_secret['ARN'] != '' # Test arn not empty | ||||
|     assert rotated_secret['Name'] == secret_name | ||||
|     assert rotated_secret['VersionId'] != '' | ||||
| 
 | ||||
|  | ||||
| @ -82,11 +82,20 @@ def test_create_secret(): | ||||
|                            headers={ | ||||
|                                "X-Amz-Target": "secretsmanager.CreateSecret"}, | ||||
|                            ) | ||||
|     res_2 = test_client.post('/', | ||||
|                            data={"Name": "test-secret-2", | ||||
|                                  "SecretString": "bar-secret"}, | ||||
|                            headers={ | ||||
|                                "X-Amz-Target": "secretsmanager.CreateSecret"}, | ||||
|                            ) | ||||
| 
 | ||||
|     json_data = json.loads(res.data.decode("utf-8")) | ||||
|     assert json_data['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad') | ||||
|     assert json_data['ARN'] != '' | ||||
|     assert json_data['Name'] == 'test-secret' | ||||
|      | ||||
|     json_data_2 = json.loads(res_2.data.decode("utf-8")) | ||||
|     assert json_data_2['ARN'] != '' | ||||
|     assert json_data_2['Name'] == 'test-secret-2' | ||||
| 
 | ||||
| @mock_secretsmanager | ||||
| def test_describe_secret(): | ||||
| @ -107,12 +116,30 @@ def test_describe_secret(): | ||||
|                             "X-Amz-Target": "secretsmanager.DescribeSecret" | ||||
|                         }, | ||||
|                     ) | ||||
|      | ||||
|     create_secret_2 = test_client.post('/', | ||||
|                         data={"Name": "test-secret-2", | ||||
|                               "SecretString": "barsecret"}, | ||||
|                         headers={ | ||||
|                             "X-Amz-Target": "secretsmanager.CreateSecret" | ||||
|                         }, | ||||
|                     ) | ||||
|     describe_secret_2 = test_client.post('/', | ||||
|                         data={"SecretId": "test-secret-2"}, | ||||
|                         headers={ | ||||
|                             "X-Amz-Target": "secretsmanager.DescribeSecret" | ||||
|                         }, | ||||
|                     ) | ||||
| 
 | ||||
|     json_data = json.loads(describe_secret.data.decode("utf-8")) | ||||
|     assert json_data   # Returned dict is not empty | ||||
|     assert json_data['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad' | ||||
|     ) | ||||
|     assert json_data['ARN'] != '' | ||||
|     assert json_data['Name'] == 'test-secret' | ||||
|      | ||||
|     json_data_2 = json.loads(describe_secret_2.data.decode("utf-8")) | ||||
|     assert json_data_2   # Returned dict is not empty | ||||
|     assert json_data_2['ARN'] != '' | ||||
|     assert json_data_2['Name'] == 'test-secret-2' | ||||
| 
 | ||||
| @mock_secretsmanager | ||||
| def test_describe_secret_that_does_not_exist(): | ||||
| @ -179,9 +206,7 @@ def test_rotate_secret(): | ||||
| 
 | ||||
|     json_data = json.loads(rotate_secret.data.decode("utf-8")) | ||||
|     assert json_data   # Returned dict is not empty | ||||
|     assert json_data['ARN'] == ( | ||||
|         'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad' | ||||
|     ) | ||||
|     assert json_data['ARN'] != '' | ||||
|     assert json_data['Name'] == 'test-secret' | ||||
|     assert json_data['VersionId'] == client_request_token | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user