async def authenticate_message(request, message_tree, message_payload, fingerprint_lookup): if request.secure and 'ven_id' in message_payload: print("Getting cert fingerprint from request") connection_fingerprint = utils.get_cert_fingerprint_from_request( request) print("Checking cert fingerprint") if connection_fingerprint is None: msg = ( "Your request must use a client side SSL certificate, of which the " "fingerprint must match the fingerprint that you have given to this VTN" ) raise errors.NotRegisteredOrAuthorizedError(msg) try: ven_id = message_payload.get('ven_id') expected_fingerprint = fingerprint_lookup(ven_id) if iscoroutine(expected_fingerprint): expected_fingerprint = await expected_fingerprint except ValueError: msg = ( f"Your venID {ven_id} is not known to this VTN. Make sure you use the venID " "that you receive from this VTN during the registration step") raise errors.NotRegisteredOrAuthorizedError(msg) if expected_fingerprint is None: msg = ( "This VTN server does not know what your certificate fingerprint is. Please " "deliver your fingerprint to the VTN (outside of OpenADR). You used the " "following fingerprint to make this request:") raise errors.NotRegisteredOrAuthorizedError(msg) print("Checking connection fingerprint") if connection_fingerprint != expected_fingerprint: msg = ( f"The fingerprint of your HTTPS certificate {connection_fingerprint} " f"does not match the expected fingerprint {expected_fingerprint}" ) raise errors.NotRegisteredOrAuthorizedError(msg) print("Checking message fingerprint") message_cert = utils.extract_pem_cert(message_tree) message_fingerprint = utils.certificate_fingerprint(message_cert) if message_fingerprint != expected_fingerprint: msg = ( f"The fingerprint of the certificate used to sign the message " f"{message_fingerprint} did not match the fingerprint that this " f"VTN has for you {expected_fingerprint}. Make sure you use the correct " "certificate to sign your messages.") raise errors.NotRegisteredOrAuthorizedError(msg) print("Validating XML signature") try: validate_xml_signature(message_tree) except ValueError: msg = ( "The message signature did not match the message contents. Please make sure " "you are using the correct XMLDSig algorithm and C14n canonicalization." ) raise errors.NotRegisteredOrAuthorizedError(msg)
async def handler(self, request): """ Handle all incoming POST requests. """ try: # Check the Content-Type header content_type = request.headers.get('content-type', '') if not content_type.lower().startswith("application/xml"): raise errors.HTTPError(status=HTTPStatus.BAD_REQUEST, description="The Content-Type header must be application/xml; " f"you provided {request.headers.get('content-type', '')}") content = await request.read() hooks.call('before_parse', content) # Validate the message to the XML Schema message_tree = validate_xml_schema(content) # Parse the message to a type and payload dict message_type, message_payload = parse_message(content) if message_type == 'oadrResponse': raise errors.SendEmptyHTTPResponse() if 'vtn_id' in message_payload \ and message_payload['vtn_id'] is not None \ and message_payload['vtn_id'] != self.vtn_id: raise errors.InvalidIdError(f"The supplied vtnID is invalid. It should be '{self.vtn_id}', " f"you supplied {message_payload['vtn_id']}.") # Check if we know this VEN, ask for reregistration otherwise if message_type not in ('oadrCreatePartyRegistration', 'oadrQueryRegistration') \ and 'ven_id' in message_payload and hasattr(self, 'ven_lookup'): result = await utils.await_if_required(self.ven_lookup(ven_id=message_payload['ven_id'])) if result is None or result.get('registration_id', None) is None: raise errors.RequestReregistration(message_payload['ven_id']) # Authenticate the message if request.secure and 'ven_id' in message_payload: if hasattr(self, 'fingerprint_lookup'): await authenticate_message(request, message_tree, message_payload, fingerprint_lookup=self.fingerprint_lookup) elif hasattr(self, 'ven_lookup'): await authenticate_message(request, message_tree, message_payload, ven_lookup=self.ven_lookup) else: logger.error("Could not authenticate this VEN because " "you did not provide a 'ven_lookup' function. Please see " "https://openleadr.org/docs/server.html#signing-messages for info.") # Pass the message off to the handler and get the response type and payload try: # Add the request fingerprint to the message so that the handler can check for it. if request.secure and message_type == 'oadrCreatePartyRegistration': message_payload['fingerprint'] = utils.get_cert_fingerprint_from_request(request) response_type, response_payload = await self.handle_message(message_type, message_payload) except Exception as err: logger.error("An exception occurred during the execution of your " f"{self.__class__.__name__} handler: " f"{err.__class__.__name__}: {err}") raise err if 'response' not in response_payload: response_payload['response'] = {'response_code': 200, 'response_description': 'OK', 'request_id': message_payload.get('request_id')} response_payload['vtn_id'] = self.vtn_id if 'ven_id' not in response_payload: response_payload['ven_id'] = message_payload.get('ven_id') except errors.RequestReregistration as err: response_type = 'oadrRequestReregistration' response_payload = {'ven_id': err.ven_id} msg = self._create_message(response_type, **response_payload) response = web.Response(text=msg, status=HTTPStatus.OK, content_type='application/xml') except errors.SendEmptyHTTPResponse: response = web.Response(text='', status=HTTPStatus.OK, content_type='application/xml') except errors.ProtocolError as err: # In case of an OpenADR error, return a valid OpenADR message response_type, response_payload = self.error_response(message_type, err.response_code, err.response_description) msg = self._create_message(response_type, **response_payload) response = web.Response(text=msg, status=HTTPStatus.OK, content_type='application/xml') except errors.HTTPError as err: # If we throw a http-related error, deal with it here response = web.Response(text=err.response_description, status=err.response_code) except XMLSyntaxError as err: logger.warning(f"XML schema validation of incoming message failed: {err}.") response = web.Response(text=f'XML failed validation: {err}', status=HTTPStatus.BAD_REQUEST) except errors.FingerprintMismatch as err: logger.warning(err) response = web.Response(text=str(err), status=HTTPStatus.FORBIDDEN) except InvalidSignature: logger.warning("Incoming message had invalid signature, ignoring.") response = web.Response(text='Invalid Signature', status=HTTPStatus.FORBIDDEN) except Exception as err: # In case of some other error, return a HTTP 500 logger.error(f"The VTN server encountered an error: {err.__class__.__name__}: {err}") response = web.Response(status=HTTPStatus.INTERNAL_SERVER_ERROR) else: # We've successfully handled this message msg = self._create_message(response_type, **response_payload) response = web.Response(text=msg, status=HTTPStatus.OK, content_type='application/xml') hooks.call('before_respond', response.text) return response