parent
0211e9d78d
commit
c9dd9cc7f9
5
Makefile
5
Makefile
@ -1,13 +1,10 @@
|
|||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
|
||||||
ifeq ($(TEST_SERVER_MODE), true)
|
ifeq ($(TEST_SERVER_MODE), true)
|
||||||
# exclude test_iot and test_iotdata for now
|
|
||||||
# because authentication of iot is very complicated
|
|
||||||
|
|
||||||
# exclude test_kinesisvideoarchivedmedia
|
# exclude test_kinesisvideoarchivedmedia
|
||||||
# because testing with moto_server is difficult with data-endpoint
|
# because testing with moto_server is difficult with data-endpoint
|
||||||
|
|
||||||
TEST_EXCLUDE := -k 'not (test_iot or test_kinesisvideoarchivedmedia)'
|
TEST_EXCLUDE := -k 'not test_kinesisvideoarchivedmedia'
|
||||||
else
|
else
|
||||||
TEST_EXCLUDE :=
|
TEST_EXCLUDE :=
|
||||||
endif
|
endif
|
||||||
|
@ -425,8 +425,19 @@ class IoTResponse(BaseResponse):
|
|||||||
self.iot_backend.attach_policy(policy_name=policy_name, target=target)
|
self.iot_backend.attach_policy(policy_name=policy_name, target=target)
|
||||||
return json.dumps(dict())
|
return json.dumps(dict())
|
||||||
|
|
||||||
|
def dispatch_attached_policies(self, request, full_url, headers):
|
||||||
|
# This endpoint requires specialized handling because it has
|
||||||
|
# a uri parameter containing forward slashes that is not
|
||||||
|
# correctly url encoded when we're running in server mode.
|
||||||
|
# https://github.com/pallets/flask/issues/900
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
self.querystring["Action"] = ["ListAttachedPolicies"]
|
||||||
|
target = self.path.partition("/attached-policies/")[-1]
|
||||||
|
self.querystring["target"] = [unquote(target)] if "%" in target else [target]
|
||||||
|
return self.call_action()
|
||||||
|
|
||||||
def list_attached_policies(self):
|
def list_attached_policies(self):
|
||||||
principal = unquote(self._get_param("target"))
|
principal = self._get_param("target")
|
||||||
# marker = self._get_param("marker")
|
# marker = self._get_param("marker")
|
||||||
# page_size = self._get_int_param("pageSize")
|
# page_size = self._get_int_param("pageSize")
|
||||||
policies = self.iot_backend.list_attached_policies(target=principal)
|
policies = self.iot_backend.list_attached_policies(target=principal)
|
||||||
|
@ -7,4 +7,19 @@ url_bases = ["https?://iot.(.+).amazonaws.com"]
|
|||||||
response = IoTResponse()
|
response = IoTResponse()
|
||||||
|
|
||||||
|
|
||||||
url_paths = {"{0}/.*$": response.dispatch}
|
url_paths = {
|
||||||
|
#
|
||||||
|
# Paths for :class:`moto.core.models.MockAWS`
|
||||||
|
#
|
||||||
|
# This route requires special handling.
|
||||||
|
"{0}/attached-policies/(?P<target>.*)$": response.dispatch_attached_policies,
|
||||||
|
# The remaining routes can be handled by the default dispatcher.
|
||||||
|
"{0}/.*$": response.dispatch,
|
||||||
|
#
|
||||||
|
# (Flask) Paths for :class:`moto.core.models.ServerModeMockAWS`
|
||||||
|
#
|
||||||
|
# This route requires special handling.
|
||||||
|
"{0}/attached-policies/<path:target>$": response.dispatch_attached_policies,
|
||||||
|
# The remaining routes can be handled by the default dispatcher.
|
||||||
|
"{0}/<path:route>$": response.dispatch,
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from .models import iotdata_backends
|
from .models import iotdata_backends
|
||||||
import json
|
import json
|
||||||
|
from six.moves.urllib.parse import unquote
|
||||||
|
|
||||||
|
|
||||||
class IoTDataPlaneResponse(BaseResponse):
|
class IoTDataPlaneResponse(BaseResponse):
|
||||||
@ -29,6 +30,17 @@ class IoTDataPlaneResponse(BaseResponse):
|
|||||||
payload = self.iotdata_backend.delete_thing_shadow(thing_name=thing_name)
|
payload = self.iotdata_backend.delete_thing_shadow(thing_name=thing_name)
|
||||||
return json.dumps(payload.to_dict())
|
return json.dumps(payload.to_dict())
|
||||||
|
|
||||||
|
def dispatch_publish(self, request, full_url, headers):
|
||||||
|
# This endpoint requires specialized handling because it has
|
||||||
|
# a uri parameter containing forward slashes that is not
|
||||||
|
# correctly url encoded when we're running in server mode.
|
||||||
|
# https://github.com/pallets/flask/issues/900
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
self.querystring["Action"] = ["Publish"]
|
||||||
|
topic = self.path.partition("/topics/")[-1]
|
||||||
|
self.querystring["target"] = [unquote(topic)] if "%" in topic else [topic]
|
||||||
|
return self.call_action()
|
||||||
|
|
||||||
def publish(self):
|
def publish(self):
|
||||||
topic = self._get_param("topic")
|
topic = self._get_param("topic")
|
||||||
qos = self._get_int_param("qos")
|
qos = self._get_int_param("qos")
|
||||||
|
@ -7,4 +7,19 @@ url_bases = ["https?://data.iot.(.+).amazonaws.com"]
|
|||||||
response = IoTDataPlaneResponse()
|
response = IoTDataPlaneResponse()
|
||||||
|
|
||||||
|
|
||||||
url_paths = {"{0}/.*$": response.dispatch}
|
url_paths = {
|
||||||
|
#
|
||||||
|
# Paths for :class:`moto.core.models.MockAWS`
|
||||||
|
#
|
||||||
|
# This route requires special handling.
|
||||||
|
"{0}/topics/(?P<topic>.*)$": response.dispatch_publish,
|
||||||
|
# The remaining routes can be handled by the default dispatcher.
|
||||||
|
"{0}/.*$": response.dispatch,
|
||||||
|
#
|
||||||
|
# (Flask) Paths for :class:`moto.core.models.ServerModeMockAWS`
|
||||||
|
#
|
||||||
|
# This route requires special handling.
|
||||||
|
"{0}/topics/<path:topic>$": response.dispatch_publish,
|
||||||
|
# The remaining routes can be handled by the default dispatcher.
|
||||||
|
"{0}/<path:route>$": response.dispatch,
|
||||||
|
}
|
||||||
|
@ -34,6 +34,13 @@ UNSIGNED_REQUESTS = {
|
|||||||
}
|
}
|
||||||
UNSIGNED_ACTIONS = {"AssumeRoleWithSAML": ("sts", "us-east-1")}
|
UNSIGNED_ACTIONS = {"AssumeRoleWithSAML": ("sts", "us-east-1")}
|
||||||
|
|
||||||
|
# Some services have v4 signing names that differ from the backend service name/id.
|
||||||
|
SIGNING_ALIASES = {
|
||||||
|
"eventbridge": "events",
|
||||||
|
"execute-api": "iot",
|
||||||
|
"iotdata": "data.iot",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DomainDispatcherApplication(object):
|
class DomainDispatcherApplication(object):
|
||||||
"""
|
"""
|
||||||
@ -74,6 +81,7 @@ class DomainDispatcherApplication(object):
|
|||||||
try:
|
try:
|
||||||
credential_scope = auth.split(",")[0].split()[1]
|
credential_scope = auth.split(",")[0].split()[1]
|
||||||
_, _, region, service, _ = credential_scope.split("/")
|
_, _, region, service, _ = credential_scope.split("/")
|
||||||
|
service = SIGNING_ALIASES.get(service.lower(), service)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Signature format does not match, this is exceptional and we can't
|
# Signature format does not match, this is exceptional and we can't
|
||||||
# infer a service-region. A reduced set of services still use
|
# infer a service-region. A reduced set of services still use
|
||||||
@ -94,11 +102,6 @@ class DomainDispatcherApplication(object):
|
|||||||
# S3 is the last resort when the target is also unknown
|
# S3 is the last resort when the target is also unknown
|
||||||
service, region = DEFAULT_SERVICE_REGION
|
service, region = DEFAULT_SERVICE_REGION
|
||||||
|
|
||||||
if service == "EventBridge":
|
|
||||||
# Go SDK uses 'EventBridge' in the SigV4 request instead of 'events'
|
|
||||||
# see https://github.com/spulec/moto/issues/3494
|
|
||||||
service = "events"
|
|
||||||
|
|
||||||
if service == "dynamodb":
|
if service == "dynamodb":
|
||||||
if environ["HTTP_X_AMZ_TARGET"].startswith("DynamoDBStreams"):
|
if environ["HTTP_X_AMZ_TARGET"].startswith("DynamoDBStreams"):
|
||||||
host = "dynamodbstreams"
|
host = "dynamodbstreams"
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
|
import pytest
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
import moto.server as server
|
import moto.server as server
|
||||||
@ -17,4 +21,32 @@ def test_iot_list():
|
|||||||
|
|
||||||
# just making sure that server is up
|
# just making sure that server is up
|
||||||
res = test_client.get("/things")
|
res = test_client.get("/things")
|
||||||
res.status_code.should.equal(404)
|
res.status_code.should.equal(200)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url_encode_arn",
|
||||||
|
[
|
||||||
|
pytest.param(True, id="Target Arn in Path is URL encoded"),
|
||||||
|
pytest.param(False, id="Target Arn in Path is *not* URL encoded"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock_iot
|
||||||
|
def test_list_attached_policies(url_encode_arn):
|
||||||
|
backend = server.create_backend_app("iot")
|
||||||
|
test_client = backend.test_client()
|
||||||
|
|
||||||
|
result = test_client.post("/keys-and-certificate?setAsActive=true")
|
||||||
|
result_dict = json.loads(result.data.decode("utf-8"))
|
||||||
|
certificate_arn = result_dict["certificateArn"]
|
||||||
|
|
||||||
|
test_client.post("/policies/my-policy", json={"policyDocument": {}})
|
||||||
|
test_client.put("/target-policies/my-policy", json={"target": certificate_arn})
|
||||||
|
|
||||||
|
if url_encode_arn:
|
||||||
|
certificate_arn = quote(certificate_arn, safe="")
|
||||||
|
|
||||||
|
result = test_client.post("/attached-policies/{}".format(certificate_arn))
|
||||||
|
result.status_code.should.equal(200)
|
||||||
|
result_dict = json.loads(result.data.decode("utf-8"))
|
||||||
|
result_dict["policies"][0]["policyName"].should.equal("my-policy")
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
|
import pytest
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
import moto.server as server
|
import moto.server as server
|
||||||
@ -19,3 +22,22 @@ def test_iotdata_list():
|
|||||||
thing_name = "nothing"
|
thing_name = "nothing"
|
||||||
res = test_client.get("/things/{}/shadow".format(thing_name))
|
res = test_client.get("/things/{}/shadow".format(thing_name))
|
||||||
res.status_code.should.equal(404)
|
res.status_code.should.equal(404)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url_encode_topic",
|
||||||
|
[
|
||||||
|
pytest.param(True, id="Topic in Path is URL encoded"),
|
||||||
|
pytest.param(False, id="Topic in Path is *not* URL encoded"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@mock_iotdata
|
||||||
|
def test_publish(url_encode_topic):
|
||||||
|
backend = server.create_backend_app("iot-data")
|
||||||
|
test_client = backend.test_client()
|
||||||
|
|
||||||
|
topic = "test/topic"
|
||||||
|
topic_for_path = quote(topic, safe="") if url_encode_topic else topic
|
||||||
|
|
||||||
|
result = test_client.post("/topics/{}".format(topic_for_path))
|
||||||
|
result.status_code.should.equal(200)
|
||||||
|
Loading…
Reference in New Issue
Block a user