Feature: Signer service (#5389)

This commit is contained in:
Bert Blommers 2022-08-23 19:47:44 +00:00 committed by GitHub
parent 50efabe0d3
commit f05d56afc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 434 additions and 1 deletions

View File

@ -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

View 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

View File

@ -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")

View File

@ -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
View 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)

View File

@ -0,0 +1 @@
"""Exceptions raised by the signer service."""

192
moto/signer/models.py Normal file
View 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
View 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
View 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,
}

View File

@ -207,6 +207,13 @@ servicediscovery:
- TestAccServiceDiscoveryPrivateDNSNamespace
- TestAccServiceDiscoveryPublicDNSNamespace
- TestAccServiceDiscoveryService
signer:
- TestAccSignerSigningProfileDataSource_basic
- TestAccSignerSigningProfile_basic
- TestAccSignerSigningProfile_generateNameWithNamePrefix
- TestAccSignerSigningProfile_generateName
- TestAccSignerSigningProfile_tags
- TestAccSignerSigningProfile_signatureValidityPeriod
sns:
- TestAccSNSTopicPolicy
- TestAccSNSTopicDataSource

View File

View 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"}
)

View 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")