From 0a4d30c65772647c3ad039004e4bb40abd7e7aa9 Mon Sep 17 00:00:00 2001 From: Artem Martynovich Date: Fri, 28 Jun 2019 23:28:00 +0600 Subject: [PATCH 1/4] Create wott.py --- wott.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 wott.py diff --git a/wott.py b/wott.py new file mode 100644 index 0000000..87618b8 --- /dev/null +++ b/wott.py @@ -0,0 +1,116 @@ +import json +import os + +from google.oauth2 import service_account +from google.cloud.iot import DeviceManagerClient, types, enums +from google.api_core.exceptions import NotFound + + +PROJECT = 'wott-244904' +LOCATION = 'europe-west1' +REGISTRY = 'wott_registry' +PUBSUB = 'wott-pubsub' +CA_CERT = "-----BEGIN CERTIFICATE-----\nMIICIjCCAcigAwIBAgIUVX6IR6IZkUV5c9uFtkCPqz/S/8IwCgYIKoZIzj0EAwIw\nWzELMAkGA1UEBhMCVUsxDzANBgNVBAcTBkxvbmRvbjEeMBwGA1UEChMVV2ViIG9m\nIFRydXN0ZWQgVGhpbmdzMRswGQYDVQQDExJyb290LWNhLndvdHQubG9jYWwwHhcN\nMTkwMTI0MTY0NjAwWhcNMjAwMTI0MTY0NjAwWjBfMQswCQYDVQQGEwJVSzEPMA0G\nA1UEBxMGTG9uZG9uMSMwIQYDVQQKExpXZWIgb2YgVHJ1c3RlZCBUaGluZ3MsIEx0\nZDEaMBgGA1UEAxMRY2EwLWNhLndvdHQubG9jYWwwWTATBgcqhkjOPQIBBggqhkjO\nPQMBBwNCAARtfUDIXmuqlXh/iUBASCMsuv/okVl5Rr411rtDVkJb9/pOXvytrLHO\npqRoaTb20vjvZJyIfIQHjYCVhqgydJ/0o2YwZDAOBgNVHQ8BAf8EBAMCAYYwEgYD\nVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUqbbNcauIPejjNseUbyqxFACFNwEw\nHwYDVR0jBBgwFoAUFFMJ5IilG4BJpHcho67y9faWbpQwCgYIKoZIzj0EAwIDSAAw\nRQIhALsFD/KDOFfMpiL4/8VA65quwJYPQIZZSljuFbjIadnJAiAJ5rYreEJ0A8du\nlTjJd3us3c4uqtFC+9lvbyvY7Kqsbg==\n-----END CERTIFICATE-----" +DEVICE_ID = '799d00d82544489eb01b339c618d0b62.d.wott.local' +DEVICE_CERT = """ +-----BEGIN CERTIFICATE----- +MIICljCCAj2gAwIBAgIUHeVSXevXy8NaE4oW8FhHH66Q3EMwCgYIKoZIzj0EAwIw +XzELMAkGA1UEBhMCVUsxDzANBgNVBAcTBkxvbmRvbjEjMCEGA1UEChMaV2ViIG9m +IFRydXN0ZWQgVGhpbmdzLCBMdGQxGjAYBgNVBAMTEWNhMC1jYS53b3R0LmxvY2Fs +MB4XDTE5MDYyNjEwNTAwMFoXDTE5MDcwMzEwNTAwMFowezELMAkGA1UEBhMCVUsx +DzANBgNVBAgTBkxvbmRvbjEjMCEGA1UEChMaV2ViIG9mIFRydXN0ZWQgVGhpbmdz +LCBMdGQxNjA0BgNVBAMTLTc5OWQwMGQ4MjU0NDQ4OWViMDFiMzM5YzYxOGQwYjYy +LmQud290dC5sb2NhbDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHPR6u35nKxO ++YwmBud6tykF0FHEBu2XAN7KBGl9E0Ad3QH1GL9Xn9izpAhN9uRBMVzjriOpmEBn +IeWMThDRO3ejgbowgbcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUF +BwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS/FuaT7csgjr3m +jUetHjWVwK7O9DAfBgNVHSMEGDAWgBSpts1xq4g96OM2x5RvKrEUAIU3ATA4BgNV +HREEMTAvgi03OTlkMDBkODI1NDQ0ODllYjAxYjMzOWM2MThkMGI2Mi5kLndvdHQu +bG9jYWwwCgYIKoZIzj0EAwIDRwAwRAIgJpwOZymT7nzyJvIxhazoHQm7B/TBlR2X +A5NmBOpzKCACIAslvVt01ks2iONwHzG/2OSaDOfzlS4qeBnpgTWrXdZ9 +-----END CERTIFICATE----- +""" + +def create_client(): + # iot.DeviceManagerClient doesn't do this himself, unlike other Google libs. + if 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ: + with open(os.environ['GOOGLE_APPLICATION_CREDENTIALS']) as key: + key_json = json.load(key) + credentials = service_account.Credentials.from_service_account_info(key_json) + else: + credentials = None + + client = DeviceManagerClient(credentials=credentials) + return client + + +def create_registry(client, project_name, location, registry_name, pubsub_topic, ca_cert): + location_path = client.location_path(project_name, location) + registry_path = client.registry_path(project_name, location, registry_name) + try: + registry = client.get_device_registry(registry_path) + except NotFound: + print(f'Registry "{registry_path}" not found, creating.') + registry = types.DeviceRegistry( + id = registry_name, + # name should be empty + mqtt_config = types.MqttConfig( + mqtt_enabled_state = enums.MqttState.MQTT_ENABLED + ), + state_notification_config = types.StateNotificationConfig( + pubsub_topic_name = f"projects/{project_name}/topics/{pubsub_topic}" + ), + credentials = [types.RegistryCredential( + public_key_certificate=types.PublicKeyCertificate( + format=enums.PublicKeyCertificateFormat.X509_CERTIFICATE_PEM, + certificate=ca_cert + ) + )], + http_config = types.HttpConfig( + http_enabled_state=enums.HttpState.HTTP_DISABLED + ) + ) + registry = client.create_device_registry(location_path, registry) + return registry + + +def create_or_update_device(client, project_name, location_name, registry_name, device_id, device_cert): + device_name = client.device_path(project_name, location_name, registry_name, 'wott-' + device_id) + try: + device = client.get_device(device_name) + except NotFound: + print(f'Creating new device {device_name}') + device = types.Device( + id='wott-' + device_id, + # name should be empty + credentials=[ + types.DeviceCredential( + public_key=types.PublicKeyCredential( + format=enums.PublicKeyFormat.ES256_X509_PEM, + key=device_cert + ) + ) + ] + ) + registry_path = client.registry_path(project_name, location_name, registry_name) + device = client.create_device(registry_path, device) + else: + print(f'Updating device {device_name}') + device = types.Device( + name = device_name, + credentials=[ + types.DeviceCredential( + public_key=types.PublicKeyCredential( + format=enums.PublicKeyFormat.ES256_X509_PEM, + key=device_cert + ) + ) + ] + ) + client.update_device(device, types.FieldMask(paths=['credentials'])) + return device + + +client = create_client() +registry = create_registry(client, PROJECT, LOCATION, REGISTRY, PUBSUB, CA_CERT) +device = create_or_update_device(client, PROJECT, LOCATION, REGISTRY, DEVICE_ID, DEVICE_CERT) From b1be25f828f61288befab5fec645760277fd2320 Mon Sep 17 00:00:00 2001 From: Artem Martynovich Date: Fri, 28 Jun 2019 23:35:23 +0600 Subject: [PATCH 2/4] Add cmdline args. --- wott.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/wott.py b/wott.py index 87618b8..8a08393 100644 --- a/wott.py +++ b/wott.py @@ -1,3 +1,4 @@ +import argparse import json import os @@ -111,6 +112,28 @@ def create_or_update_device(client, project_name, location_name, registry_name, return device -client = create_client() -registry = create_registry(client, PROJECT, LOCATION, REGISTRY, PUBSUB, CA_CERT) -device = create_or_update_device(client, PROJECT, LOCATION, REGISTRY, DEVICE_ID, DEVICE_CERT) +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--project', + required=True, + help="IoT Project name.") + parser.add_argument( + '--location', + required=True, + help="IoT location region.") + parser.add_argument( + '--registry', + required=False, + default=REGISTRY, + help="IoT Registry name.") + parser.add_argument( + '--pubsub', + required=False, + default=PUBSUB, + help="pubsub name.") + args = parser.parse_args() + + client = create_client() + registry = create_registry(client, args.project, args.location, args.registry, args.pubsub, CA_CERT) + device = create_or_update_device(client, args.project, args.location, args.registry, DEVICE_ID, DEVICE_CERT) From 8f744e96623b4730a98950f683368def9bf71c67 Mon Sep 17 00:00:00 2001 From: Artem Martynovich Date: Fri, 28 Jun 2019 23:48:53 +0600 Subject: [PATCH 3/4] Get certs from WoTT. --- wott.py | 73 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/wott.py b/wott.py index 8a08393..75c8073 100644 --- a/wott.py +++ b/wott.py @@ -2,35 +2,19 @@ import json import os +import requests + from google.oauth2 import service_account from google.cloud.iot import DeviceManagerClient, types, enums from google.api_core.exceptions import NotFound +WOTT_ENDPOINT = os.getenv('WOTT_ENDPOINT', 'https://api.wott.io') PROJECT = 'wott-244904' LOCATION = 'europe-west1' REGISTRY = 'wott_registry' PUBSUB = 'wott-pubsub' -CA_CERT = "-----BEGIN CERTIFICATE-----\nMIICIjCCAcigAwIBAgIUVX6IR6IZkUV5c9uFtkCPqz/S/8IwCgYIKoZIzj0EAwIw\nWzELMAkGA1UEBhMCVUsxDzANBgNVBAcTBkxvbmRvbjEeMBwGA1UEChMVV2ViIG9m\nIFRydXN0ZWQgVGhpbmdzMRswGQYDVQQDExJyb290LWNhLndvdHQubG9jYWwwHhcN\nMTkwMTI0MTY0NjAwWhcNMjAwMTI0MTY0NjAwWjBfMQswCQYDVQQGEwJVSzEPMA0G\nA1UEBxMGTG9uZG9uMSMwIQYDVQQKExpXZWIgb2YgVHJ1c3RlZCBUaGluZ3MsIEx0\nZDEaMBgGA1UEAxMRY2EwLWNhLndvdHQubG9jYWwwWTATBgcqhkjOPQIBBggqhkjO\nPQMBBwNCAARtfUDIXmuqlXh/iUBASCMsuv/okVl5Rr411rtDVkJb9/pOXvytrLHO\npqRoaTb20vjvZJyIfIQHjYCVhqgydJ/0o2YwZDAOBgNVHQ8BAf8EBAMCAYYwEgYD\nVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUqbbNcauIPejjNseUbyqxFACFNwEw\nHwYDVR0jBBgwFoAUFFMJ5IilG4BJpHcho67y9faWbpQwCgYIKoZIzj0EAwIDSAAw\nRQIhALsFD/KDOFfMpiL4/8VA65quwJYPQIZZSljuFbjIadnJAiAJ5rYreEJ0A8du\nlTjJd3us3c4uqtFC+9lvbyvY7Kqsbg==\n-----END CERTIFICATE-----" -DEVICE_ID = '799d00d82544489eb01b339c618d0b62.d.wott.local' -DEVICE_CERT = """ ------BEGIN CERTIFICATE----- -MIICljCCAj2gAwIBAgIUHeVSXevXy8NaE4oW8FhHH66Q3EMwCgYIKoZIzj0EAwIw -XzELMAkGA1UEBhMCVUsxDzANBgNVBAcTBkxvbmRvbjEjMCEGA1UEChMaV2ViIG9m -IFRydXN0ZWQgVGhpbmdzLCBMdGQxGjAYBgNVBAMTEWNhMC1jYS53b3R0LmxvY2Fs -MB4XDTE5MDYyNjEwNTAwMFoXDTE5MDcwMzEwNTAwMFowezELMAkGA1UEBhMCVUsx -DzANBgNVBAgTBkxvbmRvbjEjMCEGA1UEChMaV2ViIG9mIFRydXN0ZWQgVGhpbmdz -LCBMdGQxNjA0BgNVBAMTLTc5OWQwMGQ4MjU0NDQ4OWViMDFiMzM5YzYxOGQwYjYy -LmQud290dC5sb2NhbDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHPR6u35nKxO -+YwmBud6tykF0FHEBu2XAN7KBGl9E0Ad3QH1GL9Xn9izpAhN9uRBMVzjriOpmEBn -IeWMThDRO3ejgbowgbcwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUF -BwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBS/FuaT7csgjr3m -jUetHjWVwK7O9DAfBgNVHSMEGDAWgBSpts1xq4g96OM2x5RvKrEUAIU3ATA4BgNV -HREEMTAvgi03OTlkMDBkODI1NDQ0ODllYjAxYjMzOWM2MThkMGI2Mi5kLndvdHQu -bG9jYWwwCgYIKoZIzj0EAwIDRwAwRAIgJpwOZymT7nzyJvIxhazoHQm7B/TBlR2X -A5NmBOpzKCACIAslvVt01ks2iONwHzG/2OSaDOfzlS4qeBnpgTWrXdZ9 ------END CERTIFICATE----- -""" + def create_client(): # iot.DeviceManagerClient doesn't do this himself, unlike other Google libs. @@ -112,6 +96,39 @@ def create_or_update_device(client, project_name, location_name, registry_name, return device +def get_ca_cert(debug): + ca = requests.get(f'{WOTT_ENDPOINT}/v0.2/ca-bundle') + + if debug: + print("[RECEIVED] Get CA Cert: {}".format(ca.status_code)) + print("[RECEIVED] Get CA Cert: {}".format(ca.content)) + + if not ca.ok: + print('Failed to get CA...') + print(ca.status_code) + print(ca.content) + return + + return ca.json()['ca_bundle'] + + +def get_device_cert(device_id, debug): + cert_request = requests.get(f'{WOTT_ENDPOINT}/v0.2/device-cert/{device_id}') + + if debug: + print("[RECEIVED] Get CA Cert: {}".format(cert_request.status_code)) + print("[RECEIVED] Get CA Cert: {}".format(cert_request.content)) + + if not cert_request.ok: + print('Failed to get CA...') + print(cert_request.status_code) + print(cert_request.content) + return + + return cert_request.content + + + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument( @@ -132,8 +149,20 @@ def create_or_update_device(client, project_name, location_name, registry_name, required=False, default=PUBSUB, help="pubsub name.") + parser.add_argument( + '--device', + required=False, + default='', + help="device id.") + parser.add_argument( + '--debug', + action="store_true", + help="debug mode.") args = parser.parse_args() + ca_cert = get_ca_cert(args.debug) client = create_client() - registry = create_registry(client, args.project, args.location, args.registry, args.pubsub, CA_CERT) - device = create_or_update_device(client, args.project, args.location, args.registry, DEVICE_ID, DEVICE_CERT) + registry = create_registry(client, args.project, args.location, args.registry, args.pubsub, ca_cert) + + device_cert = get_device_cert(args.device, args.debug) + device = create_or_update_device(client, args.project, args.location, args.registry, args.device, device_cert) From fe6c5e412e8ab2bbf16bb0991aad2ed643c22e98 Mon Sep 17 00:00:00 2001 From: Artem Martynovich Date: Sat, 29 Jun 2019 00:31:29 +0600 Subject: [PATCH 4/4] Create requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..206070c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests==2.20.1 +google-cloud-iot==0.2.0