diff --git a/fabapi/connect.py b/fabapi/connect.py index 81b8ff1..af0a642 100644 --- a/fabapi/connect.py +++ b/fabapi/connect.py @@ -2,6 +2,7 @@ import asyncio import socket import ssl import capnp +import logging connection_capnp = capnp.load('schema/connection.capnp') authenticationsystem_capnp = capnp.load('schema/authenticationsystem.capnp') @@ -40,25 +41,23 @@ async def connect(host, port, user, pw): # Start TwoPartyClient using TwoWayPipe (takes no arguments in this mode) client = capnp.TwoPartyClient() - cap = client.bootstrap().cast_as(connection_capnp.Bootstrap) # Assemble reader and writer tasks, run in the background coroutines = [myreader(client, reader), mywriter(client, writer)] asyncio.gather(*coroutines, return_exceptions=True) - auth = cap.authenticationSystem().authenticationSystem - req = auth.start_request() - req.request.mechanism = "PLAIN" - req.request.initialResponse.initial = "\0" + user + "\0" + pw - rep_prom = req.send() - response = await rep_prom.a_wait() - if response.response.outcome.result == "successful": - return cap + boot = client.bootstrap().cast_as(connection_capnp.Bootstrap) + auth = await boot.createSession("PLAIN").a_wait() + p = "\0" + user + "\0" + pw + response = await auth.authentication.step(p).a_wait() + if response.which() == 'successful': + return response.successful.session else: - print("Auth failed") + print("Authentication failed!") return None + async def connect_with_fabfire_initial(host, port, uid): # Setup SSL context ctx = ssl.create_default_context( @@ -79,38 +78,32 @@ async def connect_with_fabfire_initial(host, port, uid): # Start TwoPartyClient using TwoWayPipe (takes no arguments in this mode) client = capnp.TwoPartyClient() - cap = client.bootstrap().cast_as(connection_capnp.Bootstrap) + # Assemble reader and writer tasks, run in the background coroutines = [myreader(client, reader), mywriter(client, writer)] asyncio.gather(*coroutines, return_exceptions=True) - auth = cap.authenticationSystem().authenticationSystem - req = auth.start_request() - req.request.mechanism = "X-FABFIRE" - req.request.initialResponse.initial = uid - rep_prom = req.send() - response = await rep_prom.a_wait() - print(response.response.which()) - if response.response.which() == "challence": - print(f"challenge: {response.response.challence}") - return auth, response.response.challence, cap + boot = client.bootstrap().cast_as(connection_capnp.Bootstrap) + auth = await boot.createSession("X-FABFIRE").a_wait() + response = await auth.authentication.step(uid).a_wait() + logging.debug(f"got response type: {response.which()}") + if response.which() == "challenge": + logging.debug(f"challenge: {response.challenge}") + return auth, response.challenge else: - print(f"Auth failed: {response.response.outcome.result}, additional info: {response.response.outcome.helpText}") + logging.error(f"Auth failed: {response.failed.code}, additional info: {response.failed.additionalData}") return None async def connect_with_fabfire_step(auth, msg): - req = auth.step_request() - req.response = msg - rep_prom = req.send() - response = await rep_prom.a_wait() - if response.response.which() == "challence": - print(f"challenge: {response.response.challence}") - return response.response.challence, False # auth cap, challenge, not done - elif response.response.outcome.result == "successful": - print(f"Auth completed successfully! Got additional Data: {response.response.outcome.additionalData.additional}") - return response.response.outcome.additionalData.additional, True # dont care, message, we are done + response = await auth.authentication.step(msg).a_wait() + if response.which() == "challenge": + logging.debug(f"challenge: {response.challenge}") + return response.challenge, None # auth cap, challenge, not done + elif response.which() == "successful": + logging.info(f"Auth completed successfully! Got additional Data: {response.successful.additionalData}") + return response.successful.additionalData, response.successful.session # dont care, message, we are done else: - print(f"Auth failed: {response.response.outcome.result}, additional info: {response.response.outcome.helpText}") + logging.error(f"Auth failed: {response.failed.code}, additional info: {response.failed.additionalData}") return None diff --git a/single.py b/single.py index 6d62675..6304bbc 100644 --- a/single.py +++ b/single.py @@ -1,43 +1,77 @@ import asyncio +import logging from asyncio_mqtt import Client import json import fabapi +from timer import Timer - +BFFHD_HOST = "127.0.0.1" +MQTT_HOST = "127.0.0.1" +MACHINE_URN = "" +READER_ID = "" async def main(): done = False - cap = None - auth = None + auth_cap = None + session = None msg = None - async with Client("192.168.178.31") as client: + async with Client(MQTT_HOST) as client: await client.publish("/cmnd/reader", payload='{"Cmd":"haltPICC"}', qos=2, retain=False) await client.publish("/cmnd/reader", payload='{"Cmd": "message", "MssgID": 0, "AddnTxt":" Karte auflegen"}', qos=2, retain=False) - async with client.filtered_messages("/rfid_reader/111") as messages: + async with client.filtered_messages(f"/rfid_reader/{READER_ID}") as messages: await client.subscribe("/rfid_reader/#") async for message in messages: - if not auth: - auth, msg, cap = await fabapi.connect_with_fabfire_initial("192.168.178.31", 59661, message.payload) - elif not done: - msg, done = await fabapi.connect_with_fabfire_step(auth, message.payload) - if done: - await client.publish("/cmnd/reader", payload='{"Cmd":"haltPICC"}', qos=2, retain=False) - ms = await cap.machineSystem().a_wait() - info = await ms.machineSystem.info().a_wait() - ma = await info.info.getMachineURN("urn:fabaccess:resource:Testmachine").a_wait() - if ma.machine.state == "inUse": - await ma.machine.inuse.giveBack().a_wait() + response_for_reader = None + if not auth_cap: + timeout_timer = Timer(10, lambda: handle_timeout(client)) + auth_cap, response_for_reader = await fabapi.connect_with_fabfire_initial( + BFFHD_HOST, 59961, message.payload) + elif not session: + response_for_reader, session = await fabapi.connect_with_fabfire_step(auth_cap, + message.payload) + if session: + timeout_timer.cancel() + await client.publish(f"/cmnd/reader/{READER_ID}", payload='{"Cmd":"haltPICC"}', qos=2, + retain=False) + + info = session.machineSystem.info + ma = await info.getMachineURN(f"{MACHINE_URN}").a_wait() + + if ma.which() == "just": + ma = ma.just else: - await ma.machine.use.use().a_wait() - done = False - cap = None - auth = None - msg = None + logging.critical( + f"Could not get machine {MACHINE_URN}. Machine does not exist or insufficient permissions") + raise Exception( + f"Could not get machine {MACHINE_URN}. Machine does not exist or insufficient permissions") + + if ma.state == "inUse": + await ma.inuse.giveBack().a_wait() + await client.publish(f"/cmnd/reader/{READER_ID}", + payload='{"Cmd":"message","MssgID":0,"AddnTxt":""}', qos=2, + retain=False) + else: + await ma.use.use().a_wait() + await client.publish(f"/cmnd/reader/{READER_ID}", payload=response_for_reader, qos=2, + retain=False) + await asyncio.sleep(2) + await client.publish(f"/cmnd/reader/{READER_ID}", + payload='{"Cmd":"message","MssgID":3,"AddnTxt":""}', qos=2, + retain=False) + session = None + auth_cap = None + response_for_reader = None + if response_for_reader: + await client.publish(f"/cmnd/reader/{READER_ID}", payload=response_for_reader, qos=2, retain=False) await client.publish("/cmnd/reader", payload=msg, qos=2, retain=False) +async def handle_timeout(client, reader_id): + await client.publish(f"/cmnd/reader/{reader_id}", payload='{"Cmd":"haltPICC"}', qos=2, + retain=False) + logging.critical(f"authentication timed out on reader {reader_id}") if __name__ == "__main__": loop = asyncio.get_event_loop() diff --git a/timer.py b/timer.py new file mode 100644 index 0000000..1e523dc --- /dev/null +++ b/timer.py @@ -0,0 +1,18 @@ +import asyncio +from typing import Callable + +import asyncio_mqtt + + +class Timer: + def __init__(self, timeout: int, callback: Callable): + self._timeout = timeout + self._callback = callback + self._task = asyncio.ensure_future(self._job()) + + async def _job(self): + await asyncio.sleep(self._timeout) + await self._callback() + + def cancel(self): + self._task.cancel()