diff --git a/agrirouter/auth/response.py b/agrirouter/auth/response.py index a711c4a6..9aac71b3 100644 --- a/agrirouter/auth/response.py +++ b/agrirouter/auth/response.py @@ -46,12 +46,14 @@ def verify(self, public_key) -> None: encoded_data = self._state + self._token unquoted_signature = unquote(self._signature) encoded_signature = base64.b64decode(unquoted_signature.encode("utf-8")) - self._was_verified = True + try: verify_signature(encoded_data, encoded_signature, public_key) except InvalidSignature: print("Response is invalid: invalid signature.") self._is_valid = False + finally: + self._was_verified = True self._is_valid = True diff --git a/agrirouter/messaging/certification.py b/agrirouter/messaging/certification.py index 5579b870..50de6778 100644 --- a/agrirouter/messaging/certification.py +++ b/agrirouter/messaging/certification.py @@ -5,10 +5,11 @@ import tempfile -from agrirouter.onboarding.response import BaseOnboardingResonse +from agrirouter.onboarding.response import SoftwareOnboardingResponse -def create_certificate_file(onboard_response: BaseOnboardingResonse): +def create_certificate_file_from_pen(onboard_response: SoftwareOnboardingResponse): + dir_ = tempfile.mkdtemp() prefix = onboard_response.get_sensor_alternate_id() data = onboard_response.get_authentication().get_certificate() diff --git a/agrirouter/messaging/clients/http.py b/agrirouter/messaging/clients/http.py index 08530c77..fa3110e9 100644 --- a/agrirouter/messaging/clients/http.py +++ b/agrirouter/messaging/clients/http.py @@ -1,16 +1,62 @@ +import http.client +import json +import os +import ssl + +from agrirouter.messaging.certification import create_certificate_file_from_pen +from agrirouter.onboarding.dto import ConnectionCriteria +from agrirouter.onboarding.response import SoftwareOnboardingResponse + + class HttpClient: headers = {"Content-Type": "application/json"} - def __init__(self, - on_message_callback: callable, - timeout=20 - ): + def __init__( + self, + on_message_callback: callable, + timeout=20 + ): self.on_message_callback = on_message_callback self.timeout = timeout - def publish(self): - pass + @staticmethod + def make_connection(certificate_file_path: str, onboard_response: SoftwareOnboardingResponse): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.load_cert_chain( + certfile=certificate_file_path, + keyfile=certificate_file_path, + password=onboard_response.get_authentication().get_secret(), + ) + connection = http.client.HTTPSConnection( + host=onboard_response.connection_criteria.get_host(), + port=onboard_response.connection_criteria.get_port(), + context=context + ) + return connection + + def send(self, method: str, onboard_response: SoftwareOnboardingResponse, request_body=None): + certificate_file_path = create_certificate_file_from_pen(onboard_response) + try: + connection = self.make_connection(certificate_file_path, onboard_response) + if request_body is not None: + connection.request( + method=method, + url=onboard_response.get_connection_criteria().get_measures(), + headers=self.headers, + body=json.dumps(request_body) + ) + else: + connection.request( + method=method, + url=onboard_response.get_connection_criteria().get_measures(), + headers=self.headers, + ) + response = connection.getresponse() + finally: + os.remove(certificate_file_path) + + return response def subscribe(self): pass diff --git a/agrirouter/messaging/services/commons.py b/agrirouter/messaging/services/commons.py index 3e368632..939a6fdc 100644 --- a/agrirouter/messaging/services/commons.py +++ b/agrirouter/messaging/services/commons.py @@ -4,6 +4,7 @@ import requests from agrirouter.messaging.certification import create_certificate_file +from agrirouter.messaging.clients.http import HttpClient from agrirouter.messaging.clients.mqtt import MqttClient from agrirouter.messaging.messages import Message from agrirouter.messaging.request import MessageRequest @@ -32,21 +33,16 @@ def send(self, parameters): class HttpMessagingService(AbstractMessagingClient): + def __init__(self, on_message_callback, timeout): + self.client = HttpClient(on_message_callback=on_message_callback, timeout=timeout) + def send(self, parameters) -> MessagingResult: request = self.create_message_request(parameters) - cert_file_path = create_certificate_file(parameters.get_onboarding_response()) - try: - response = requests.post( - url=parameters.get_onboarding_response().get_connection_criteria().get_measures(), - headers={"Content-type": "application/json"}, - data=request.json_serialize(), - cert=( - cert_file_path, - parameters.get_onboarding_response().get_authentication().get_secret() - ), - ) - finally: - os.remove(cert_file_path) + response = self.client.send( + "POST", + parameters.get_onboarding_response(), + request + ) result = MessagingResult([parameters.get_message_id()]) return result diff --git a/agrirouter/messaging/services/http/outbox.py b/agrirouter/messaging/services/http/outbox.py index b81bfcbf..72328072 100644 --- a/agrirouter/messaging/services/http/outbox.py +++ b/agrirouter/messaging/services/http/outbox.py @@ -2,26 +2,23 @@ import requests +from agrirouter.messaging.clients.http import HttpClient from agrirouter.messaging.result import OutboxResponse -from agrirouter.messaging.certification import create_certificate_file +from agrirouter.messaging.certification import create_certificate_file_from_pen class OutboxService: + def __init__(self, on_message_callback, timeout): + self.client = HttpClient(on_message_callback=on_message_callback, timeout=timeout) + def fetch(self, onboarding_response) -> OutboxResponse: - cert_file_path = create_certificate_file(onboarding_response) - try: - response = requests.get( - url=onboarding_response.get_connection_criteria().get_commands(), - headers={"Content-type": "application/json"}, - cert=( - cert_file_path, - onboarding_response.get_authentication().get_secret() - ), - ) - finally: - os.remove(cert_file_path) + response = self.client.send( + "GET", + onboarding_response, + None + ) outbox_response = OutboxResponse(status_code=response.status_code) outbox_response.json_deserialize(response.json()["contents"]) diff --git a/agrirouter/onboarding/dto.py b/agrirouter/onboarding/dto.py new file mode 100644 index 00000000..233c907e --- /dev/null +++ b/agrirouter/onboarding/dto.py @@ -0,0 +1,289 @@ +import json +from typing import Union + +from agrirouter.messaging.exceptions import WrongFieldError + + +class ConnectionCriteria: + CLIENT_ID = 'clientId' + COMMANDS = 'commands' + GATEWAY_ID = 'gatewayId' + HOST = 'host' + MEASURES = 'measures' + PORT = 'port' + + def __init__(self, + *, + gateway_id: str = None, + measures: str = None, + commands: str = None, + host: str = None, + port: str = None, + client_id: str = None + ): + self.gateway_id = gateway_id + self.measures = measures + self.commands = commands + self.host = host + self.port = port + self.client_id = client_id + + def json_serialize(self) -> dict: + return { + self.GATEWAY_ID: self.gateway_id, + self.MEASURES: self.measures, + self.COMMANDS: self.commands, + self.PORT: self.port, + self.CLIENT_ID: self.client_id + } + + def json_deserialize(self, data: Union[str, dict]) -> None: + data = data if type(data) == dict else json.loads(data) + for key, value in data.items(): + if key == self.GATEWAY_ID: + self.gateway_id = value + elif key == self.MEASURES: + self.measures = value + elif key == self.COMMANDS: + self.commands = value + elif key == self.HOST: + self.host = value + elif key == self.PORT: + self.port = value + elif key == self.CLIENT_ID: + self.client_id = value + else: + raise WrongFieldError(f"Unknown field {key} for Connection Criteria class") + + def get_gateway_id(self) -> str: + return self.gateway_id + + def set_gateway_id(self, gateway_id: str) -> None: + self.gateway_id = gateway_id + + def get_measures(self) -> str: + return self.measures + + def set_measures(self, measures: str) -> None: + self.measures = measures + + def get_commands(self) -> str: + return self.commands + + def set_commands(self, commands: str) -> None: + self.commands = commands + + def get_host(self) -> str: + return self.host + + def set_host(self, host: str) -> None: + self.host = host + + def get_port(self) -> str: + return self.port + + def set_port(self, port: str) -> None: + self.port = port + + def get_client_id(self) -> str: + return self.client_id + + def set_client_id(self, client_id: str) -> None: + self.client_id = client_id + + +class Authentication: + TYPE = 'type' + SECRET = 'secret' + CERTIFICATE = 'certificate' + + def __init__(self, + *, + type: str = None, + secret: str = None, + certificate: str = None, + ): + self.type = type + self.secret = secret + self.certificate = certificate + + def json_serialize(self) -> dict: + return { + self.TYPE: self.type, + self.SECRET: self.secret, + self.CERTIFICATE: self.certificate, + } + + def json_deserialize(self, data: Union[str, dict]) -> None: + data = data if type(data) == dict else json.loads(data) + for key, value in data.items(): + if key == self.TYPE: + self.type = value + elif key == self.SECRET: + self.secret = value + elif key == self.CERTIFICATE: + self.certificate = value + else: + raise WrongFieldError(f"Unknown field {key} for Authentication class") + + def get_type(self) -> str: + return self.type + + def set_type(self, type: str) -> None: + self.type = type + + def get_secret(self) -> str: + return self.secret + + def set_secret(self, secret: str) -> None: + self.secret = secret + + def get_certificate(self) -> str: + return self.certificate + + def set_certificate(self, certificate: str) -> None: + self.certificate = certificate + + +class AuthorizationResultUrl: + def __init__(self, + *, + state: str = None, + signature: str = None, + token: str = None, + error: str = None + ): + self.state = state + self.signature = signature + self.token = token + self.error = error + + def get_state(self) -> str: + return self.state + + def set_state(self, state: str) -> None: + self.state = state + + def get_signature(self) -> str: + return self.signature + + def set_signature(self, signature: str) -> None: + self.signature = signature + + def get_token(self) -> str: + return self.token + + def set_token(self, token: str) -> None: + self.token = token + + def get_error(self) -> str: + return self.error + + def set_error(self, error: str) -> None: + self.error = error + + +class AuthorizationToken: + ACCOUNT = 'account' + REGISTRATION_CODE = 'regcode' + EXPIRES = 'expires' + + def __init__(self, + *, + account: str = None, + regcode: str = None, + expires: str = None + ): + self.account = account + self.regcode = regcode + self.expires = expires + + def json_deserialize(self, data: Union[str, dict]) -> None: + data = data if type(data) == dict else json.loads(data) + for key, value in data.items(): + if key == self.ACCOUNT: + self.account = value + elif key == self.REGISTRATION_CODE: + self.regcode = value + elif key == self.EXPIRES: + self.expires = value + else: + raise WrongFieldError(f"Unknown field {key} for AuthorizationToken class") + + def get_account(self) -> str: + return self.account + + def set_account(self, account: str) -> None: + self.account = account + + def get_regcode(self) -> str: + return self.regcode + + def set_regcode(self, regcode: str) -> None: + self.regcode = regcode + + def get_expires(self) -> str: + return self.expires + + def set_expires(self, expires: str) -> None: + self.expires = expires + + +class AuthorizationResult: + def __init__(self, + *, + authorization_url: str = None, + state: str = None, + ): + self.authorization_url = authorization_url + self.state = state + + def get_authorization_url(self) -> str: + return self.authorization_url + + def set_authorization_url(self, authorization_url: str) -> None: + self.authorization_url = authorization_url + + def get_state(self) -> str: + return self.state + + def set_state(self, state: str) -> None: + self.state = state + + +class ErrorResponse: + def __init__(self, + *, + code, + message, + target, + details + ): + self.code = code + self.message = message + self.target = target + self.details = details + + def get_code(self) -> str: + return self.code + + def set_code(self, code: str) -> None: + self.code = code + + def get_message(self) -> str: + return self.message + + def set_message(self, message: str) -> None: + self.message = message + + def get_target(self) -> str: + return self.target + + def set_target(self, target: str) -> None: + self.target = target + + def get_details(self) -> str: + return self.details + + def set_details(self, details: str) -> None: + self.details = details diff --git a/agrirouter/onboarding/headers.py b/agrirouter/onboarding/headers.py index a1e0d61c..a19fe008 100644 --- a/agrirouter/onboarding/headers.py +++ b/agrirouter/onboarding/headers.py @@ -35,10 +35,8 @@ def __init__(self, def get_header(self) -> dict: return self.params - def sign(self, signature: bytes): - print(signature) - self.params["X-Agrirouter-Signature"] = base64.b64encode(signature).decode() - print(self.params["X-Agrirouter-Signature"]) + def sign(self, signature: str): + self.params["X-Agrirouter-Signature"] = signature def _set_params(self, reg_code: str, application_id: str, signature: str, content_type: str): header = dict() diff --git a/agrirouter/onboarding/onboarding.py b/agrirouter/onboarding/onboarding.py index 155d08dc..ad07b896 100644 --- a/agrirouter/onboarding/onboarding.py +++ b/agrirouter/onboarding/onboarding.py @@ -30,11 +30,11 @@ def _create_request(self, params: BaseOnboardingParameter, url: str) -> Software def _perform_request(self, params: BaseOnboardingParameter, url: str) -> requests.Response: request = self._create_request(params, url) - request.sign(self._private_key) + request.sign(self._private_key, self._public_key) if request.is_signed: return requests.post( url=request.get_url(), - data=json.dumps(request.get_data()), + json=request.get_data(), headers=request.get_header() ) raise RequestNotSigned diff --git a/agrirouter/onboarding/request.py b/agrirouter/onboarding/request.py index 1e7113b0..e02e0ec7 100644 --- a/agrirouter/onboarding/request.py +++ b/agrirouter/onboarding/request.py @@ -1,6 +1,6 @@ from agrirouter.onboarding.headers import SoftwareOnboardingHeader, BaseOnboardingHeader from agrirouter.onboarding.request_body import SoftwareOnboardingBody, BaseOnboardingBody -from agrirouter.onboarding.signature import create_signature +from agrirouter.onboarding.signature import create_signature, verify_signature class BaseOnboardingRequest: @@ -18,8 +18,10 @@ def get_data(self): def get_header(self): return self.header.get_header() - def sign(self, private_key): - signature = create_signature(self.body.json(), private_key) + def sign(self, private_key, public_key): + body = self.body.json().replace("\n", "") + signature = create_signature(body, private_key) + verify_signature(body, bytes.fromhex(signature), public_key) self.header.sign(signature) @property diff --git a/agrirouter/onboarding/response.py b/agrirouter/onboarding/response.py index cab6bca0..50826c4c 100644 --- a/agrirouter/onboarding/response.py +++ b/agrirouter/onboarding/response.py @@ -1,59 +1,92 @@ from requests import Response +from agrirouter.onboarding.dto import ErrorResponse, ConnectionCriteria, Authentication + class BaseOnboardingResonse: def __init__(self, http_response: Response): - self.response: Response = http_response - - def get_connection_criteria(self) -> dict: - response_data = self.data() - return response_data.get("connectionCriteria") - - def get_sensor_alternate_id(self): - response_data = self.data() - return response_data.get("sensorAlternateId") - - def get_authentication(self): - response_data = self.data() - return response_data.get("authentication") - @property - def data(self): - return self.response.json() + self._status_code = http_response.status_code + self._text = http_response.text @property def status_code(self): - return self.response.status_code + return self._status_code @property def text(self): - return self.response.text + return self._text class SoftwareVerifyOnboardingResponse(BaseOnboardingResonse): """ Response from verify request used for Farming Software or Telemetry Platform before onboarding """ - pass + + def __init__(self, http_response: Response): + super(SoftwareVerifyOnboardingResponse, self).__init__(http_response) + response_body = http_response.json() + + self.account_id = response_body.get("accountId", None) + + self.error = ErrorResponse( + code=response_body.get("error").get("code"), + message=response_body.get("error").get("message"), + target=response_body.get("error").get("target"), + details=response_body.get("error").get("details"), + ) if response_body.get("error", None) else None + + def get_account_id(self) -> str: + return self.account_id class SoftwareOnboardingResponse(BaseOnboardingResonse): """ Response from onboarding request used for Farming Software or Telemetry Platform """ - - def get_connection_criteria(self) -> dict: - response_data = self.data() - return response_data.get("connectionCriteria") - - def get_sensor_alternate_id(self): - response_data = self.data() - return response_data.get("sensorAlternateId") - - def get_authentication(self): - response_data = self.data() - return response_data.get("authentication") + def __init__(self, http_response: Response): + super(SoftwareOnboardingResponse, self).__init__(http_response) + response_body = http_response.json() + + self.connection_criteria = ConnectionCriteria( + gateway_id=response_body.get("connectionCriteria").get("gatewayId"), + measures=response_body.get("connectionCriteria").get("measures"), + commands=response_body.get("connectionCriteria").get("commands"), + host=response_body.get("connectionCriteria").get("host"), + ) if response_body.get("connectionCriteria", None) else None + + self.authentication = Authentication( + type=response_body.get("authentication").get("type"), + secret=response_body.get("authentication").get("secret"), + certificate=response_body.get("authentication").get("certificate") + ) if response_body.get("authentication", None) else None + + self.capability_alternate_id = response_body.get("capabilityAlternateId", None) + self.device_alternate_id = response_body.get("deviceAlternateId", None) + self.sensor_alternate_id = response_body.get("sensorAlternateId", None) + + self.error = ErrorResponse( + code=response_body.get("error").get("code"), + message=response_body.get("error").get("message"), + target=response_body.get("error").get("target"), + details=response_body.get("error").get("details"), + ) if response_body.get("error", None) else None + + def get_connection_criteria(self) -> ConnectionCriteria: + return self.connection_criteria + + def get_authentication(self) -> Authentication: + return self.authentication + + def get_sensor_alternate_id(self) -> str: + return self.sensor_alternate_id + + def get_device_alternate_id(self) -> str: + return self.device_alternate_id + + def get_capability_alternate_id(self) -> str: + return self.capability_alternate_id class CUOnboardingResponse(BaseOnboardingResonse): diff --git a/agrirouter/onboarding/signature.py b/agrirouter/onboarding/signature.py index 6bedc3bd..64174adc 100644 --- a/agrirouter/onboarding/signature.py +++ b/agrirouter/onboarding/signature.py @@ -2,20 +2,24 @@ from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding -from pprint import pprint SIGNATURE_ALGORITHM = "SHA256withRSA" -def create_signature(request_body: str, private_key: str) -> bytes: +def to_hex(sign: bytes): + return sign.hex() + + +def create_signature(request_body: str, private_key: str) -> str: private_key_bytes = bytearray(private_key.encode('utf-8')) private_key_data = load_pem_private_key(private_key_bytes, None) signature = private_key_data.sign( - request_body.encode('utf-8'), + request_body.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256() ) - return signature + + return to_hex(signature) def verify_signature(request_body: str, signature: bytes, public_key: str) -> None: diff --git a/examples.txt b/examples.txt index f0fa895a..e59fe910 100644 --- a/examples.txt +++ b/examples.txt @@ -1,3 +1,44 @@ + +public_key = "-----BEGIN PUBLIC KEY-----\n" \ + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvFlVFgYRIkGL6Ay/av2e\n" \ + "S2yIag7XHRWFgVFewPegFyWjUQPSe5t2unrNOUu6Ucp7/ck/Ivm4c/6g39fDDzmq\n" \ + "i4JU8OfMpVbUxpiJSGa/OSiXnDuWkJyjdac/C8ip0EpOCFjAWdE+pnGhDny1XAwp\n" \ + "i4t0/WtO8U+IOYtjxpyyOp3daX97C7ihM1I6eOecVN6Caz9B38EnPg12UGA5NkZO\n" \ + "pnz4BHMwYUZqgxaeOPlh4MquAnF5fdjOV3TkmFWkbP1un3BJkU6owcadbjN5DQCG\n" \ + "jguFzX8VVfJEgn2VtIFbbhqsRivvNDmWst1XNZ0GRpviFFQRymz1WroV0lB9P9vK\n" \ + "mwIDAQAB\n" \ + "-----END PUBLIC KEY-----" + +private_key = "-----BEGIN PRIVATE KEY-----\n" \ + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8WVUWBhEiQYvo\n" \ + "DL9q/Z5LbIhqDtcdFYWBUV7A96AXJaNRA9J7m3a6es05S7pRynv9yT8i+bhz/qDf\n" \ + "18MPOaqLglTw58ylVtTGmIlIZr85KJecO5aQnKN1pz8LyKnQSk4IWMBZ0T6mcaEO\n" \ + "fLVcDCmLi3T9a07xT4g5i2PGnLI6nd1pf3sLuKEzUjp455xU3oJrP0HfwSc+DXZQ\n" \ + "YDk2Rk6mfPgEczBhRmqDFp44+WHgyq4CcXl92M5XdOSYVaRs/W6fcEmRTqjBxp1u\n" \ + "M3kNAIaOC4XNfxVV8kSCfZW0gVtuGqxGK+80OZay3Vc1nQZGm+IUVBHKbPVauhXS\n" \ + "UH0/28qbAgMBAAECggEANVX8vMBaEL/L/RnDCOqp7UTeOl5adx91j2G5+d4FhRiA\n" \ + "73usGpmzHOqSe/OgXvH+e6cGDIL3w00rREgGsiSL0XbGU/PoJTf6CAUA9zI1W1vN\n" \ + "1w2evPPGbBZAybb4s4WfJEjxq12QJrUNvRr+hoLhLuV+axb8o2P4uQbqab9Mz0ER\n" \ + "lczCbHi4VDs1fwmNR3o47T1J4Qffzv1nMlor3pSrDzRDebic7/DC5JFkYZNGUtHk\n" \ + "jKDF5Uv7Vzxgb4Of+i3JA5mRMqvG33pdenvvetwl9X69WOiC29bVlymSHyybBE4A\n" \ + "ItfCAHIiY3nUL7UqzoIXpsyPs3ftkiy3Hn7isVSpLQKBgQDjadkGlqIgXCKZ8RS6\n" \ + "a4iLTTTlh8Ur+vMrejBLPul1oxz2dRWZy8zykfNN2MPz7q2xT8wXGuxgj+jei/fi\n" \ + "Gk08+UudMhV5Dtshb3fFq0NFCBe1ZUEX/wAcKC4Ed9xuuHpe7HOKAG0AsnzS8MPC\n" \ + "lcMiL1/vz0GuRbsiyMY6hXweZQKBgQDUBmQNqOBWDTQkO/8MFHopo6Ju9iNvZ4fC\n" \ + "u4SWqL+5BO3nnQHAQyslsj8FNilqhgMI+zaFFbZMZPv5opBSaAR0CQanKxMe3c9I\n" \ + "XYkAJH2+M0fpp80LtxwShD411UDhIypzumfKe8vUXRW/8TWfl6VidfEVjxw6Rc2D\n" \ + "g9btI4k0/wKBgQC42plnGZq/4yTdLXJD9pUPZrrQuQQ1M8/mT3RiNclfri8kxxe/\n" \ + "5EG8C5dSeBkQd7sInmyve1sZQuFvxSbBy89s+NfV95gsxz6odwtMymHsAyACe0Pm\n" \ + "VYmpWZ/OUgAEoEAYWOuyCZaRMoT0knEOAt6TMx8wt7AUEOqE497+QvMZYQKBgQC6\n" \ + "ARlJenvEQjUaDKBFYrmBShK4MasIktThG0zINyZrFE35wR3GI6b4nRT4Z3mSABst\n" \ + "h+Vef5u8DWOYrurZwHMXsMtrYDiX/ZNZMuV7gIfnkmlmLFWQD4XLIMTKyVjvqcAW\n" \ + "YtOnKU+58CeiieO3LHxkkn97oF7tKEuRMtock+5M1QKBgC2fquqxXMrBEIoMGCZs\n" \ + "ooU5V9gOjFVKC52VWnTNgmOWTqgZuqxPJtCTN5wPvhOSggQuHPwBHa9ioshJ0dGE\n" \ + "6jdxGaJjAc82q2KZu9VEqoH/Xa2aS8dPEHwfJtzUVTia6WkrFtMFNaDMFd6byWDQ\n" \ + "ai+T4i2J3/SDL0BfsFWdQuje\n" \ + "-----END PRIVATE KEY-----" + + >>> private_key = ... # store here your private key you get in AR UI during application creation >>> public_key = ... # store here your public key you get in AR UI during application creation >>> application_id = "8c947a45-c57d-4fd2-affc-206e2sdg3a50" # # store here your application id. You can find it in AR UI @@ -12,13 +53,13 @@ >>> auth_client = ar.Authorization("QA", public_key=public_key, private_key=private_key) >>> auth_url = auth_client.get_auth_request_url(auth_params) # use this url to authorize the user as described at https://docs.my-agrirouter.com/agrirouter-interface-documentation/latest/integration/authorization.html#perform-authorization ->>> auth_result_url = ... # the url the user was redirected after his authorization. ->>> auth_response = auth_client.extract_auth_response(auth_result_url) # auth_response containing the results of the auth process +>>> auth_result_url = ... # the url the user was redirected to after his authorization. +>>> auth_response = auth_client.extract_auth_response(auth_result_url) # auth_response contains the results of the auth process >>> auth_client.verify_auth_response(auth_response) # you may verify auth_response to ensure answer was from AR ->>> auth_response.is_successfull # True if user accepted application, False if he rejected +>>> auth_response.is_successful # True if user accepted application, False if he rejected True ->>> auth_response.is_valid # Result of verification, if False, response was not validated by public key. Doesn't indicate was the auth successfull. Accessible only after response verifying +>>> auth_response.is_valid # Result of verification, if False, response was not validated by public key. Doesn't indicate the auth was successfull. Accessible only after response verifying True >>> # Get dict containing data from auth process you will use for futher communication. @@ -45,15 +86,16 @@ True >>> id_ = "mydeviceid" >>> certification_version_id = ... # get from AR UI ->>> utc_timestamp = "2018-06-20T07:29:23.457Z" >>> time_zone = "+03:00" >>> onboarding_client = ar.SoftwareOnboarding("QA", public_key=public_key, private_key=private_key) ->>> onboarding_parameters = ar.SoftwareOnboardingParameter(id_=id_, application_id=application_id, certification_version_id=certification_version_id, gateway_id=GateWays.REST.value, utc_timestamp=utc_timestamp, time_zone=time_zone, reg_code=auth_data["credentials"]["regcode"]) +>>> onboarding_parameters = ar.SoftwareOnboardingParameter(id_=id_, application_id=application_id, certification_version_id=certification_version_id, gateway_id=GateWays.REST.value, time_zone=time_zone, reg_code=auth_data["credentials"]["regcode"]) >>> onboarding_verifying_response = onboarding_client.verify(onboarding_parameters) +>>> onboarding_verifying_response.status_code +>>> onboarding_verifying_response.text >>> onboarding_response = onboarding_client.onboard(onboarding_parameters) ->>> onboarding_response.status_code() ->>> onboarding_response.data() # or onboarding_response.text() +>>> onboarding_response.status_code +>>> onboarding_response.text { "authentication": { @@ -69,4 +111,4 @@ True }, "deviceAlternateId": "c067272a-d3a7-4dcf-ab58-5c45ba66ad60", "sensorAlternateId": "5564ce96-385f-448a-9502-9ea3c940a259" -} \ No newline at end of file +}