Move /swipe from inventory-app
This commit is contained in:
		| @@ -3,6 +3,7 @@ from datetime import date, datetime | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import text, json | from sanic.response import text, json | ||||||
| from sanic_prometheus import monitor | from sanic_prometheus import monitor | ||||||
|  | from dateutil.parser import parse | ||||||
| import httpx | import httpx | ||||||
| from motor.motor_asyncio import AsyncIOMotorClient | from motor.motor_asyncio import AsyncIOMotorClient | ||||||
| from pymongo.errors import PyMongoError | from pymongo.errors import PyMongoError | ||||||
| @@ -20,7 +21,6 @@ WORKSHOP_ACCESS_GROUP = os.environ["WORKSHOP_ACCESS_GROUP"] | |||||||
| MONGO_URI = os.environ["MONGO_URI"] | MONGO_URI = os.environ["MONGO_URI"] | ||||||
| INVENTORY_API_KEY = os.environ["INVENTORY_API_KEY"] # asshole forwards to inventory-app, instead of using mongo directly | INVENTORY_API_KEY = os.environ["INVENTORY_API_KEY"] # asshole forwards to inventory-app, instead of using mongo directly | ||||||
| CARD_URI = os.environ["CARD_URI"] | CARD_URI = os.environ["CARD_URI"] | ||||||
| SWIPE_URI = os.environ["SWIPE_URI"] |  | ||||||
|  |  | ||||||
| assert len(DOORBOY_SECRET_FLOOR) >= 10 | assert len(DOORBOY_SECRET_FLOOR) >= 10 | ||||||
| assert len(DOORBOY_SECRET_WORKSHOP) >= 10 | assert len(DOORBOY_SECRET_WORKSHOP) >= 10 | ||||||
| @@ -132,8 +132,9 @@ async def view_longpoll(request): | |||||||
|         await response.send("data: response-generator-ended\n\n") |         await response.send("data: response-generator-ended\n\n") | ||||||
|         return |         return | ||||||
|  |  | ||||||
|  | # Called by the door to log a card swipe. Does not decide whether the door should be opened. | ||||||
| @app.post("/swipe") | @app.post("/swipe") | ||||||
| async def forward_swipe(request): | async def swipe(request): | ||||||
|     # authenticate |     # authenticate | ||||||
|     key = request.headers.get("KEY") |     key = request.headers.get("KEY") | ||||||
|     if not key or key not in [DOORBOY_SECRET_FLOOR, DOORBOY_SECRET_WORKSHOP]: |     if not key or key not in [DOORBOY_SECRET_FLOOR, DOORBOY_SECRET_WORKSHOP]: | ||||||
| @@ -150,41 +151,47 @@ async def forward_swipe(request): | |||||||
|         print("Door", repr(data.get("door")), "not in", doors) |         print("Door", repr(data.get("door")), "not in", doors) | ||||||
|         return text("Not allowed", 403) |         return text("Not allowed", 403) | ||||||
|  |  | ||||||
| def slack_post(msg): |     timestamp = parse(data["timestamp"]) if data.get("timestamp") else datetime.now(datetime.timezone.utc) | ||||||
|     if SLACK_DOORLOG_CALLBACK == "DEV": |  | ||||||
|         print(f"[DEV SLACK]: {msg}")  |  | ||||||
|         return |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         requests.post(SLACK_DOORLOG_CALLBACK, json={"text": msg }).raise_for_status() |  | ||||||
|     except requests.exceptions.RequestException as e: |  | ||||||
|         print(f"[SLACK]: {e}") |  | ||||||
|  |  | ||||||
| def approvedStr(approved: bool) -> str: |  | ||||||
|     if approved: |  | ||||||
|         return "Permitted" |  | ||||||
|      |      | ||||||
|     return "Denied" |     # Update token, create if unknown | ||||||
|  |     app.ctx.db.inventory.update_one({ | ||||||
| # consumes SLACK_DOORLOG_CALLBACK and app.ctx.db |         "component": "doorboy", | ||||||
| @app.listener("after_server_start") |         "type": "token", | ||||||
| async def slack_log(app, loop): |         "token.uid_hash": data["uid_hash"] | ||||||
|     pipeline = [{ |     }, { | ||||||
|         "$match": { |         "$set": { | ||||||
|             "operationType": "insert", |             "last_seen": timestamp | ||||||
|  |         }, | ||||||
|  |         "$setOnInsert": { | ||||||
|  |             "first_seen": timestamp, | ||||||
|  |             "inventory": { | ||||||
|  |                 "claimable": True, | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     }] |     }, upsert=True) | ||||||
|     while True: |      | ||||||
|         try: |     token = app.ctx.db.inventory.find_one({ | ||||||
|             async with app.ctx.db.eventlog.watch(pipeline) as stream: |         "component": "doorboy", | ||||||
|                 async for event in stream: |         "type": "token", | ||||||
|                     ev = event["fullDocument"]                     |         "token.uid_hash": data["uid_hash"] | ||||||
|                      |     }) | ||||||
|                     msg = "%s %s access for %s via %s" % (approvedStr(ev["approved"]), ev["door"], ev["user"]["name"], ev("method")) |      | ||||||
|                     slack_post(msg) |     event_swipe = { | ||||||
|          |         "component": "doorboy", | ||||||
|         except PyMongoError as e: |         "method": "token", | ||||||
|             print(e) |         "timestamp": timestamp, | ||||||
|  |         "door": data["door"], | ||||||
|  |         "event": "card-swiped", | ||||||
|  |         "approved": data["approved"], | ||||||
|  |         "uid_hash":  data["uid_hash"], | ||||||
|  |         "user": { | ||||||
|  |             "id": token.get("inventory", {}).get("owner", {}).get("username", ""), | ||||||
|  |             "name": token.get("inventory", {}).get("owner", {}).get("display_name", "Unclaimed Token") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     app.ctx.db.eventlog.insert_one(event_swipe) | ||||||
|  |  | ||||||
|  |     return text("ok") | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     app.run(debug=False, host="0.0.0.0", port=5000, single_process=True, access_log=True) |     app.run(debug=False, host="0.0.0.0", port=5000, single_process=True, access_log=True) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user