move /cards to doorboy-proxy + refactor mongo format
This commit is contained in:
		| @@ -23,12 +23,8 @@ spec: | ||||
|           env: | ||||
|             - name: OIDC_USERS_NAMESPACE | ||||
|               value: "default" | ||||
|             - name: SLACK_DOORLOG_CALLBACK | ||||
|               value: "changeme" | ||||
|             - name: SLACK_VERIFICATION_TOKEN | ||||
|               value: "changeme" | ||||
|             - name: INVENTORY_API_KEY | ||||
|               value: "sptWL6XFxl4b8" | ||||
|             - name: PYTHONUNBUFFERED | ||||
|               value: "1" | ||||
|             # Google test key | ||||
|   | ||||
| @@ -1,57 +1,14 @@ | ||||
| import re | ||||
| import const | ||||
| import time | ||||
| import threading | ||||
| import time | ||||
| from datetime import datetime | ||||
| from functools import wraps | ||||
|  | ||||
| import const | ||||
| from flask import Blueprint, g, request | ||||
| from pymongo import MongoClient | ||||
| from flask import Blueprint, g, request, jsonify | ||||
| from common import slack_post | ||||
|  | ||||
| page_api = Blueprint("api", __name__) | ||||
| db = MongoClient(const.MONGO_URI).get_default_database() | ||||
|  | ||||
| def check_api_key(f): | ||||
|     @wraps(f) | ||||
|     def decorated_function(*args, **kwargs): | ||||
|         request_key = request.headers.get('Authorization', False) | ||||
|         if not request_key: | ||||
|             return "nope", 403 | ||||
|         found_key = re.search(r"Basic (.*)", request_key).group(1) | ||||
|         if not found_key or found_key != const.INVENTORY_API_KEY: | ||||
|             return "nope", 403 | ||||
|         return f(*args, **kwargs) | ||||
|     return decorated_function | ||||
|  | ||||
| @page_api.route("/cards", methods=["POST"]) | ||||
| @check_api_key | ||||
| def get_group_cards(): | ||||
|     request_groups = request.json.get("groups", False) | ||||
|     if not request_groups: | ||||
|         return "must specify groups in parameter", 400 | ||||
|     print(f"found {len(g.users)} users for groups: {request_groups}") | ||||
|     keys = [] | ||||
|     for u in g.users: | ||||
|         for group in u.groups: | ||||
|             if group in request_groups: | ||||
|                 keys.append(u.username) | ||||
|                 break | ||||
|     print(f"{len(keys)} doorkeys") | ||||
|     flt = { | ||||
|         "token.uid_hash": {"$exists": True}, | ||||
|         "inventory.owner.username": {"$in": keys} | ||||
|     } | ||||
|     prj = { | ||||
|         "inventory.owner": True, | ||||
|         "token.uid_hash": True | ||||
|     } | ||||
|     found = [] | ||||
|     for obj in db.inventory.find(flt, prj): | ||||
|         found.append({"token": obj["token"]}) | ||||
|     fl = list(found) | ||||
|     print(f"{len(fl)} doorkey tokens") | ||||
|     return jsonify(fl) | ||||
|  | ||||
| @page_api.route("/api/slack/doorboy", methods=['POST']) | ||||
| def view_slack_doorboy(): | ||||
|     begin_time = time.perf_counter() | ||||
| @@ -91,27 +48,26 @@ def view_slack_doorboy(): | ||||
|  | ||||
|     threading.Thread(target=handle_slack_door_event, args=(doors, approved, member, door, status, subject)).start() | ||||
|  | ||||
|     return_message = "Opening %s for %s" % (door, subject) if approved else "Permission denied" | ||||
|     end_time = time.perf_counter() | ||||
|     print(f"view_slack_doorboy done in {end_time - begin_time:.4f} seconds") | ||||
|  | ||||
|     return_message = "Opening %s for %s" % (door, subject) if approved else "Permission denied" | ||||
|     return return_message | ||||
|  | ||||
| def handle_slack_door_event(doors, approved, member, door, status, subject): | ||||
|     begin_time = time.perf_counter() | ||||
|     for d in doors: | ||||
|         db.eventlog.insert_one({ | ||||
|             "method": "slack", | ||||
|             "approved": approved, | ||||
|             "duration": 5, | ||||
|             "component": "doorboy", | ||||
|             "type": "open-door", | ||||
|             "door": d, | ||||
|             "member_id": member.username, | ||||
|             "member": member.display_name, | ||||
|             "method": "slack", | ||||
|             "timestamp": datetime.utcnow(), | ||||
|             "door": d, | ||||
|             "approved": approved, | ||||
|             "user": { | ||||
|                 "id": member.username, | ||||
|                 "name": member.display_name, | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|     msg = "%s %s door access for %s via Slack bot" % (status, door, subject) | ||||
|     slack_post(msg, "doorboy") | ||||
|     end_time = time.perf_counter() | ||||
|     print(f"handle_slack_door_event done in {end_time - begin_time:.4f} seconds") | ||||
|   | ||||
| @@ -1,17 +1,16 @@ | ||||
| import os | ||||
| import collections.abc | ||||
| from functools import wraps | ||||
|  | ||||
| import requests | ||||
| from bson.objectid import ObjectId | ||||
| from flask import g, request | ||||
| from flask_wtf import FlaskForm | ||||
| from pymongo import MongoClient | ||||
| from kubernetes import client, config | ||||
| from typing import List | ||||
| import os | ||||
| from dataclasses import dataclass, field | ||||
| from functools import wraps | ||||
| from typing import List | ||||
|  | ||||
| import const | ||||
| from bson.objectid import ObjectId | ||||
| from common import read_user | ||||
| from flask import g, request | ||||
| from flask_wtf import FlaskForm | ||||
| from kubernetes import client, config | ||||
| from pymongo import MongoClient | ||||
|  | ||||
| devenv = const.ENVIRONMENT_TYPE == "DEV" | ||||
| OIDC_USERS_NAMESPACE = os.getenv("OIDC_USERS_NAMESPACE") | ||||
| @@ -93,18 +92,6 @@ def flatten(d, parent_key='', sep='.'): | ||||
|             items.append((new_key, v)) | ||||
|     return dict(items) | ||||
|  | ||||
| def slack_post(msg, channel): | ||||
|     if devenv: | ||||
|         print(f"{channel}: {msg}") | ||||
|         return | ||||
|  | ||||
|     channels = { | ||||
|       "doorboy": const.SLACK_DOORLOG_CALLBACK, | ||||
|     } | ||||
|     url = channels.get(channel, const.SLACK_DOORLOG_CALLBACK) | ||||
|  | ||||
|     requests.post(url, json={"text": msg }) | ||||
|  | ||||
| def build_query(base_query, fields=[], sort_fields={}): | ||||
|     top_usernames= ['k-space'] | ||||
|     selectors = [] | ||||
|   | ||||
| @@ -18,6 +18,4 @@ BUCKET_NAME = os.environ["BUCKET_NAME"] | ||||
| INVENTORY_ASSETS_BASE_URL = os.environ["INVENTORY_ASSETS_BASE_URL"] | ||||
| MONGO_URI = os.environ["MONGO_URI"] | ||||
| SLACK_VERIFICATION_TOKEN = os.environ["SLACK_VERIFICATION_TOKEN"] # used to verify (deprecated) incoming requests from slack | ||||
| SLACK_DOORLOG_CALLBACK = os.environ["SLACK_DOORLOG_CALLBACK"] # used for sending logs to private channel | ||||
| INVENTORY_API_KEY = os.environ["INVENTORY_API_KEY"] # used by doorboy-proxy (@check_api_key) | ||||
| MACADDRESS_OUTLINK_BASEURL = os.environ["MACADDRESS_OUTLINK_BASEURL"] | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| from datetime import datetime, timedelta | ||||
| from dateutil.parser import parse, ParserError | ||||
|  | ||||
| from bson.objectid import ObjectId | ||||
| from flask import Blueprint, g, redirect, render_template, request, abort | ||||
| from flask_wtf import FlaskForm | ||||
| from pymongo import MongoClient | ||||
| from wtforms import StringField, IntegerField, SelectField, BooleanField, DateTimeField, validators | ||||
| from wtforms.validators import DataRequired | ||||
|  | ||||
| import pytz | ||||
| import const | ||||
| from common import slack_post, User | ||||
| import pytz | ||||
| from bson.objectid import ObjectId | ||||
| from common import User | ||||
| from dateutil.parser import ParserError, parse | ||||
| from flask import Blueprint, abort, g, redirect, render_template, request | ||||
| from flask_wtf import FlaskForm | ||||
| from oidc import login_required, read_user | ||||
| from pymongo import MongoClient | ||||
| from wtforms import (BooleanField, IntegerField, SelectField, StringField, | ||||
|                      validators) | ||||
| from wtforms.validators import DataRequired | ||||
|  | ||||
| page_doorboy = Blueprint("doorboy", __name__) | ||||
| db = MongoClient(const.MONGO_URI).get_default_database() | ||||
| @@ -27,7 +27,7 @@ def view_doorboy_claim(event_id): | ||||
|     }) | ||||
|  | ||||
|     # Find token object to associate with user | ||||
|     token = db.inventory.update_one({ | ||||
|     db.inventory.update_one({ | ||||
|       "type": "token", | ||||
|       "token.uid_hash": event["token"]["uid_hash"], | ||||
|       "inventory.owner.username": { "$exists": False } | ||||
| @@ -127,35 +127,27 @@ class HoldDoorForm(FlaskForm): | ||||
|     door_name = SelectField("Door name", choices=[(j,j) for j in ["grounddoor", "frontdoor", "backdoor"]], validators=[DataRequired()]) | ||||
|     duration = IntegerField('Duration in seconds', validators=[DataRequired(), validators.NumberRange(min=5, max=21600)]) | ||||
|  | ||||
| # duration=0 to override and close right away | ||||
| @page_doorboy.route("/m/doorboy/hold", methods=["POST"]) | ||||
| @login_required | ||||
| def view_doorboy_hold(): | ||||
|     user = read_user() | ||||
|     form = HoldDoorForm(request.form) | ||||
|     now = datetime.utcnow() | ||||
|     if form.validate_on_submit(): | ||||
|         db.eventlog.insert_one({ | ||||
|           "component": "doorboy", | ||||
|           "type": "hold", | ||||
|           "requester": user["name"], | ||||
|           "method": "hold", | ||||
|           "timestamp": datetime.utcnow(), | ||||
|           "door": form.door_name.data, | ||||
|           "approved": True, | ||||
|           "user": { | ||||
|             "id": user["username"], | ||||
|             "name": user["name"] | ||||
|           }, | ||||
|           "expires": datetime.utcnow() + timedelta(seconds=form.duration.data) | ||||
|         }) | ||||
|     return redirect("/m/doorboy") | ||||
|  | ||||
| @page_doorboy.route("/m/doorboy/slam", methods=["POST"]) | ||||
| @login_required | ||||
| def view_doorboy_slam(): | ||||
|     user = read_user() | ||||
|     db.eventlog.insert_one({ | ||||
|       "component": "doorboy", | ||||
|       "type": "hold", | ||||
|       "requester": user["name"], | ||||
|       "door": form.door_name.data, | ||||
|       "expires": datetime.utcnow() + timedelta(minutes=form.duration_min.data) | ||||
|     }) | ||||
|     return redirect("/m/doorboy") | ||||
|  | ||||
| @page_doorboy.route("/m/doorboy/<door>/open") | ||||
| @login_required | ||||
| def view_doorboy_open(door): | ||||
| @@ -169,22 +161,17 @@ def view_doorboy_open(door): | ||||
|         access_group = "k-space:floor" | ||||
|     approved = access_group in g.users_lookup.get(user["username"], User()).groups | ||||
|     db.eventlog.insert_one({ | ||||
|       "method": "web", | ||||
|       "approved": approved, | ||||
|       "duration": 5, | ||||
|       "component": "doorboy", | ||||
|       "type": "open-door", | ||||
|       "door": door, | ||||
|       "member_id": user["username"], | ||||
|       "member": user["name"], | ||||
|       "method": "web", | ||||
|       "timestamp": datetime.utcnow(), | ||||
|       "door": door, | ||||
|       "approved": approved, | ||||
|       "user": { | ||||
|         "id": user["username"], | ||||
|         "name": user["name"] | ||||
|       } | ||||
|     }) | ||||
|  | ||||
|     status = "Permitted" if approved else "Denied" | ||||
|     subject = user["name"] | ||||
|     msg = "%s %s door access for %s via https://inventory.k-space.ee/m/doorboy" % (status, door, subject) | ||||
|     slack_post(msg, "doorboy") | ||||
|  | ||||
|     if approved: | ||||
|         return redirect("/m/doorboy") | ||||
|     else: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user