Skip to content

Commit bfcc835

Browse files
committed
basic check on certificate validity
1 parent 682dd89 commit bfcc835

File tree

7 files changed

+148
-21
lines changed

7 files changed

+148
-21
lines changed

src/bulk_importer/main.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,6 @@
3333
from aws_lambda_powertools.utilities.data_classes import S3Event
3434
from aws_lambda_powertools.utilities.typing import LambdaContext
3535
from aws_lambda_powertools.utilities.validation import validator
36-
from schemas import INPUT_SCHEMA, OUTPUT_SCHEMA
37-
38-
_LAMBDA_S3_RESOURCE = { "resource" : boto3resource('s3'),
39-
"bucket_name" : os.environ.get("S3_BUCKET_NAME","NONE") }
40-
_LAMBDA_SQS_RESOURCE = { "resource" : boto3resource('sqs'),
41-
"bucket_name" : os.environ.get("S3_BUCKET_NAME","NONE") }
42-
_LAMBDA_IOT_RESOURCE = { "resource" : boto3resource('iot'),
43-
"bucket_name" : os.environ.get("S3_BUCKET_NAME","NONE") }
4436

4537
logger = logging.getLogger()
4638
logger.setLevel("INFO")
@@ -51,10 +43,10 @@
5143
100: "d",
5244
}
5345

54-
5546
verbose = True
5647
config = None
5748

49+
# Verify that the certificate is in IoT Core
5850
def get_certificate(certificateId):
5951
try:
6052
response = iot_client.describe_certificate(certificateId=certificateId)
@@ -63,6 +55,7 @@ def get_certificate(certificateId):
6355
print("Certificate [" + certificateId + "] not found in IoT Core.")
6456
return None
6557

58+
# Retrieve the certificate Arn.
6659
def get_certificate_arn(certificateId):
6760
try:
6861
response = iot_client.describe_certificate(certificateId=certificateId)
@@ -109,11 +102,11 @@ def get_thing_group(thingGroupName):
109102

110103
def get_thing_type(typeName):
111104
try:
112-
response = iot_client.describeThingType(thingTypeName=thingTypeName)
105+
response = iot_client.describeThingType(thingTypeName=typeName)
113106
return response.get('thingTypeArn')
114107
except botocore.exceptions.ClientError as error:
115108
if error.response['Error']['Code'] == 'ResourceNotFoundException':
116-
print("ERROR: You need to configure the Thing Type [" + thingTypeName + "] in your target region first.")
109+
print("ERROR: You need to configure the Thing Type [" + typeName + "] in your target region first.")
117110
if error.response['Error']['Code'] == 'UnauthorizedException':
118111
print("ERROR: There is a deployment problem with the attached Role. Unable to reach IoT Core object.")
119112
return None
@@ -157,22 +150,24 @@ def process_thing(thingName, certificateId, thingTypeName):
157150
return None
158151

159152
def requeue():
160-
sqs_client = boto3.client('sqs')
153+
sqs_client = boto3client('sqs')
161154
queueUrl = os.environ.get('QUEUE_TARGET')
162155
sqs_client.send_message( QueueUrl=queueUrl,
163-
MessageBody=json.dumps(config))
156+
MessageBody=json.dumps(config))
157+
158+
def get_certificate_fingerprint(certificate: x509.Certificate):
159+
return binascii.hexlify(certificate.fingerprint(hashes.SHA256())).decode('UTF-8')
164160

165161
def process_certificate(payload):
166-
client = boto3.client('iot')
162+
client = boto3client('iot')
167163

168164
certificateText = base64.b64decode(eval(payload))
169165

170166
# See if the certificate has already been registered. If so, bail.
171167
certificateObj = x509.load_pem_x509_certificate(data=certificateText,
172168
backend=default_backend())
173169

174-
fingerprint = binascii.hexlify(certificateObj.fingerprint(hashes.SHA256())).decode('UTF-8')
175-
print("Fingerprint: " + fingerprint)
170+
fingerprint = get_certificate_fingerprint(certificateObj)
176171

177172
if (get_certificate(fingerprint)):
178173
try:
@@ -183,7 +178,6 @@ def process_certificate(payload):
183178
except:
184179
print("Certificate [" + fingerprint + "] not found in IoT Core. Importing.")
185180

186-
187181
try:
188182
response = iot_client.register_certificate_without_ca(certificatePem=certificateText.decode('ascii'),
189183
status='ACTIVE')

src/bulk_importer/schemas.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
INPUT_SCHEMA = {
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "http://example.com/example.json",
4+
"type": "object",
5+
"title": "Sample input schema",
6+
"description": "The Document generation path parameters, Document Type and Customer ID.",
7+
"required": ["pathParameters"],
8+
"properties": {
9+
"pathParameters" : {
10+
"$id": "#/properties/pathParameters",
11+
"type": "object",
12+
"required": ["docType", "customerId"],
13+
"properties": {
14+
"docType": {
15+
"$id": "#/properties/pathParameters/docType",
16+
"type": "string",
17+
"title": "The Document Type to Generate",
18+
"examples": ["TestDoc","WELCOME"],
19+
"maxLength": 30,
20+
},
21+
"customerId": {
22+
"$id": "#/properties/pathParameters/customerId",
23+
"type": "string",
24+
"title": "The Customer ID to send the document",
25+
"examples": ["TestCustomer","TestCustomer01"],
26+
"maxLength": 30,
27+
}
28+
}
29+
}
30+
},
31+
}
32+
33+
OUTPUT_SCHEMA = {
34+
"$schema": "http://json-schema.org/draft-07/schema",
35+
"$id": "http://example.com/example.json",
36+
"type": "object",
37+
"title": "Sample outgoing schema",
38+
"description": "The root schema comprises the entire JSON document.",
39+
"examples": [{"statusCode": 200, "body": "OK"}],
40+
"required": ["statusCode", "body"],
41+
"properties": {
42+
"statusCode": {"$id": "#/properties/statusCode", "type": "integer", "title": "The statusCode"},
43+
"body": {"$id": "#/properties/body", "type": "string", "title": "The response"}
44+
},
45+
}

src/bulk_importer/testable.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from os import environ
2+
import warnings
3+
with warnings.catch_warnings():
4+
from boto3 import resource
5+
6+
_LAMBDA_SQS_RESOURCE = { "resource" : resource('sqs'),
7+
"queue_name" : environ.get("SQS_QUEUE_NAME","NONE") }
8+
9+
class LambdaSQSClass:
10+
"""
11+
AWS SQS Resource Class
12+
"""
13+
def __init__(self, lambda_sqs_resource):
14+
"""
15+
Initialize an SQS Resource
16+
"""
17+
self.resource = lambda_sqs_resource["resource"]
18+
self.queue_name = lambda_sqs_resource["queue_name"]
19+
self.queue = self.resource.Queue(self.queue_name)

src/provider_espressif/main.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
from cryptography.hazmat.primitives import serialization
1313
import base64
1414

15-
from schemas import INPUT_SCHEMA, OUTPUT_SCHEMA
16-
from testable import LambdaSQSClass, LambdaS3Class
17-
1815
# Given a bucket and object, verify its existence and return the resource.
1916
def s3_object_stream(bucket_name: str, object_name: str):
2017
s3 = resource('s3')

test/artifacts/single.pem

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDfDCCAmSgAwIBAgIUR671iZbm5q0jCpoLgAGwH6oZKFwwDQYJKoZIhvcNAQEL
3+
BQAwUzELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQHDARQdW5lMRIw
4+
EAYDVQQKDAlFc3ByZXNzaWYxFDASBgNVBAMMC2VzcC10ZXN0LWNuMB4XDTI1MDQw
5+
MzE0MzEwOVoXDTM1MDQwMTE0MzEwOVowezELMAkGA1UEBhMCSU4xCzAJBgNVBAgM
6+
Ak1IMRowGAYDVQQKDBFFc3ByZXNzaWYgU3lzdGVtczEUMBIGA1UECwwLRXhwcmVz
7+
c0xpbmsxLTArBgNVBAMMJDZkMWQ1NGJhLWVkODEtNGMzMi04ZTA4LWY5ZjhjODg1
8+
YTZlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+SHRhdFKzPogsu
9+
F7yKdyzSOPAGcuAMmaGMEb1WRLPMkI9yW2SERyTglOBoyXxUJUj49n5y7AKm5zp4
10+
nfljsF1DcV4pwgq7FUVjkko3YjGbanxyfwhHUb0wlXWp86S76QcdxbL9rA4pHPam
11+
M+qc0iy/2YmG18AuWpQ7gwfn3WpFxIeNgTpMP/AqCE/F5pLRaHWvrXXAbVrUa4Tm
12+
svpCvFwwalR+VjF74TpbRYfLbtRDS/vzgdRLRJzDmnBalc3+A5OcOQbIXOvOySwx
13+
5E2ermNhqCd0GOJ7L395Kh+2XV4XTCp7Cmsfhnoxo5/UuGuAYHZGio16GqpqZcY7
14+
5eYsi9kCAwEAAaMgMB4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwDQYJ
15+
KoZIhvcNAQELBQADggEBAIxi68b/5AfznHOoSddeBDfkgtJV1a2a0n8w6NfGwmtm
16+
8i7PTAa8tvnUCyAYXvFeaaVsCDMYfSU4dw8vMS3lRPRiUJ09wizxyiOG5Af4q2iY
17+
msy561rxsdydrvf0TTVPNNwu5MBOyrIUWVZVtAg8SwBWZ7Q+4oTrYvpTkVXOKCXs
18+
+uwxfk7ht4X9w8P6EjM1g1CKpCT9+iRpnD6Gi51ZVGFqIu1a0eWBEAi/zKsWfoc8
19+
lzeyB/DJbVaz7+MVnI+WIH5/PASI3tbFUq/vF32O1zTm1+PLvOEppPG17p981oEp
20+
Hz8WLhhGzqsnhW96DyIY4qUU2MxXfg5QzFFQz9jEoH4=
21+
-----END CERTIFICATE-----
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import sys
2+
import os
3+
import io
4+
import pytest
5+
6+
import botocore
7+
8+
from boto3 import resource, client
9+
from moto import mock_aws, settings
10+
from moto.settings import iot_use_valid_cert
11+
12+
from aws_lambda_powertools.utilities.validation import validate
13+
14+
from unittest import TestCase
15+
from unittest.mock import MagicMock, patch
16+
sys.path.append('./src/bulk_importer')
17+
os.environ['AWS_DEFAULT_REGION'] = "us-east-1"
18+
from src.bulk_importer.testable import LambdaSQSClass # pylint: disable=wrong-import-position
19+
from src.bulk_importer.main import lambda_handler, get_certificate, get_certificate_fingerprint, get_certificate_arn, get_thing, get_policy, get_thing_group, get_thing_type, process_policy, process_thing, requeue, process_certificate, process_thing_group, get_name_from_certificate, process_sqs # pylint: disable=wrong-import-position
20+
from cryptography import x509
21+
from cryptography.hazmat.backends import default_backend
22+
from cryptography.hazmat.primitives import serialization
23+
import base64
24+
25+
@mock_aws(config={'iot': {'use_valid_cert': True}})
26+
class TestBulkImporter(TestCase):
27+
def setUp(self):
28+
self.test_sqs_queue_name = "provider"
29+
sqs_client = client('sqs', region_name="us-east-1")
30+
sqs_client.create_queue(QueueName=self.test_sqs_queue_name)
31+
mocked_sqs_resource = resource("sqs")
32+
mocked_sqs_resource = { "resource" : resource('sqs'),
33+
"queue_name" : self.test_sqs_queue_name }
34+
self.mocked_sqs_class = LambdaSQSClass(mocked_sqs_resource)
35+
36+
def test_pos_process_certificate(self):
37+
with open('./test/artifacts/single.pem', 'rb') as data:
38+
pem_obj = x509.load_pem_x509_certificate(data.read(),
39+
backend=default_backend())
40+
block = pem_obj.public_bytes(encoding=serialization.Encoding.PEM).decode('ascii')
41+
cert = str(base64.b64encode(block.encode('ascii')))
42+
r = process_certificate(cert)
43+
assert (r == get_certificate_fingerprint(pem_obj))
44+
45+
def tearDown(self):
46+
sqs_resource = resource("sqs", region_name="us-east-1")
47+
sqs_client = client("sqs", "us-east-1")
48+
sqs_queue_url_r = sqs_client.get_queue_url(QueueName=self.test_sqs_queue_name)
49+
sqs_queue_url = sqs_queue_url_r['QueueUrl']
50+
sqs_resource = sqs_resource.Queue(url=sqs_queue_url)
51+
sqs_resource.delete()

test/unit/src/test_provider_espressif.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from src.provider_espressif.testable import LambdaS3Class, LambdaSQSClass # pylint: disable=wrong-import-position
1717
from src.provider_espressif.main import lambda_handler, s3_filebuf_bytes, invoke_export # pylint: disable=wrong-import-position
1818
from src.provider_espressif.main import s3_object_stream
19-
from src.provider_espressif.main import INPUT_SCHEMA # pylint: disable=wrong-import-position
19+
from src.provider_espressif.schemas import INPUT_SCHEMA # pylint: disable=wrong-import-position
2020

2121
@mock_aws
2222
class TestProviderEspressif(TestCase):

0 commit comments

Comments
 (0)