doorboy-direct #5
@@ -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:
 | 
					    # Update token, create if unknown
 | 
				
			||||||
        requests.post(SLACK_DOORLOG_CALLBACK, json={"text": msg }).raise_for_status()
 | 
					    app.ctx.db.inventory.update_one({
 | 
				
			||||||
    except requests.exceptions.RequestException as e:
 | 
					        "component": "doorboy",
 | 
				
			||||||
        print(f"[SLACK]: {e}")
 | 
					        "type": "token",
 | 
				
			||||||
 | 
					        "token.uid_hash": data["uid_hash"]
 | 
				
			||||||
def approvedStr(approved: bool) -> str:
 | 
					    }, {
 | 
				
			||||||
    if approved:
 | 
					        "$set": {
 | 
				
			||||||
        return "Permitted"
 | 
					            "last_seen": timestamp
 | 
				
			||||||
    
 | 
					        },
 | 
				
			||||||
    return "Denied"
 | 
					        "$setOnInsert": {
 | 
				
			||||||
 | 
					            "first_seen": timestamp,
 | 
				
			||||||
# consumes SLACK_DOORLOG_CALLBACK and app.ctx.db
 | 
					            "inventory": {
 | 
				
			||||||
@app.listener("after_server_start")
 | 
					                "claimable": True,
 | 
				
			||||||
async def slack_log(app, loop):
 | 
					 | 
				
			||||||
    pipeline = [{
 | 
					 | 
				
			||||||
        "$match": {
 | 
					 | 
				
			||||||
            "operationType": "insert",
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    }]
 | 
					        }
 | 
				
			||||||
    while True:
 | 
					    }, upsert=True)
 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            async with app.ctx.db.eventlog.watch(pipeline) as stream:
 | 
					 | 
				
			||||||
                async for event in stream:
 | 
					 | 
				
			||||||
                    ev = event["fullDocument"]                    
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
                    msg = "%s %s access for %s via %s" % (approvedStr(ev["approved"]), ev["door"], ev["user"]["name"], ev("method"))
 | 
					    token = app.ctx.db.inventory.find_one({
 | 
				
			||||||
                    slack_post(msg)
 | 
					        "component": "doorboy",
 | 
				
			||||||
 | 
					        "type": "token",
 | 
				
			||||||
 | 
					        "token.uid_hash": data["uid_hash"]
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        except PyMongoError as e:
 | 
					    event_swipe = {
 | 
				
			||||||
            print(e)
 | 
					        "component": "doorboy",
 | 
				
			||||||
 | 
					        "method": "token",
 | 
				
			||||||
 | 
					        "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