Feature: Signer service (#5389)
This commit is contained in:
parent
50efabe0d3
commit
f05d56afc5
@ -5683,6 +5683,29 @@
|
||||
- [X] verify_email_identity
|
||||
</details>
|
||||
|
||||
## signer
|
||||
<details>
|
||||
<summary>23% implemented</summary>
|
||||
|
||||
- [ ] add_profile_permission
|
||||
- [X] cancel_signing_profile
|
||||
- [ ] describe_signing_job
|
||||
- [ ] get_signing_platform
|
||||
- [X] get_signing_profile
|
||||
- [ ] list_profile_permissions
|
||||
- [ ] list_signing_jobs
|
||||
- [X] list_signing_platforms
|
||||
- [ ] list_signing_profiles
|
||||
- [ ] list_tags_for_resource
|
||||
- [X] put_signing_profile
|
||||
- [ ] remove_profile_permission
|
||||
- [ ] revoke_signature
|
||||
- [ ] revoke_signing_profile
|
||||
- [ ] start_signing_job
|
||||
- [ ] tag_resource
|
||||
- [ ] untag_resource
|
||||
</details>
|
||||
|
||||
## sns
|
||||
<details>
|
||||
<summary>55% implemented</summary>
|
||||
@ -6370,7 +6393,6 @@
|
||||
- servicecatalog-appregistry
|
||||
- sesv2
|
||||
- shield
|
||||
- signer
|
||||
- sms
|
||||
- sms-voice
|
||||
- snow-device-management
|
||||
|
55
docs/docs/services/signer.rst
Normal file
55
docs/docs/services/signer.rst
Normal file
@ -0,0 +1,55 @@
|
||||
.. _implementedservice_signer:
|
||||
|
||||
.. |start-h3| raw:: html
|
||||
|
||||
<h3>
|
||||
|
||||
.. |end-h3| raw:: html
|
||||
|
||||
</h3>
|
||||
|
||||
======
|
||||
signer
|
||||
======
|
||||
|
||||
.. autoclass:: moto.signer.models.SignerBackend
|
||||
|
||||
|start-h3| Example usage |end-h3|
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@mock_signer
|
||||
def test_signer_behaviour:
|
||||
boto3.client("signer")
|
||||
...
|
||||
|
||||
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] add_profile_permission
|
||||
- [X] cancel_signing_profile
|
||||
- [ ] describe_signing_job
|
||||
- [ ] get_signing_platform
|
||||
- [X] get_signing_profile
|
||||
- [ ] list_profile_permissions
|
||||
- [ ] list_signing_jobs
|
||||
- [X] list_signing_platforms
|
||||
|
||||
Pagination is not yet implemented. The parameters category, partner, target are not yet implemented
|
||||
|
||||
|
||||
- [ ] list_signing_profiles
|
||||
- [ ] list_tags_for_resource
|
||||
- [X] put_signing_profile
|
||||
|
||||
The following parameters are not yet implemented: SigningMaterial, Overrides, SigningParamaters
|
||||
|
||||
|
||||
- [ ] remove_profile_permission
|
||||
- [ ] revoke_signature
|
||||
- [ ] revoke_signing_profile
|
||||
- [ ] start_signing_job
|
||||
- [ ] tag_resource
|
||||
- [ ] untag_resource
|
||||
|
@ -137,6 +137,7 @@ mock_sdb = lazy_load(".sdb", "mock_sdb")
|
||||
mock_secretsmanager = lazy_load(".secretsmanager", "mock_secretsmanager")
|
||||
mock_ses = lazy_load(".ses", "mock_ses")
|
||||
mock_servicediscovery = lazy_load(".servicediscovery", "mock_servicediscovery")
|
||||
mock_signer = lazy_load(".signer", "mock_signer", boto3_name="signer")
|
||||
mock_sns = lazy_load(".sns", "mock_sns")
|
||||
mock_sqs = lazy_load(".sqs", "mock_sqs")
|
||||
mock_ssm = lazy_load(".ssm", "mock_ssm")
|
||||
|
@ -143,6 +143,7 @@ backend_url_patterns = [
|
||||
),
|
||||
("ses", re.compile("https?://email\\.(.+)\\.amazonaws\\.com")),
|
||||
("ses", re.compile("https?://ses\\.(.+)\\.amazonaws\\.com")),
|
||||
("signer", re.compile("https?://signer\\.(.+)\\.amazonaws\\.com")),
|
||||
("sns", re.compile("https?://sns\\.(.+)\\.amazonaws\\.com")),
|
||||
("sqs", re.compile("https?://(.*\\.)?(queue|sqs)\\.(.*\\.)?amazonaws\\.com")),
|
||||
("ssm", re.compile("https?://ssm\\.(.+)\\.amazonaws\\.com")),
|
||||
|
5
moto/signer/__init__.py
Normal file
5
moto/signer/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""signer module initialization; sets value for base decorator."""
|
||||
from .models import signer_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_signer = base_decorator(signer_backends)
|
1
moto/signer/exceptions.py
Normal file
1
moto/signer/exceptions.py
Normal file
@ -0,0 +1 @@
|
||||
"""Exceptions raised by the signer service."""
|
192
moto/signer/models.py
Normal file
192
moto/signer/models.py
Normal file
@ -0,0 +1,192 @@
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import BackendDict, get_random_hex
|
||||
|
||||
|
||||
class SigningProfile(BaseModel):
|
||||
def __init__(
|
||||
self, account_id, region, name, platform_id, signature_validity_period, tags
|
||||
):
|
||||
self.name = name
|
||||
self.platform_id = platform_id
|
||||
self.signature_validity_period = signature_validity_period or {
|
||||
"value": 135,
|
||||
"type": "MONTHS",
|
||||
}
|
||||
self.tags = tags
|
||||
|
||||
self.status = "Active"
|
||||
self.arn = f"arn:aws:signer:{region}:{account_id}:/signing-profiles/{name}"
|
||||
self.profile_version = get_random_hex(10)
|
||||
self.profile_version_arn = f"{self.arn}/{self.profile_version}"
|
||||
|
||||
def cancel(self):
|
||||
self.status = "Canceled"
|
||||
|
||||
def to_dict(self, full=True):
|
||||
small = {
|
||||
"arn": self.arn,
|
||||
"profileVersion": self.profile_version,
|
||||
"profileVersionArn": self.profile_version_arn,
|
||||
}
|
||||
if full:
|
||||
small.update(
|
||||
{
|
||||
"status": self.status,
|
||||
"profileName": self.name,
|
||||
"platformId": self.platform_id,
|
||||
"signatureValidityPeriod": self.signature_validity_period,
|
||||
"signingMaterial": {},
|
||||
"platformDisplayName": next(
|
||||
(
|
||||
p["displayName"]
|
||||
for p in SignerBackend.platforms
|
||||
if p["platformId"] == self.platform_id
|
||||
),
|
||||
None,
|
||||
),
|
||||
}
|
||||
)
|
||||
if self.tags:
|
||||
small.update({"tags": self.tags})
|
||||
return small
|
||||
|
||||
|
||||
class SignerBackend(BaseBackend):
|
||||
"""Implementation of signer APIs."""
|
||||
|
||||
platforms = [
|
||||
{
|
||||
"platformId": "AWSIoTDeviceManagement-SHA256-ECDSA",
|
||||
"displayName": "AWS IoT Device Management SHA256-ECDSA ",
|
||||
"partner": "AWSIoTDeviceManagement",
|
||||
"target": "SHA256-ECDSA",
|
||||
"category": "AWS",
|
||||
"signingConfiguration": {
|
||||
"encryptionAlgorithmOptions": {
|
||||
"allowedValues": ["ECDSA"],
|
||||
"defaultValue": "ECDSA",
|
||||
},
|
||||
"hashAlgorithmOptions": {
|
||||
"allowedValues": ["SHA256"],
|
||||
"defaultValue": "SHA256",
|
||||
},
|
||||
},
|
||||
"signingImageFormat": {
|
||||
"supportedFormats": ["JSONDetached"],
|
||||
"defaultFormat": "JSONDetached",
|
||||
},
|
||||
"maxSizeInMB": 2048,
|
||||
"revocationSupported": False,
|
||||
},
|
||||
{
|
||||
"platformId": "AWSLambda-SHA384-ECDSA",
|
||||
"displayName": "AWS Lambda",
|
||||
"partner": "AWSLambda",
|
||||
"target": "SHA384-ECDSA",
|
||||
"category": "AWS",
|
||||
"signingConfiguration": {
|
||||
"encryptionAlgorithmOptions": {
|
||||
"allowedValues": ["ECDSA"],
|
||||
"defaultValue": "ECDSA",
|
||||
},
|
||||
"hashAlgorithmOptions": {
|
||||
"allowedValues": ["SHA384"],
|
||||
"defaultValue": "SHA384",
|
||||
},
|
||||
},
|
||||
"signingImageFormat": {
|
||||
"supportedFormats": ["JSONDetached"],
|
||||
"defaultFormat": "JSONDetached",
|
||||
},
|
||||
"maxSizeInMB": 250,
|
||||
"revocationSupported": True,
|
||||
},
|
||||
{
|
||||
"platformId": "AmazonFreeRTOS-TI-CC3220SF",
|
||||
"displayName": "Amazon FreeRTOS SHA1-RSA CC3220SF-Format",
|
||||
"partner": "AmazonFreeRTOS",
|
||||
"target": "SHA1-RSA-TISHA1",
|
||||
"category": "AWS",
|
||||
"signingConfiguration": {
|
||||
"encryptionAlgorithmOptions": {
|
||||
"allowedValues": ["RSA"],
|
||||
"defaultValue": "RSA",
|
||||
},
|
||||
"hashAlgorithmOptions": {
|
||||
"allowedValues": ["SHA1"],
|
||||
"defaultValue": "SHA1",
|
||||
},
|
||||
},
|
||||
"signingImageFormat": {
|
||||
"supportedFormats": ["JSONEmbedded"],
|
||||
"defaultFormat": "JSONEmbedded",
|
||||
},
|
||||
"maxSizeInMB": 16,
|
||||
"revocationSupported": False,
|
||||
},
|
||||
{
|
||||
"platformId": "AmazonFreeRTOS-Default",
|
||||
"displayName": "Amazon FreeRTOS SHA256-ECDSA",
|
||||
"partner": "AmazonFreeRTOS",
|
||||
"target": "SHA256-ECDSA",
|
||||
"category": "AWS",
|
||||
"signingConfiguration": {
|
||||
"encryptionAlgorithmOptions": {
|
||||
"allowedValues": ["ECDSA", "RSA"],
|
||||
"defaultValue": "ECDSA",
|
||||
},
|
||||
"hashAlgorithmOptions": {
|
||||
"allowedValues": ["SHA256"],
|
||||
"defaultValue": "SHA256",
|
||||
},
|
||||
},
|
||||
"signingImageFormat": {
|
||||
"supportedFormats": ["JSONEmbedded"],
|
||||
"defaultFormat": "JSONEmbedded",
|
||||
},
|
||||
"maxSizeInMB": 16,
|
||||
"revocationSupported": False,
|
||||
},
|
||||
]
|
||||
|
||||
def __init__(self, region_name, account_id):
|
||||
super().__init__(region_name, account_id)
|
||||
self.signing_profiles: [str, SigningProfile] = dict()
|
||||
|
||||
def cancel_signing_profile(self, profile_name) -> None:
|
||||
self.signing_profiles[profile_name].cancel()
|
||||
|
||||
def get_signing_profile(self, profile_name) -> SigningProfile:
|
||||
return self.signing_profiles[profile_name]
|
||||
|
||||
def put_signing_profile(
|
||||
self,
|
||||
profile_name,
|
||||
signature_validity_period,
|
||||
platform_id,
|
||||
tags,
|
||||
) -> SigningProfile:
|
||||
"""
|
||||
The following parameters are not yet implemented: SigningMaterial, Overrides, SigningParamaters
|
||||
"""
|
||||
profile = SigningProfile(
|
||||
account_id=self.account_id,
|
||||
region=self.region_name,
|
||||
name=profile_name,
|
||||
platform_id=platform_id,
|
||||
signature_validity_period=signature_validity_period,
|
||||
tags=tags,
|
||||
)
|
||||
self.signing_profiles[profile_name] = profile
|
||||
return profile
|
||||
|
||||
def list_signing_platforms(self):
|
||||
"""
|
||||
Pagination is not yet implemented. The parameters category, partner, target are not yet implemented
|
||||
"""
|
||||
return SignerBackend.platforms
|
||||
|
||||
|
||||
# Using the lambda-regions
|
||||
# boto3.Session().get_available_regions("signer") still returns an empty list
|
||||
signer_backends: [str, [str, SignerBackend]] = BackendDict(SignerBackend, "lambda")
|
43
moto/signer/responses.py
Normal file
43
moto/signer/responses.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""Handles incoming signer requests, invokes methods, returns responses."""
|
||||
import json
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import signer_backends, SignerBackend
|
||||
|
||||
|
||||
class signerResponse(BaseResponse):
|
||||
def __init__(self):
|
||||
super().__init__(service_name="signer")
|
||||
|
||||
@property
|
||||
def signer_backend(self) -> SignerBackend:
|
||||
"""Return backend instance specific for this region."""
|
||||
return signer_backends[self.current_account][self.region]
|
||||
|
||||
def cancel_signing_profile(self):
|
||||
profile_name = self.path.split("/")[-1]
|
||||
self.signer_backend.cancel_signing_profile(profile_name=profile_name)
|
||||
return "{}"
|
||||
|
||||
def get_signing_profile(self):
|
||||
profile_name = self.path.split("/")[-1]
|
||||
profile = self.signer_backend.get_signing_profile(profile_name=profile_name)
|
||||
return json.dumps(profile.to_dict())
|
||||
|
||||
def put_signing_profile(self):
|
||||
params = json.loads(self.body)
|
||||
profile_name = self.path.split("/")[-1]
|
||||
signature_validity_period = params.get("signatureValidityPeriod")
|
||||
platform_id = params.get("platformId")
|
||||
tags = params.get("tags")
|
||||
profile = self.signer_backend.put_signing_profile(
|
||||
profile_name=profile_name,
|
||||
signature_validity_period=signature_validity_period,
|
||||
platform_id=platform_id,
|
||||
tags=tags,
|
||||
)
|
||||
return json.dumps(profile.to_dict(full=False))
|
||||
|
||||
def list_signing_platforms(self):
|
||||
platforms = self.signer_backend.list_signing_platforms()
|
||||
return json.dumps(dict(platforms=platforms))
|
15
moto/signer/urls.py
Normal file
15
moto/signer/urls.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""signer base URL and path."""
|
||||
from .responses import signerResponse
|
||||
|
||||
url_bases = [
|
||||
r"https?://signer\.(.+)\.amazonaws\.com",
|
||||
]
|
||||
|
||||
|
||||
response = signerResponse()
|
||||
|
||||
|
||||
url_paths = {
|
||||
"{0}/signing-profiles/(?P<profile_name>[^/]+)$": response.dispatch,
|
||||
"{0}/signing-platforms$": response.dispatch,
|
||||
}
|
@ -207,6 +207,13 @@ servicediscovery:
|
||||
- TestAccServiceDiscoveryPrivateDNSNamespace
|
||||
- TestAccServiceDiscoveryPublicDNSNamespace
|
||||
- TestAccServiceDiscoveryService
|
||||
signer:
|
||||
- TestAccSignerSigningProfileDataSource_basic
|
||||
- TestAccSignerSigningProfile_basic
|
||||
- TestAccSignerSigningProfile_generateNameWithNamePrefix
|
||||
- TestAccSignerSigningProfile_generateName
|
||||
- TestAccSignerSigningProfile_tags
|
||||
- TestAccSignerSigningProfile_signatureValidityPeriod
|
||||
sns:
|
||||
- TestAccSNSTopicPolicy
|
||||
- TestAccSNSTopicDataSource
|
||||
|
0
tests/test_signer/__init__.py
Normal file
0
tests/test_signer/__init__.py
Normal file
21
tests/test_signer/test_signing_platforms.py
Normal file
21
tests/test_signer/test_signing_platforms.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""Unit tests for signer-supported APIs."""
|
||||
import boto3
|
||||
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
from moto import mock_signer
|
||||
|
||||
# See our Development Tips on writing tests for hints on how to write good tests:
|
||||
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
|
||||
|
||||
|
||||
@mock_signer
|
||||
def test_list_signing_platforms():
|
||||
client = boto3.client("signer", region_name="us-east-2")
|
||||
resp = client.list_signing_platforms()
|
||||
|
||||
resp.should.have.key("platforms").should.have.length_of(4)
|
||||
|
||||
partners = [x["partner"] for x in resp["platforms"]]
|
||||
set(partners).should.equal(
|
||||
{"AmazonFreeRTOS", "AWSLambda", "AWSIoTDeviceManagement"}
|
||||
)
|
70
tests/test_signer/test_signing_profiles.py
Normal file
70
tests/test_signer/test_signing_profiles.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""Unit tests for signer-supported APIs."""
|
||||
import boto3
|
||||
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
from moto import mock_signer
|
||||
|
||||
# See our Development Tips on writing tests for hints on how to write good tests:
|
||||
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
|
||||
|
||||
|
||||
@mock_signer
|
||||
def test_put_signing_profile():
|
||||
client = boto3.client("signer", region_name="eu-west-1")
|
||||
resp = client.put_signing_profile(profileName="prof1", platformId="pid")
|
||||
|
||||
resp.should.have.key("arn")
|
||||
resp.should.have.key("profileVersion")
|
||||
resp.should.have.key("profileVersionArn")
|
||||
|
||||
|
||||
@mock_signer
|
||||
def test_get_signing_profile():
|
||||
client = boto3.client("signer", region_name="eu-west-1")
|
||||
resp = client.put_signing_profile(
|
||||
profileName="prof1", platformId="AWSLambda-SHA384-ECDSA"
|
||||
)
|
||||
|
||||
resp = client.get_signing_profile(profileName="prof1")
|
||||
|
||||
resp.should.have.key("arn")
|
||||
resp.should.have.key("profileVersion")
|
||||
resp.should.have.key("profileVersionArn")
|
||||
resp.should.have.key("status").equals("Active")
|
||||
resp.should.have.key("profileName").equals("prof1")
|
||||
resp.should.have.key("platformId").equals("AWSLambda-SHA384-ECDSA")
|
||||
resp.should.have.key("signatureValidityPeriod").equals(
|
||||
{"type": "MONTHS", "value": 135}
|
||||
)
|
||||
|
||||
|
||||
@mock_signer
|
||||
def test_get_signing_profile__with_args():
|
||||
client = boto3.client("signer", region_name="eu-west-1")
|
||||
resp = client.put_signing_profile(
|
||||
profileName="prof1",
|
||||
platformId="AWSLambda-SHA384-ECDSA",
|
||||
signatureValidityPeriod={"type": "DAYS", "value": 10},
|
||||
tags={"k1": "v1", "k2": "v2"},
|
||||
)
|
||||
|
||||
resp = client.get_signing_profile(profileName="prof1")
|
||||
|
||||
resp.should.have.key("signatureValidityPeriod").equals(
|
||||
{"type": "DAYS", "value": 10}
|
||||
)
|
||||
resp.should.have.key("tags").equals({"k1": "v1", "k2": "v2"})
|
||||
|
||||
|
||||
@mock_signer
|
||||
def test_cancel_signing_profile():
|
||||
client = boto3.client("signer", region_name="eu-west-1")
|
||||
resp = client.put_signing_profile(
|
||||
profileName="prof1", platformId="AWSLambda-SHA384-ECDSA"
|
||||
)
|
||||
|
||||
client.cancel_signing_profile(profileName="prof1")
|
||||
|
||||
resp = client.get_signing_profile(profileName="prof1")
|
||||
|
||||
resp.should.have.key("status").equals("Canceled")
|
Loading…
Reference in New Issue
Block a user