mobilityhouse / ocpp

Python implementation of the Open Charge Point Protocol (OCPP).
MIT License
788 stars 310 forks source link

Send a TriggerMessage after sending a CALLERROR : Security Error #295

Open jainmohit2001 opened 2 years ago

jainmohit2001 commented 2 years ago

I wanted to handle the case when the CP is booted and the first message it sends is not a BootNotification request. So the procedure from the CSMS side should be : Check if BootNotification is recorded before or not. I am doing this using a DB call and storing the latest BootNotification call timestamp in it. So if a new CP gets connected and it doesn't send a BootNotification first, then the value is NULL and the CSMS shall raise a SecurityError. After raising the security error, a CALLERROR is sent to the CP. And after that, I should be able to call a trigger message somehow. Can you help me find the place where should I call trigger message function? I thought of using a @after tag, but the code is unreachable.

My @on(Authorize) code

@on(Action.Authorize)
async def on_authorize(self, id_tag:str,*args, **kwargs):
  await self.check_boot_status_accepted()
  return call_result.AuthorizePayload(
      id_tag_info={
          'status': AuthorizationStatus.accepted
      }
  )

My check_boot_status_accepted() function

async def check_boot_status_accepted(self):
    if not self.boot_status == RegistrationStatus.accepted.value:
        self.trigger_message_for = MessageTrigger.boot_notification
        asyncio.create_task(self.trigger_message(MessageTrigger.boot_notification))
        raise SecurityError(description="No BootNotification received!")
    self.trigger_message_for = None
    return True

The reason why @after doesn't work: (in the charge_point.py file)

try:
    handler = handlers['_on_action']
except KeyError:
    raise NotImplementedError(f"No handler for '{msg.action}' "
                              "registered.")

try:
    response = handler(**snake_case_payload)
    if inspect.isawaitable(response):
        response = await response
except Exception as e:
    LOGGER.exception("Error while handling request '%s'", msg)
    response = msg.create_call_error(e).to_json()
    await self._send(response)

    return

CSMS log:

14:24:02,149 websockets.server INFO server listening on 0.0.0.0:9000
14:24:08,787 websockets.server INFO connection open
14:24:08,787 root INFO Protocols Matched: ocpp1.6
14:24:08,789 root INFO Charger CP_1 connected.
14:24:08,796 ocpp INFO CP_1: receive message [2,"9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a","Authorize",{"idTag":"id_tag"}]
14:24:08,874 ocpp INFO CP_1: send [2,"2dca0ac9-6405-4855-a95c-e3f7059b2cdf","TriggerMessage",{"requestedMessage":"BootNotification"}]
14:24:18,814 ocpp ERROR Error while handling request '<Call - unique_id=9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a, action=Authorize, payload={'idTag': 'id_tag'}>'
Traceback (most recent call last):
  File "D:\evy/dev/cms-ocpp\ocpp\charge_point.py", line 196, in _handle_call
    response = await response
  File "D:\evy/dev/cms-ocpp\csms\v16\central_system\charge_point_handler.py", line 52, in on_authorize
    await self.check_boot_status_accepted()
  File "D:\evy/dev/cms-ocpp\csms\v16\central_system\charge_point_handler.py", line 28, in check_boot_status_accepted
    raise SecurityError(description="No BootNotification received!")
ocpp.exceptions.SecurityError: SecurityError: No BootNotification received!, {}
14:24:18,858 ocpp INFO CP_1: send [4,"9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a","SecurityError","No BootNotification received!",{}]
14:24:18,859 ocpp INFO CP_1: receive message [3,"2dca0ac9-6405-4855-a95c-e3f7059b2cdf",{"status":"Accepted"}]
14:24:18,861 ocpp INFO CP_1: receive message [2,"3949a5cb-40fb-4b06-9673-f00b11331500","BootNotification",{"chargePointModel":"Model-1","chargePointVendor":"Vendor-1"}]
14:24:18,870 ocpp INFO CP_1: send [3,"3949a5cb-40fb-4b06-9673-f00b11331500",{"currentTime":"2022-01-26T08:54:18.868944","interval":60,"status":"Accepted"}]
14:24:18,876 ocpp INFO CP_1: receive message [2,"4107d2a3-b40e-4799-8e21-5a02d300ce8b","Heartbeat",{}]
14:24:18,880 ocpp INFO CP_1: send [3,"4107d2a3-b40e-4799-8e21-5a02d300ce8b",{"currentTime":"2022-01-26T08:54:18.879837"}]

CP log:

14:24:08,794 ocpp INFO CP_1: send [2,"9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a","Authorize",{"idTag":"id_tag"}]
14:24:08,875 ocpp INFO CP_1: receive message [2,"2dca0ac9-6405-4855-a95c-e3f7059b2cdf","TriggerMessage",{"requestedMessage":"BootNotification"}]
14:24:08,880 ocpp INFO CP_1: send [3,"2dca0ac9-6405-4855-a95c-e3f7059b2cdf",{"status":"Accepted"}]
14:24:18,859 ocpp INFO CP_1: receive message [4,"9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a","SecurityError","No BootNotification received!",{}]
14:24:18,860 ocpp WARNING Received a CALLError: <CallError - unique_id=9bd5f9e3-bc2c-47d6-a8ef-dc93883c767a, error_code=SecurityError, error_description=No BootNotification received!, error_details={}>'
14:24:18,860 ocpp INFO CP_1: send [2,"3949a5cb-40fb-4b06-9673-f00b11331500","BootNotification",{"chargePointModel":"Model-1","chargePointVendor":"Vendor-1"}]
14:24:18,871 ocpp INFO CP_1: receive message [3,"3949a5cb-40fb-4b06-9673-f00b11331500",{"currentTime":"2022-01-26T08:54:18.868944","interval":60,"status":"Accepted"}]
14:24:18,875 ocpp INFO CP_1: send [2,"4107d2a3-b40e-4799-8e21-5a02d300ce8b","Heartbeat",{}]
14:24:18,881 ocpp INFO CP_1: receive message [3,"4107d2a3-b40e-4799-8e21-5a02d300ce8b",{"currentTime":"2022-01-26T08:54:18.879837"}]
OrangeTux commented 2 years ago

That is a good catch! Thanks for opening the issue @jainmohit2001. It's surprising that @after() handlers are not executed if the @on() handler raises an exception. Therefore it's bad API design and should be fixed somehow. Either by making this behavior explicit or changing the behavior.

On the other hand, you could also argue that it makes sense that flow of request handling is stopped when an exception is raised.