Merge branch 'master' into policy-version-fixes

This commit is contained in:
Steve Pulec 2019-07-01 20:58:38 -05:00 committed by GitHub
commit 172e089854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2773 additions and 206 deletions

View File

@ -2,21 +2,18 @@ dist: xenial
language: python language: python
sudo: false sudo: false
services: services:
- docker - docker
python: python:
- 2.7 - 2.7
- 3.6 - 3.6
- 3.7 - 3.7
env: env:
- TEST_SERVER_MODE=false - TEST_SERVER_MODE=false
- TEST_SERVER_MODE=true - TEST_SERVER_MODE=true
before_install: before_install:
- export BOTO_CONFIG=/dev/null - export BOTO_CONFIG=/dev/null
install: 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
# Python images come from here: https://hub.docker.com/_/python/
- |
python setup.py sdist python setup.py sdist
if [ "$TEST_SERVER_MODE" = "true" ]; then if [ "$TEST_SERVER_MODE" = "true" ]; then
@ -32,6 +29,29 @@ install:
python wait_for.py python wait_for.py
fi fi
script: script:
- make test - make test
after_success: after_success:
- coveralls - coveralls
before_deploy:
- git checkout $TRAVIS_BRANCH
- git fetch --unshallow
- python update_version_from_git.py
deploy:
- provider: pypi
distributions: sdist bdist_wheel
user: spulec
password:
secure: NxnPylnTfekJmGyoufCw0lMoYRskSMJzvAIyAlJJVYKwEhmiCPOrdy5qV8i8mRZ1AkUsqU3jBZ/PD56n96clHW0E3d080UleRDj6JpyALVdeLfMqZl9kLmZ8bqakWzYq3VSJKw2zGP/L4tPGf8wTK1SUv9yl/YNDsBdCkjDverw=
on:
branch:
- master
skip_cleanup: true
skip_existing: true
- provider: pypi
distributions: sdist bdist_wheel
user: spulec
password:
secure: NxnPylnTfekJmGyoufCw0lMoYRskSMJzvAIyAlJJVYKwEhmiCPOrdy5qV8i8mRZ1AkUsqU3jBZ/PD56n96clHW0E3d080UleRDj6JpyALVdeLfMqZl9kLmZ8bqakWzYq3VSJKw2zGP/L4tPGf8wTK1SUv9yl/YNDsBdCkjDverw=
on:
tags: true
skip_existing: true

106
README.md
View File

@ -55,95 +55,95 @@ With the decorator wrapping the test, all the calls to s3 are automatically mock
It gets even better! Moto isn't just for Python code and it isn't just for S3. Look at the [standalone server mode](https://github.com/spulec/moto#stand-alone-server-mode) for more information about running Moto with other languages. Here's the status of the other AWS services implemented: It gets even better! Moto isn't just for Python code and it isn't just for S3. Look at the [standalone server mode](https://github.com/spulec/moto#stand-alone-server-mode) for more information about running Moto with other languages. Here's the status of the other AWS services implemented:
```gherkin ```gherkin
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Service Name | Decorator | Development Status | | Service Name | Decorator | Development Status |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| ACM | @mock_acm | all endpoints done | | ACM | @mock_acm | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| API Gateway | @mock_apigateway | core endpoints done | | API Gateway | @mock_apigateway | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Autoscaling | @mock_autoscaling| core endpoints done | | Autoscaling | @mock_autoscaling | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Cloudformation | @mock_cloudformation| core endpoints done | | Cloudformation | @mock_cloudformation | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Cloudwatch | @mock_cloudwatch | basic endpoints done | | Cloudwatch | @mock_cloudwatch | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| CloudwatchEvents | @mock_events | all endpoints done | | CloudwatchEvents | @mock_events | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Cognito Identity | @mock_cognitoidentity| basic endpoints done | | Cognito Identity | @mock_cognitoidentity | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Cognito Identity Provider | @mock_cognitoidp| basic endpoints done | | Cognito Identity Provider | @mock_cognitoidp | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Config | @mock_config | basic endpoints done | | Config | @mock_config | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Data Pipeline | @mock_datapipeline| basic endpoints done | | Data Pipeline | @mock_datapipeline | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| DynamoDB | @mock_dynamodb | core endpoints done | | DynamoDB | @mock_dynamodb | core endpoints done |
| DynamoDB2 | @mock_dynamodb2 | all endpoints + partial indexes | | DynamoDB2 | @mock_dynamodb2 | all endpoints + partial indexes |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| EC2 | @mock_ec2 | core endpoints done | | EC2 | @mock_ec2 | core endpoints done |
| - AMI | | core endpoints done | | - AMI | | core endpoints done |
| - EBS | | core endpoints done | | - EBS | | core endpoints done |
| - Instances | | all endpoints done | | - Instances | | all endpoints done |
| - Security Groups | | core endpoints done | | - Security Groups | | core endpoints done |
| - Tags | | all endpoints done | | - Tags | | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| ECR | @mock_ecr | basic endpoints done | | ECR | @mock_ecr | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| ECS | @mock_ecs | basic endpoints done | | ECS | @mock_ecs | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| ELB | @mock_elb | core endpoints done | | ELB | @mock_elb | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| ELBv2 | @mock_elbv2 | all endpoints done | | ELBv2 | @mock_elbv2 | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| EMR | @mock_emr | core endpoints done | | EMR | @mock_emr | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Glacier | @mock_glacier | core endpoints done | | Glacier | @mock_glacier | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| IAM | @mock_iam | core endpoints done | | IAM | @mock_iam | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| IoT | @mock_iot | core endpoints done | | IoT | @mock_iot | core endpoints done |
| | @mock_iotdata | core endpoints done | | | @mock_iotdata | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Kinesis | @mock_kinesis | core endpoints done |
|-------------------------------------------------------------------------------------|
| KMS | @mock_kms | basic endpoints done |
|-------------------------------------------------------------------------------------|
| Lambda | @mock_lambda | basic endpoints done, requires | | Lambda | @mock_lambda | basic endpoints done, requires |
| | | docker | | | | docker |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Logs | @mock_logs | basic endpoints done | | Logs | @mock_logs | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Kinesis | @mock_kinesis | core endpoints done |
|------------------------------------------------------------------------------|
| KMS | @mock_kms | basic endpoints done |
|------------------------------------------------------------------------------|
| Organizations | @mock_organizations | some core endpoints done | | Organizations | @mock_organizations | some core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Polly | @mock_polly | all endpoints done | | Polly | @mock_polly | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| RDS | @mock_rds | core endpoints done | | RDS | @mock_rds | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| RDS2 | @mock_rds2 | core endpoints done | | RDS2 | @mock_rds2 | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Redshift | @mock_redshift | core endpoints done | | Redshift | @mock_redshift | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| Route53 | @mock_route53 | core endpoints done | | Route53 | @mock_route53 | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| S3 | @mock_s3 | core endpoints done | | S3 | @mock_s3 | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SecretsManager | @mock_secretsmanager | basic endpoints done | SecretsManager | @mock_secretsmanager | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SES | @mock_ses | all endpoints done | | SES | @mock_ses | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SNS | @mock_sns | all endpoints done | | SNS | @mock_sns | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SQS | @mock_sqs | core endpoints done | | SQS | @mock_sqs | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SSM | @mock_ssm | core endpoints done | | SSM | @mock_ssm | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| STS | @mock_sts | core endpoints done | | STS | @mock_sts | core endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| SWF | @mock_swf | basic endpoints done | | SWF | @mock_swf | basic endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
| X-Ray | @mock_xray | all endpoints done | | X-Ray | @mock_xray | all endpoints done |
|------------------------------------------------------------------------------| |-------------------------------------------------------------------------------------|
``` ```
For a full list of endpoint [implementation coverage](https://github.com/spulec/moto/blob/master/IMPLEMENTATION_COVERAGE.md) For a full list of endpoint [implementation coverage](https://github.com/spulec/moto/blob/master/IMPLEMENTATION_COVERAGE.md)
@ -318,3 +318,11 @@ boto3.resource(
```console ```console
$ pip install moto $ pip install moto
``` ```
## Releases
Releases are done from travisci. Fairly closely following this:
https://docs.travis-ci.com/user/deployment/pypi/
- Commits to `master` branch do a dev deploy to pypi.
- Commits to a tag do a real deploy to pypi.

View File

@ -17,66 +17,95 @@ with ``moto`` and its usage.
Currently implemented Services: Currently implemented Services:
------------------------------- -------------------------------
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Service Name | Decorator | Development Status | | Service Name | Decorator | Development Status |
+=======================+=====================+===================================+ +===========================+=======================+====================================+
| ACM | @mock_acm | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| API Gateway | @mock_apigateway | core endpoints done | | API Gateway | @mock_apigateway | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Autoscaling | @mock_autoscaling | core endpoints done | | Autoscaling | @mock_autoscaling | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Cloudformation | @mock_cloudformation| core endpoints done | | Cloudformation | @mock_cloudformation | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Cloudwatch | @mock_cloudwatch | basic endpoints done | | Cloudwatch | @mock_cloudwatch | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| CloudwatchEvents | @mock_events | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Cognito Identity | @mock_cognitoidentity | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Cognito Identity Provider | @mock_cognitoidp | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Config | @mock_config | basic endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Data Pipeline | @mock_datapipeline | basic endpoints done | | Data Pipeline | @mock_datapipeline | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| - DynamoDB | - @mock_dynamodb | - core endpoints done | | DynamoDB | - @mock_dynamodb | - core endpoints done |
| - DynamoDB2 | - @mock_dynamodb2 | - core endpoints + partial indexes| | DynamoDB2 | - @mock_dynamodb2 | - core endpoints + partial indexes |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| EC2 | @mock_ec2 | core endpoints done | | EC2 | @mock_ec2 | core endpoints done |
| - AMI | | - core endpoints done | | - AMI | | - core endpoints done |
| - EBS | | - core endpoints done | | - EBS | | - core endpoints done |
| - Instances | | - all endpoints done | | - Instances | | - all endpoints done |
| - Security Groups | | - core endpoints done | | - Security Groups | | - core endpoints done |
| - Tags | | - all endpoints done | | - Tags | | - all endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| ECR | @mock_ecr | basic endpoints done |
+---------------------------+-----------------------+------------------------------------+
| ECS | @mock_ecs | basic endpoints done | | ECS | @mock_ecs | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| ELB | @mock_elb | core endpoints done | | ELB | @mock_elb | core endpoints done |
| | @mock_elbv2 | core endpoints done | +---------------------------+-----------------------+------------------------------------+
+-----------------------+---------------------+-----------------------------------+ | ELBv2 | @mock_elbv2 | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| EMR | @mock_emr | core endpoints done | | EMR | @mock_emr | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Glacier | @mock_glacier | core endpoints done | | Glacier | @mock_glacier | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| IAM | @mock_iam | core endpoints done | | IAM | @mock_iam | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Lambda | @mock_lambda | basic endpoints done | | IoT | @mock_iot | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ | | @mock_iotdata | core endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Kinesis | @mock_kinesis | core endpoints done | | Kinesis | @mock_kinesis | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| KMS | @mock_kms | basic endpoints done | | KMS | @mock_kms | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Lambda | @mock_lambda | basic endpoints done, |
| | | requires docker |
+---------------------------+-----------------------+------------------------------------+
| Logs | @mock_logs | basic endpoints done |
+---------------------------+-----------------------+------------------------------------+
| Organizations | @mock_organizations | some core edpoints done |
+---------------------------+-----------------------+------------------------------------+
| Polly | @mock_polly | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| RDS | @mock_rds | core endpoints done | | RDS | @mock_rds | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| RDS2 | @mock_rds2 | core endpoints done | | RDS2 | @mock_rds2 | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Redshift | @mock_redshift | core endpoints done | | Redshift | @mock_redshift | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| Route53 | @mock_route53 | core endpoints done | | Route53 | @mock_route53 | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| S3 | @mock_s3 | core endpoints done | | S3 | @mock_s3 | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| SES | @mock_ses | core endpoints done | | SecretsManager | @mock_secretsmanager | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| SNS | @mock_sns | core endpoints done | | SES | @mock_ses | all endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| SNS | @mock_sns | all endpoints done |
+---------------------------+-----------------------+------------------------------------+
| SQS | @mock_sqs | core endpoints done | | SQS | @mock_sqs | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| SSM | @mock_ssm | core endpoints done |
+---------------------------+-----------------------+------------------------------------+
| STS | @mock_sts | core endpoints done | | STS | @mock_sts | core endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| SWF | @mock_swf | basic endpoints done | | SWF | @mock_swf | basic endpoints done |
+-----------------------+---------------------+-----------------------------------+ +---------------------------+-----------------------+------------------------------------+
| X-Ray | @mock_xray | all endpoints done |
+---------------------------+-----------------------+------------------------------------+

View File

@ -3,7 +3,7 @@ import logging
# logging.getLogger('boto').setLevel(logging.CRITICAL) # logging.getLogger('boto').setLevel(logging.CRITICAL)
__title__ = 'moto' __title__ = 'moto'
__version__ = '1.3.8' __version__ = '1.3.9'
from .acm import mock_acm # flake8: noqa from .acm import mock_acm # flake8: noqa
from .apigateway import mock_apigateway, mock_apigateway_deprecated # flake8: noqa from .apigateway import mock_apigateway, mock_apigateway_deprecated # flake8: noqa

View File

@ -42,6 +42,14 @@ class MalformedCertificate(RESTError):
'MalformedCertificate', 'Certificate {cert} is malformed'.format(cert=cert)) 'MalformedCertificate', 'Certificate {cert} is malformed'.format(cert=cert))
class MalformedPolicyDocument(RESTError):
code = 400
def __init__(self, message=""):
super(MalformedPolicyDocument, self).__init__(
'MalformedPolicyDocument', message)
class DuplicateTags(RESTError): class DuplicateTags(RESTError):
code = 400 code = 400

View File

@ -11,6 +11,7 @@ from cryptography.hazmat.backends import default_backend
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_without_milliseconds, iso_8601_datetime_with_milliseconds from moto.core.utils import iso_8601_datetime_without_milliseconds, iso_8601_datetime_with_milliseconds
from moto.iam.policy_validation import IAMPolicyDocumentValidator
from .aws_managed_policies import aws_managed_policies_data from .aws_managed_policies import aws_managed_policies_data
from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, IAMLimitExceededException, \ from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, IAMLimitExceededException, \
@ -575,6 +576,9 @@ class IAMBackend(BaseBackend):
policy.detach_from(self.get_user(user_name)) policy.detach_from(self.get_user(user_name))
def create_policy(self, description, path, policy_document, policy_name): def create_policy(self, description, path, policy_document, policy_name):
iam_policy_document_validator = IAMPolicyDocumentValidator(policy_document)
iam_policy_document_validator.validate()
policy = ManagedPolicy( policy = ManagedPolicy(
policy_name, policy_name,
description=description, description=description,
@ -667,6 +671,9 @@ class IAMBackend(BaseBackend):
def put_role_policy(self, role_name, policy_name, policy_json): def put_role_policy(self, role_name, policy_name, policy_json):
role = self.get_role(role_name) role = self.get_role(role_name)
iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
iam_policy_document_validator.validate()
role.put_policy(policy_name, policy_json) role.put_policy(policy_name, policy_json)
def delete_role_policy(self, role_name, policy_name): def delete_role_policy(self, role_name, policy_name):
@ -764,6 +771,9 @@ class IAMBackend(BaseBackend):
role.tags.pop(ref_key, None) role.tags.pop(ref_key, None)
def create_policy_version(self, policy_arn, policy_document, set_as_default): def create_policy_version(self, policy_arn, policy_document, set_as_default):
iam_policy_document_validator = IAMPolicyDocumentValidator(policy_document)
iam_policy_document_validator.validate()
policy = self.get_policy(policy_arn) policy = self.get_policy(policy_arn)
if not policy: if not policy:
raise IAMNotFoundException("Policy not found") raise IAMNotFoundException("Policy not found")
@ -911,6 +921,9 @@ class IAMBackend(BaseBackend):
def put_group_policy(self, group_name, policy_name, policy_json): def put_group_policy(self, group_name, policy_name, policy_json):
group = self.get_group(group_name) group = self.get_group(group_name)
iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
iam_policy_document_validator.validate()
group.put_policy(policy_name, policy_json) group.put_policy(policy_name, policy_json)
def list_group_policies(self, group_name, marker=None, max_items=None): def list_group_policies(self, group_name, marker=None, max_items=None):
@ -1071,6 +1084,9 @@ class IAMBackend(BaseBackend):
def put_user_policy(self, user_name, policy_name, policy_json): def put_user_policy(self, user_name, policy_name, policy_json):
user = self.get_user(user_name) user = self.get_user(user_name)
iam_policy_document_validator = IAMPolicyDocumentValidator(policy_json)
iam_policy_document_validator.validate()
user.put_policy(policy_name, policy_json) user.put_policy(policy_name, policy_json)
def delete_user_policy(self, user_name, policy_name): def delete_user_policy(self, user_name, policy_name):

View File

@ -0,0 +1,450 @@
import json
import re
from six import string_types
from moto.iam.exceptions import MalformedPolicyDocument
VALID_TOP_ELEMENTS = [
"Version",
"Id",
"Statement",
"Conditions"
]
VALID_VERSIONS = [
"2008-10-17",
"2012-10-17"
]
VALID_STATEMENT_ELEMENTS = [
"Sid",
"Action",
"NotAction",
"Resource",
"NotResource",
"Effect",
"Condition"
]
VALID_EFFECTS = [
"Allow",
"Deny"
]
VALID_CONDITIONS = [
"StringEquals",
"StringNotEquals",
"StringEqualsIgnoreCase",
"StringNotEqualsIgnoreCase",
"StringLike",
"StringNotLike",
"NumericEquals",
"NumericNotEquals",
"NumericLessThan",
"NumericLessThanEquals",
"NumericGreaterThan",
"NumericGreaterThanEquals",
"DateEquals",
"DateNotEquals",
"DateLessThan",
"DateLessThanEquals",
"DateGreaterThan",
"DateGreaterThanEquals",
"Bool",
"BinaryEquals",
"IpAddress",
"NotIpAddress",
"ArnEquals",
"ArnLike",
"ArnNotEquals",
"ArnNotLike",
"Null"
]
VALID_CONDITION_PREFIXES = [
"ForAnyValue:",
"ForAllValues:"
]
VALID_CONDITION_POSTFIXES = [
"IfExists"
]
SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS = {
"iam": 'IAM resource {resource} cannot contain region information.',
"s3": 'Resource {resource} can not contain region information.'
}
VALID_RESOURCE_PATH_STARTING_VALUES = {
"iam": {
"values": ["user/", "federated-user/", "role/", "group/", "instance-profile/", "mfa/", "server-certificate/",
"policy/", "sms-mfa/", "saml-provider/", "oidc-provider/", "report/", "access-report/"],
"error_message": 'IAM resource path must either be "*" or start with {values}.'
}
}
class IAMPolicyDocumentValidator:
def __init__(self, policy_document):
self._policy_document = policy_document
self._policy_json = {}
self._statements = []
self._resource_error = "" # the first resource error found that does not generate a legacy parsing error
def validate(self):
try:
self._validate_syntax()
except Exception:
raise MalformedPolicyDocument("Syntax errors in policy.")
try:
self._validate_version()
except Exception:
raise MalformedPolicyDocument("Policy document must be version 2012-10-17 or greater.")
try:
self._perform_first_legacy_parsing()
self._validate_resources_for_formats()
self._validate_not_resources_for_formats()
except Exception:
raise MalformedPolicyDocument("The policy failed legacy parsing")
try:
self._validate_sid_uniqueness()
except Exception:
raise MalformedPolicyDocument("Statement IDs (SID) in a single policy must be unique.")
try:
self._validate_action_like_exist()
except Exception:
raise MalformedPolicyDocument("Policy statement must contain actions.")
try:
self._validate_resource_exist()
except Exception:
raise MalformedPolicyDocument("Policy statement must contain resources.")
if self._resource_error != "":
raise MalformedPolicyDocument(self._resource_error)
self._validate_actions_for_prefixes()
self._validate_not_actions_for_prefixes()
def _validate_syntax(self):
self._policy_json = json.loads(self._policy_document)
assert isinstance(self._policy_json, dict)
self._validate_top_elements()
self._validate_version_syntax()
self._validate_id_syntax()
self._validate_statements_syntax()
def _validate_top_elements(self):
top_elements = self._policy_json.keys()
for element in top_elements:
assert element in VALID_TOP_ELEMENTS
def _validate_version_syntax(self):
if "Version" in self._policy_json:
assert self._policy_json["Version"] in VALID_VERSIONS
def _validate_version(self):
assert self._policy_json["Version"] == "2012-10-17"
def _validate_sid_uniqueness(self):
sids = []
for statement in self._statements:
if "Sid" in statement:
assert statement["Sid"] not in sids
sids.append(statement["Sid"])
def _validate_statements_syntax(self):
assert "Statement" in self._policy_json
assert isinstance(self._policy_json["Statement"], (dict, list))
if isinstance(self._policy_json["Statement"], dict):
self._statements.append(self._policy_json["Statement"])
else:
self._statements += self._policy_json["Statement"]
assert self._statements
for statement in self._statements:
self._validate_statement_syntax(statement)
@staticmethod
def _validate_statement_syntax(statement):
assert isinstance(statement, dict)
for statement_element in statement.keys():
assert statement_element in VALID_STATEMENT_ELEMENTS
assert ("Resource" not in statement or "NotResource" not in statement)
assert ("Action" not in statement or "NotAction" not in statement)
IAMPolicyDocumentValidator._validate_effect_syntax(statement)
IAMPolicyDocumentValidator._validate_action_syntax(statement)
IAMPolicyDocumentValidator._validate_not_action_syntax(statement)
IAMPolicyDocumentValidator._validate_resource_syntax(statement)
IAMPolicyDocumentValidator._validate_not_resource_syntax(statement)
IAMPolicyDocumentValidator._validate_condition_syntax(statement)
IAMPolicyDocumentValidator._validate_sid_syntax(statement)
@staticmethod
def _validate_effect_syntax(statement):
assert "Effect" in statement
assert isinstance(statement["Effect"], string_types)
assert statement["Effect"].lower() in [allowed_effect.lower() for allowed_effect in VALID_EFFECTS]
@staticmethod
def _validate_action_syntax(statement):
IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "Action")
@staticmethod
def _validate_not_action_syntax(statement):
IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "NotAction")
@staticmethod
def _validate_resource_syntax(statement):
IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "Resource")
@staticmethod
def _validate_not_resource_syntax(statement):
IAMPolicyDocumentValidator._validate_string_or_list_of_strings_syntax(statement, "NotResource")
@staticmethod
def _validate_string_or_list_of_strings_syntax(statement, key):
if key in statement:
assert isinstance(statement[key], (string_types, list))
if isinstance(statement[key], list):
for resource in statement[key]:
assert isinstance(resource, string_types)
@staticmethod
def _validate_condition_syntax(statement):
if "Condition" in statement:
assert isinstance(statement["Condition"], dict)
for condition_key, condition_value in statement["Condition"].items():
assert isinstance(condition_value, dict)
for condition_element_key, condition_element_value in condition_value.items():
assert isinstance(condition_element_value, (list, string_types))
if IAMPolicyDocumentValidator._strip_condition_key(condition_key) not in VALID_CONDITIONS:
assert not condition_value # empty dict
@staticmethod
def _strip_condition_key(condition_key):
for valid_prefix in VALID_CONDITION_PREFIXES:
if condition_key.startswith(valid_prefix):
condition_key = condition_key[len(valid_prefix):]
break # strip only the first match
for valid_postfix in VALID_CONDITION_POSTFIXES:
if condition_key.endswith(valid_postfix):
condition_key = condition_key[:-len(valid_postfix)]
break # strip only the first match
return condition_key
@staticmethod
def _validate_sid_syntax(statement):
if "Sid" in statement:
assert isinstance(statement["Sid"], string_types)
def _validate_id_syntax(self):
if "Id" in self._policy_json:
assert isinstance(self._policy_json["Id"], string_types)
def _validate_resource_exist(self):
for statement in self._statements:
assert ("Resource" in statement or "NotResource" in statement)
if "Resource" in statement and isinstance(statement["Resource"], list):
assert statement["Resource"]
elif "NotResource" in statement and isinstance(statement["NotResource"], list):
assert statement["NotResource"]
def _validate_action_like_exist(self):
for statement in self._statements:
assert ("Action" in statement or "NotAction" in statement)
if "Action" in statement and isinstance(statement["Action"], list):
assert statement["Action"]
elif "NotAction" in statement and isinstance(statement["NotAction"], list):
assert statement["NotAction"]
def _validate_actions_for_prefixes(self):
self._validate_action_like_for_prefixes("Action")
def _validate_not_actions_for_prefixes(self):
self._validate_action_like_for_prefixes("NotAction")
def _validate_action_like_for_prefixes(self, key):
for statement in self._statements:
if key in statement:
if isinstance(statement[key], string_types):
self._validate_action_prefix(statement[key])
else:
for action in statement[key]:
self._validate_action_prefix(action)
@staticmethod
def _validate_action_prefix(action):
action_parts = action.split(":")
if len(action_parts) == 1 and action_parts[0] != "*":
raise MalformedPolicyDocument("Actions/Conditions must be prefaced by a vendor, e.g., iam, sdb, ec2, etc.")
elif len(action_parts) > 2:
raise MalformedPolicyDocument("Actions/Condition can contain only one colon.")
vendor_pattern = re.compile(r'[^a-zA-Z0-9\-.]')
if action_parts[0] != "*" and vendor_pattern.search(action_parts[0]):
raise MalformedPolicyDocument("Vendor {vendor} is not valid".format(vendor=action_parts[0]))
def _validate_resources_for_formats(self):
self._validate_resource_like_for_formats("Resource")
def _validate_not_resources_for_formats(self):
self._validate_resource_like_for_formats("NotResource")
def _validate_resource_like_for_formats(self, key):
for statement in self._statements:
if key in statement:
if isinstance(statement[key], string_types):
self._validate_resource_format(statement[key])
else:
for resource in sorted(statement[key], reverse=True):
self._validate_resource_format(resource)
if self._resource_error == "":
IAMPolicyDocumentValidator._legacy_parse_resource_like(statement, key)
def _validate_resource_format(self, resource):
if resource != "*":
resource_partitions = resource.partition(":")
if resource_partitions[1] == "":
self._resource_error = 'Resource {resource} must be in ARN format or "*".'.format(resource=resource)
return
resource_partitions = resource_partitions[2].partition(":")
if resource_partitions[0] != "aws":
remaining_resource_parts = resource_partitions[2].split(":")
arn1 = remaining_resource_parts[0] if remaining_resource_parts[0] != "" or len(remaining_resource_parts) > 1 else "*"
arn2 = remaining_resource_parts[1] if len(remaining_resource_parts) > 1 else "*"
arn3 = remaining_resource_parts[2] if len(remaining_resource_parts) > 2 else "*"
arn4 = ":".join(remaining_resource_parts[3:]) if len(remaining_resource_parts) > 3 else "*"
self._resource_error = 'Partition "{partition}" is not valid for resource "arn:{partition}:{arn1}:{arn2}:{arn3}:{arn4}".'.format(
partition=resource_partitions[0],
arn1=arn1,
arn2=arn2,
arn3=arn3,
arn4=arn4
)
return
if resource_partitions[1] != ":":
self._resource_error = "Resource vendor must be fully qualified and cannot contain regexes."
return
resource_partitions = resource_partitions[2].partition(":")
service = resource_partitions[0]
if service in SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS.keys() and not resource_partitions[2].startswith(":"):
self._resource_error = SERVICE_TYPE_REGION_INFORMATION_ERROR_ASSOCIATIONS[service].format(resource=resource)
return
resource_partitions = resource_partitions[2].partition(":")
resource_partitions = resource_partitions[2].partition(":")
if service in VALID_RESOURCE_PATH_STARTING_VALUES.keys():
valid_start = False
for valid_starting_value in VALID_RESOURCE_PATH_STARTING_VALUES[service]["values"]:
if resource_partitions[2].startswith(valid_starting_value):
valid_start = True
break
if not valid_start:
self._resource_error = VALID_RESOURCE_PATH_STARTING_VALUES[service]["error_message"].format(
values=", ".join(VALID_RESOURCE_PATH_STARTING_VALUES[service]["values"])
)
def _perform_first_legacy_parsing(self):
"""This method excludes legacy parsing resources, since that have to be done later."""
for statement in self._statements:
self._legacy_parse_statement(statement)
@staticmethod
def _legacy_parse_statement(statement):
assert statement["Effect"] in VALID_EFFECTS # case-sensitive matching
if "Condition" in statement:
for condition_key, condition_value in statement["Condition"].items():
IAMPolicyDocumentValidator._legacy_parse_condition(condition_key, condition_value)
@staticmethod
def _legacy_parse_resource_like(statement, key):
if isinstance(statement[key], string_types):
if statement[key] != "*":
assert statement[key].count(":") >= 5 or "::" not in statement[key]
assert statement[key].split(":")[2] != ""
else: # list
for resource in statement[key]:
if resource != "*":
assert resource.count(":") >= 5 or "::" not in resource
assert resource[2] != ""
@staticmethod
def _legacy_parse_condition(condition_key, condition_value):
stripped_condition_key = IAMPolicyDocumentValidator._strip_condition_key(condition_key)
if stripped_condition_key.startswith("Date"):
for condition_element_key, condition_element_value in condition_value.items():
if isinstance(condition_element_value, string_types):
IAMPolicyDocumentValidator._legacy_parse_date_condition_value(condition_element_value)
else: # it has to be a list
for date_condition_value in condition_element_value:
IAMPolicyDocumentValidator._legacy_parse_date_condition_value(date_condition_value)
@staticmethod
def _legacy_parse_date_condition_value(date_condition_value):
if "t" in date_condition_value.lower() or "-" in date_condition_value:
IAMPolicyDocumentValidator._validate_iso_8601_datetime(date_condition_value.lower())
else: # timestamp
assert 0 <= int(date_condition_value) <= 9223372036854775807
@staticmethod
def _validate_iso_8601_datetime(datetime):
datetime_parts = datetime.partition("t")
negative_year = datetime_parts[0].startswith("-")
date_parts = datetime_parts[0][1:].split("-") if negative_year else datetime_parts[0].split("-")
year = "-" + date_parts[0] if negative_year else date_parts[0]
assert -292275054 <= int(year) <= 292278993
if len(date_parts) > 1:
month = date_parts[1]
assert 1 <= int(month) <= 12
if len(date_parts) > 2:
day = date_parts[2]
assert 1 <= int(day) <= 31
assert len(date_parts) < 4
time_parts = datetime_parts[2].split(":")
if time_parts[0] != "":
hours = time_parts[0]
assert 0 <= int(hours) <= 23
if len(time_parts) > 1:
minutes = time_parts[1]
assert 0 <= int(minutes) <= 59
if len(time_parts) > 2:
if "z" in time_parts[2]:
seconds_with_decimal_fraction = time_parts[2].partition("z")[0]
assert time_parts[2].partition("z")[2] == ""
elif "+" in time_parts[2]:
seconds_with_decimal_fraction = time_parts[2].partition("+")[0]
time_zone_data = time_parts[2].partition("+")[2].partition(":")
time_zone_hours = time_zone_data[0]
assert len(time_zone_hours) == 2
assert 0 <= int(time_zone_hours) <= 23
if time_zone_data[1] == ":":
time_zone_minutes = time_zone_data[2]
assert len(time_zone_minutes) == 2
assert 0 <= int(time_zone_minutes) <= 59
else:
seconds_with_decimal_fraction = time_parts[2]
seconds_with_decimal_fraction_partition = seconds_with_decimal_fraction.partition(".")
seconds = seconds_with_decimal_fraction_partition[0]
assert 0 <= int(seconds) <= 59
if seconds_with_decimal_fraction_partition[1] == ".":
decimal_seconds = seconds_with_decimal_fraction_partition[2]
assert 0 <= int(decimal_seconds) <= 999999999

View File

@ -18,6 +18,15 @@ def read(*parts):
return fp.read() return fp.read()
def get_version():
version_file = read('moto', '__init__.py')
version_match = re.search(r'^__version__ = [\'"]([^\'"]*)[\'"]',
version_file, re.MULTILINE)
if version_match:
return version_match.group(1)
raise RuntimeError('Unable to find version string.')
install_requires = [ install_requires = [
"Jinja2>=2.10.1", "Jinja2>=2.10.1",
"boto>=2.36.0", "boto>=2.36.0",
@ -57,7 +66,7 @@ else:
setup( setup(
name='moto', name='moto',
version='1.3.8', version=get_version(),
description='A library that allows your python tests to easily' description='A library that allows your python tests to easily'
' mock out the boto library', ' mock out the boto library',
long_description=read('README.md'), long_description=read('README.md'),

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import base64 import base64
import json
import boto import boto
import boto3 import boto3
@ -29,6 +30,44 @@ FyDHrtlrS80dPUQWNYHw++oACDpWO01LGLPPrGmuO/7cOdojPEd852q5gd+7W9xt
8vUH+pBa6IBLbvBp+szli51V3TLSWcoyy4ceJNQU2vCkTLoFdS0RLd/7tQ== 8vUH+pBa6IBLbvBp+szli51V3TLSWcoyy4ceJNQU2vCkTLoFdS0RLd/7tQ==
-----END CERTIFICATE-----""" -----END CERTIFICATE-----"""
MOCK_POLICY = """
{
"Version": "2012-10-17",
"Statement":
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example_bucket"
}
}
"""
MOCK_POLICY_2 = """
{
"Version": "2012-10-17",
"Id": "2",
"Statement":
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example_bucket"
}
}
"""
MOCK_POLICY_3 = """
{
"Version": "2012-10-17",
"Id": "3",
"Statement":
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example_bucket"
}
}
"""
@mock_iam_deprecated() @mock_iam_deprecated()
def test_get_all_server_certs(): def test_get_all_server_certs():
@ -243,12 +282,12 @@ def test_list_instance_profiles_for_role():
def test_list_role_policies(): def test_list_role_policies():
conn = boto.connect_iam() conn = boto.connect_iam()
conn.create_role("my-role") conn.create_role("my-role")
conn.put_role_policy("my-role", "test policy", "my policy") conn.put_role_policy("my-role", "test policy", MOCK_POLICY)
role = conn.list_role_policies("my-role") role = conn.list_role_policies("my-role")
role.policy_names.should.have.length_of(1) role.policy_names.should.have.length_of(1)
role.policy_names[0].should.equal("test policy") role.policy_names[0].should.equal("test policy")
conn.put_role_policy("my-role", "test policy 2", "another policy") conn.put_role_policy("my-role", "test policy 2", MOCK_POLICY)
role = conn.list_role_policies("my-role") role = conn.list_role_policies("my-role")
role.policy_names.should.have.length_of(2) role.policy_names.should.have.length_of(2)
@ -266,7 +305,7 @@ def test_put_role_policy():
conn = boto.connect_iam() conn = boto.connect_iam()
conn.create_role( conn.create_role(
"my-role", assume_role_policy_document="some policy", path="my-path") "my-role", assume_role_policy_document="some policy", path="my-path")
conn.put_role_policy("my-role", "test policy", "my policy") conn.put_role_policy("my-role", "test policy", MOCK_POLICY)
policy = conn.get_role_policy( policy = conn.get_role_policy(
"my-role", "test policy")['get_role_policy_response']['get_role_policy_result']['policy_name'] "my-role", "test policy")['get_role_policy_response']['get_role_policy_result']['policy_name']
policy.should.equal("test policy") policy.should.equal("test policy")
@ -286,7 +325,7 @@ def test_create_policy():
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
response = conn.create_policy( response = conn.create_policy(
PolicyName="TestCreatePolicy", PolicyName="TestCreatePolicy",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
response['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestCreatePolicy") response['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestCreatePolicy")
@ -299,12 +338,12 @@ def test_create_policy_versions():
PolicyDocument='{"some":"policy"}') PolicyDocument='{"some":"policy"}')
conn.create_policy( conn.create_policy(
PolicyName="TestCreatePolicyVersion", PolicyName="TestCreatePolicyVersion",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
version = conn.create_policy_version( version = conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
PolicyDocument='{"some":"policy"}', PolicyDocument=MOCK_POLICY,
SetAsDefault=True) SetAsDefault=True)
version.get('PolicyVersion').get('Document').should.equal({'some': 'policy'}) version.get('PolicyVersion').get('Document').should.equal(json.loads(MOCK_POLICY))
version.get('PolicyVersion').get('VersionId').should.equal("v2") version.get('PolicyVersion').get('VersionId').should.equal("v2")
version.get('PolicyVersion').get('IsDefaultVersion').should.be.ok version.get('PolicyVersion').get('IsDefaultVersion').should.be.ok
conn.delete_policy_version( conn.delete_policy_version(
@ -312,7 +351,7 @@ def test_create_policy_versions():
VersionId="v1") VersionId="v1")
version = conn.create_policy_version( version = conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
version.get('PolicyVersion').get('VersionId').should.equal("v3") version.get('PolicyVersion').get('VersionId').should.equal("v3")
version.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok version.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok
@ -362,7 +401,7 @@ def test_get_policy():
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
response = conn.create_policy( response = conn.create_policy(
PolicyName="TestGetPolicy", PolicyName="TestGetPolicy",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
policy = conn.get_policy( policy = conn.get_policy(
PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicy") PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicy")
policy['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestGetPolicy") policy['Policy']['Arn'].should.equal("arn:aws:iam::123456789012:policy/TestGetPolicy")
@ -384,10 +423,10 @@ def test_get_policy_version():
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
conn.create_policy( conn.create_policy(
PolicyName="TestGetPolicyVersion", PolicyName="TestGetPolicyVersion",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
version = conn.create_policy_version( version = conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
PolicyDocument='{"some":"policy"}') PolicyDocument=MOCK_POLICY)
with assert_raises(ClientError): with assert_raises(ClientError):
conn.get_policy_version( conn.get_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
@ -396,6 +435,7 @@ def test_get_policy_version():
PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
VersionId=version.get('PolicyVersion').get('VersionId')) VersionId=version.get('PolicyVersion').get('VersionId'))
retrieved.get('PolicyVersion').get('Document').should.equal({'some': 'policy'}) retrieved.get('PolicyVersion').get('Document').should.equal({'some': 'policy'})
retrieved.get('PolicyVersion').get('Document').should.equal(json.loads(MOCK_POLICY))
retrieved.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok retrieved.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok
@ -439,7 +479,7 @@ def test_list_policy_versions():
PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions") PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
conn.create_policy( conn.create_policy(
PolicyName="TestListPolicyVersions", PolicyName="TestListPolicyVersions",
PolicyDocument='{"first":"policy"}') PolicyDocument=MOCK_POLICY)
versions = conn.list_policy_versions( versions = conn.list_policy_versions(
PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions") PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
versions.get('Versions')[0].get('VersionId').should.equal('v1') versions.get('Versions')[0].get('VersionId').should.equal('v1')
@ -447,15 +487,15 @@ def test_list_policy_versions():
conn.create_policy_version( conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions", PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
PolicyDocument='{"second":"policy"}') PolicyDocument=MOCK_POLICY_2)
conn.create_policy_version( conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions", PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
PolicyDocument='{"third":"policy"}') PolicyDocument=MOCK_POLICY_3)
versions = conn.list_policy_versions( versions = conn.list_policy_versions(
PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions") PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions")
versions.get('Versions')[1].get('Document').should.equal({'second': 'policy'}) versions.get('Versions')[1].get('Document').should.equal(json.loads(MOCK_POLICY_2))
versions.get('Versions')[1].get('IsDefaultVersion').shouldnt.be.ok versions.get('Versions')[1].get('IsDefaultVersion').shouldnt.be.ok
versions.get('Versions')[2].get('Document').should.equal({'third': 'policy'}) versions.get('Versions')[2].get('Document').should.equal(json.loads(MOCK_POLICY_3))
versions.get('Versions')[2].get('IsDefaultVersion').shouldnt.be.ok versions.get('Versions')[2].get('IsDefaultVersion').shouldnt.be.ok
@ -464,10 +504,10 @@ def test_delete_policy_version():
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
conn.create_policy( conn.create_policy(
PolicyName="TestDeletePolicyVersion", PolicyName="TestDeletePolicyVersion",
PolicyDocument='{"first":"policy"}') PolicyDocument=MOCK_POLICY)
conn.create_policy_version( conn.create_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
PolicyDocument='{"second":"policy"}') PolicyDocument=MOCK_POLICY)
with assert_raises(ClientError): with assert_raises(ClientError):
conn.delete_policy_version( conn.delete_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion", PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
@ -549,22 +589,20 @@ def test_list_users():
@mock_iam() @mock_iam()
def test_user_policies(): def test_user_policies():
policy_name = 'UserManagedPolicy' policy_name = 'UserManagedPolicy'
policy_document = "{'mypolicy': 'test'}"
user_name = 'my-user' user_name = 'my-user'
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
conn.create_user(UserName=user_name) conn.create_user(UserName=user_name)
conn.put_user_policy( conn.put_user_policy(
UserName=user_name, UserName=user_name,
PolicyName=policy_name, PolicyName=policy_name,
PolicyDocument=policy_document PolicyDocument=MOCK_POLICY
) )
policy_doc = conn.get_user_policy( policy_doc = conn.get_user_policy(
UserName=user_name, UserName=user_name,
PolicyName=policy_name PolicyName=policy_name
) )
test = policy_document in policy_doc['PolicyDocument'] policy_doc['PolicyDocument'].should.equal(json.loads(MOCK_POLICY))
test.should.equal(True)
policies = conn.list_user_policies(UserName=user_name) policies = conn.list_user_policies(UserName=user_name)
len(policies['PolicyNames']).should.equal(1) len(policies['PolicyNames']).should.equal(1)
@ -725,7 +763,7 @@ def test_managed_policy():
conn = boto.connect_iam() conn = boto.connect_iam()
conn.create_policy(policy_name='UserManagedPolicy', conn.create_policy(policy_name='UserManagedPolicy',
policy_document={'mypolicy': 'test'}, policy_document=MOCK_POLICY,
path='/mypolicy/', path='/mypolicy/',
description='my user managed policy') description='my user managed policy')
@ -826,7 +864,7 @@ def test_attach_detach_user_policy():
policy_name = 'UserAttachedPolicy' policy_name = 'UserAttachedPolicy'
policy = iam.create_policy(PolicyName=policy_name, policy = iam.create_policy(PolicyName=policy_name,
PolicyDocument='{"mypolicy": "test"}', PolicyDocument=MOCK_POLICY,
Path='/mypolicy/', Path='/mypolicy/',
Description='my user attached policy') Description='my user attached policy')
@ -882,7 +920,6 @@ def test_get_access_key_last_used():
@mock_iam @mock_iam
def test_get_account_authorization_details(): def test_get_account_authorization_details():
import json
test_policy = json.dumps({ test_policy = json.dumps({
"Version": "2012-10-17", "Version": "2012-10-17",
"Statement": [ "Statement": [
@ -1314,7 +1351,6 @@ def test_update_role():
@mock_iam() @mock_iam()
def test_list_entities_for_policy(): def test_list_entities_for_policy():
import json
test_policy = json.dumps({ test_policy = json.dumps({
"Version": "2012-10-17", "Version": "2012-10-17",
"Statement": [ "Statement": [

View File

@ -10,6 +10,18 @@ from nose.tools import assert_raises
from boto.exception import BotoServerError from boto.exception import BotoServerError
from moto import mock_iam, mock_iam_deprecated from moto import mock_iam, mock_iam_deprecated
MOCK_POLICY = """
{
"Version": "2012-10-17",
"Statement":
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example_bucket"
}
}
"""
@mock_iam_deprecated() @mock_iam_deprecated()
def test_create_group(): def test_create_group():
@ -101,7 +113,7 @@ def test_get_groups_for_user():
def test_put_group_policy(): def test_put_group_policy():
conn = boto.connect_iam() conn = boto.connect_iam()
conn.create_group('my-group') conn.create_group('my-group')
conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}') conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
@mock_iam @mock_iam
@ -131,7 +143,7 @@ def test_get_group_policy():
with assert_raises(BotoServerError): with assert_raises(BotoServerError):
conn.get_group_policy('my-group', 'my-policy') conn.get_group_policy('my-group', 'my-policy')
conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}') conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
conn.get_group_policy('my-group', 'my-policy') conn.get_group_policy('my-group', 'my-policy')
@ -141,7 +153,7 @@ def test_get_all_group_policies():
conn.create_group('my-group') conn.create_group('my-group')
policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names'] policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names']
assert policies == [] assert policies == []
conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}') conn.put_group_policy('my-group', 'my-policy', MOCK_POLICY)
policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names'] policies = conn.get_all_group_policies('my-group')['list_group_policies_response']['list_group_policies_result']['policy_names']
assert policies == ['my-policy'] assert policies == ['my-policy']
@ -151,5 +163,5 @@ def test_list_group_policies():
conn = boto3.client('iam', region_name='us-east-1') conn = boto3.client('iam', region_name='us-east-1')
conn.create_group(GroupName='my-group') conn.create_group(GroupName='my-group')
conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument='{"some": "json"}') conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument=MOCK_POLICY)
conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy']) conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])

File diff suppressed because it is too large Load Diff

118
update_version_from_git.py Normal file
View File

@ -0,0 +1,118 @@
"""
Adapted from https://github.com/pygame/pygameweb/blob/master/pygameweb/builds/update_version_from_git.py
For updating the version from git.
__init__.py contains a __version__ field.
Update that.
If we are on master, we want to update the version as a pre-release.
git describe --tags
With these:
__init__.py
__version__= '0.0.2'
git describe --tags
0.0.1-22-g729a5ae
We want this:
__init__.py
__version__= '0.0.2.dev22.g729a5ae'
Get the branch/tag name with this.
git symbolic-ref -q --short HEAD || git describe --tags --exact-match
"""
import io
import os
import re
import subprocess
def migrate_source_attribute(attr, to_this, target_file, regex):
"""Updates __magic__ attributes in the source file"""
change_this = re.compile(regex, re.S)
new_file = []
found = False
with open(target_file, 'r') as fp:
lines = fp.readlines()
for line in lines:
if line.startswith(attr):
found = True
line = re.sub(change_this, to_this, line)
new_file.append(line)
if found:
with open(target_file, 'w') as fp:
fp.writelines(new_file)
def migrate_version(target_file, new_version):
"""Updates __version__ in the source file"""
regex = r"['\"](.*)['\"]"
migrate_source_attribute('__version__', "'{new_version}'".format(new_version=new_version), target_file, regex)
def is_master_branch():
cmd = ('git rev-parse --abbrev-ref HEAD')
tag_branch = subprocess.check_output(cmd, shell=True)
return tag_branch in [b'master\n']
def git_tag_name():
cmd = ('git describe --tags')
tag_branch = subprocess.check_output(cmd, shell=True)
tag_branch = tag_branch.decode().strip()
return tag_branch
def get_git_version_info():
cmd = 'git describe --tags'
ver_str = subprocess.check_output(cmd, shell=True)
ver, commits_since, githash = ver_str.decode().strip().split('-')
return ver, commits_since, githash
def prerelease_version():
""" return what the prerelease version should be.
https://packaging.python.org/tutorials/distributing-packages/#pre-release-versioning
0.0.2.dev22
"""
ver, commits_since, githash = get_git_version_info()
initpy_ver = get_version()
assert len(initpy_ver.split('.')) in [3, 4], 'moto/__init__.py version should be like 0.0.2 or 0.0.2.dev'
assert initpy_ver > ver, 'the moto/__init__.py version should be newer than the last tagged release.'
return '{initpy_ver}.dev{commits_since}'.format(initpy_ver=initpy_ver, commits_since=commits_since)
def read(*parts):
""" Reads in file from *parts.
"""
try:
return io.open(os.path.join(*parts), 'r', encoding='utf-8').read()
except IOError:
return ''
def get_version():
""" Returns version from moto/__init__.py
"""
version_file = read('moto', '__init__.py')
version_match = re.search(r'^__version__ = [\'"]([^\'"]*)[\'"]',
version_file, re.MULTILINE)
if version_match:
return version_match.group(1)
raise RuntimeError('Unable to find version string.')
def release_version_correct():
"""Makes sure the:
- prerelease verion for master is correct.
- release version is correct for tags.
"""
if is_master_branch():
# update for a pre release version.
initpy = os.path.abspath("moto/__init__.py")
new_version = prerelease_version()
print('updating version in __init__.py to {new_version}'.format(new_version=new_version))
migrate_version(initpy, new_version)
else:
# check that we are a tag with the same version as in __init__.py
assert get_version() == git_tag_name(), 'git tag/branch name not the same as moto/__init__.py __verion__'
if __name__ == '__main__':
release_version_correct()