diff --git a/.gitignore b/.gitignore
index 5c6f97b..32b5193 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,3 +64,4 @@ target/
# Other
venv/
*.sqlite
+ad.json
diff --git a/kdoorweb/kdoorweb/db.py b/kdoorweb/kdoorweb/db.py
index 9359198..7901935 100644
--- a/kdoorweb/kdoorweb/db.py
+++ b/kdoorweb/kdoorweb/db.py
@@ -1,14 +1,53 @@
import datetime
import sqlite3
+import inspect
+import json
+from bottle import HTTPError
SCHEMA_VERSION = 1
+class SQLitePlugin:
+
+ name = "sqlite"
+ api = 2
+
+ def __init__(self, dbfile):
+ self.dbfile = dbfile
+
+ def apply(self, callback, context):
+ # Test if the original callback accepts a 'db' keyword.
+ # Ignore it if it does not need a database handle.
+ args = inspect.signature(context.callback).parameters
+ if "db" not in args:
+ return callback
+
+ def wrapper(*args, **kwargs):
+ db = DB(self.dbfile)
+ # Add the connection handle as a keyword argument.
+ kwargs["db"] = db
+ try:
+ rv = callback(*args, **kwargs)
+ except sqlite3.IntegrityError as e:
+ db.db.rollback()
+ raise HTTPError(500, "Database Error", e)
+ finally:
+ db.close()
+ return rv
+
+ # Replace the route callback with the wrapped one.
+ return wrapper
+
+
class DB:
def __init__(self, dbfile=":memory:"):
self.dbfile = dbfile
self.db = sqlite3.connect(self.dbfile)
+ self.db.row_factory = sqlite3.Row
+
+ def close(self):
+ self.db.close()
@staticmethod
def create_db(dbfile):
@@ -21,7 +60,7 @@ class DB:
create table users (
id integer primary key,
user text,
- real_name text,
+ full_name text,
email text,
disabled integer,
admin integer
@@ -32,6 +71,7 @@ class DB:
card_uid blob,
name text,
created text,
+ disabled integer,
foreign key (user_id)
references users (id)
@@ -69,25 +109,69 @@ class DB:
)
db.commit()
- def list_users(*, page=0, count=20, query=None):
- pass
-
- def add_user(self, user, real_name=None, email=None, disabled=False, admin=False):
+ def add_user(self, user, full_name=None, email=None, disabled=False, admin=False):
self.add_users([(
- user, real_name, email, disabled, admin
+ user, full_name, email, int(disabled), int(admin)
)])
def add_users(self, users):
self.db.executemany("""
- insert into users(user, real_name, email, disabled, admin)
+ insert into users(user, full_name, email, disabled, admin)
values(?, ?, ?, ?, ?)
""", users)
self.db.commit()
+ def list_users(self):
+ cur = self.db.execute(
+ "select id, user, full_name, email from users"
+ )
+ return cur.fetchall()
+
+ def get_user(self, user_id):
+ cur = self.db.execute(
+ "select * from users where id = ?",
+ (user_id, )
+ )
+ return cur.fetchone()
+
+ def get_user_by_name(self, user_name):
+ cur = self.db.execute(
+ "select * from users where user = ?",
+ (user_name, )
+ )
+ return cur.fetchone()
+
+ @staticmethod
+ def import_ad(json_file):
+ with open(json_file) as fp:
+ json_data = json.load(fp)
+ for user, fields in json_data.items():
+ if fields.get("considered_active", False) and \
+ fields.get("groups", {}).get("floor_access", False):
+ yield (
+ user,
+ fields.get("full_name"),
+ fields.get("personal_mail"),
+ 0,
+ int(fields.get("groups",{}).get("onboarding", False))
+ )
+
+
+
if __name__ == "__main__":
- #DB.create_db("kdoorweb.sqlite")
- db = DB("kdoorweb.sqlite")
- db.add_user("juku")
+ dbfile = "../kdoorweb.sqlite"
+ import os, sys
+ from pprint import pprint
+ try:
+ os.unlink(dbfile)
+ except FileNotFoundError:
+ pass
+ DB.create_db(dbfile)
+ db = DB(dbfile)
+ db.add_users(db.import_ad("../ad.json"))
+ users = db.list_users()
+ for user in users:
+ print(dict(user))
diff --git a/kdoorweb/kdoorweb/views/base.html b/kdoorweb/kdoorweb/views/base.html
index be03984..1d95458 100644
--- a/kdoorweb/kdoorweb/views/base.html
+++ b/kdoorweb/kdoorweb/views/base.html
@@ -1,3 +1,4 @@
+% from bottle import request
@@ -10,14 +11,21 @@
{{!base}}
+
+
diff --git a/kdoorweb/kdoorweb/views/doors.html b/kdoorweb/kdoorweb/views/doors.html
new file mode 100644
index 0000000..4e8aee7
--- /dev/null
+++ b/kdoorweb/kdoorweb/views/doors.html
@@ -0,0 +1,36 @@
+% rebase('base.html')
+
+Doors List
+
+
+
+
+
+
+ Name |
+ State |
+ Action |
+
+
+
+ % for door in doors:
+
+ {{door["name"]}} |
+ {{door["state"]}} |
+
+
+ |
+
+
+ {{door["note"]}} |
+
+ % end
+
+
diff --git a/kdoorweb/kdoorweb/views/info.html b/kdoorweb/kdoorweb/views/info.html
new file mode 100644
index 0000000..77af65b
--- /dev/null
+++ b/kdoorweb/kdoorweb/views/info.html
@@ -0,0 +1,58 @@
+% rebase('base.html')
+
+Info {{full_name}}
+
+
+ - Disabled
+ - {{disabled}}
+ - Admin
+ - {{admin}}
+
+
+
+ - User
+ - {{user}}
+ - Full Name
+ - {{full_name}}
+ - Email
+ - {{email}}
+
+
+Keycards
+
+
+
+
+ Name |
+ Created |
+ Disabled |
+ Action |
+
+
+
+ % for keycard in keycards:
+
+ {{keycard["name"]}} |
+ {{keycard["created"]}} |
+ {{keycard["disabled"]}} |
+
+
+ |
+
+ % end
+
+
+
+Add new keycard
+
diff --git a/kdoorweb/kdoorweb/views/list.html b/kdoorweb/kdoorweb/views/list.html
index b7e0db0..fef248a 100644
--- a/kdoorweb/kdoorweb/views/list.html
+++ b/kdoorweb/kdoorweb/views/list.html
@@ -6,15 +6,17 @@
Name |
- nr of cards |
- Last access |
+ Cards |
+ Last access |
+ % for user in users:
- Arti Zirk |
- 2 |
+ {{user["full_name"]}} |
+ - |
- |
+ % end
diff --git a/kdoorweb/kdoorweb/views/log.html b/kdoorweb/kdoorweb/views/log.html
new file mode 100644
index 0000000..50a2496
--- /dev/null
+++ b/kdoorweb/kdoorweb/views/log.html
@@ -0,0 +1,22 @@
+% rebase('base.html')
+
+Access log
+
+
+
+
+ Time |
+ Door |
+ User |
+
+
+
+ % for event in events:
+
+ {{event["timestamp"]}} |
+ {{event["door"]}} |
+ {{event["user_name"]}} |
+
+ % end
+
+
diff --git a/kdoorweb/kdoorweb/views/login.html b/kdoorweb/kdoorweb/views/login.html
index 14034f2..e472622 100644
--- a/kdoorweb/kdoorweb/views/login.html
+++ b/kdoorweb/kdoorweb/views/login.html
@@ -1,5 +1,7 @@
% rebase('base.html')
+Login:
+
% if error:
Error: {{ error }}
% end
diff --git a/kdoorweb/kdoorweb/web.py b/kdoorweb/kdoorweb/web.py
index 5d5b9e6..6df9204 100644
--- a/kdoorweb/kdoorweb/web.py
+++ b/kdoorweb/kdoorweb/web.py
@@ -2,22 +2,45 @@ import os
import secrets
from bottle import Bottle, view, TEMPLATE_PATH, static_file, \
- request, redirect, response
+ request, redirect, response, HTTPError
+
+from .db import SQLitePlugin
application = app = Bottle()
# TODO: Could be replaced with importlib.resources
TEMPLATE_PATH.append(os.path.join(os.path.dirname(__file__), "views"))
STATIC_PATH = os.path.join(os.path.dirname(__file__), "static")
+SQLITE_PATH = os.path.join(os.path.dirname(__file__), "..", "kdoorweb.sqlite")
-COOKIE_KEY = secrets.token_bytes(32)
+COOKIE_KEY = b"1234"#secrets.token_bytes(32)
+
+
+def current_user(db):
+ user_id = request.get_cookie("uid", secret=COOKIE_KEY)
+ if not user_id:
+ return
+ return db.get_user(user_id)
+
+
+def login_user(uid):
+ response.set_cookie("uid", uid, secret=COOKIE_KEY)
+
+
+def logout_user():
+ response.set_cookie("uid", "", secret=COOKIE_KEY)
def check_auth(callback):
def wrapper(*args, **kwargs):
- user = request.get_cookie("user", key=COOKIE_KEY)
+ if "db" not in kwargs:
+ request.current_user = None
+ return callback(*args, **kwargs)
+ user = current_user(kwargs["db"])
+ request.current_user = user
if user:
- print(f"logged in as {user}")
+ print(f"logged in as {user['user']}")
+ print(request.current_user)
return callback(*args, **kwargs)
else:
print("not logged in")
@@ -26,6 +49,7 @@ def check_auth(callback):
return wrapper
+app.install(SQLitePlugin(SQLITE_PATH))
app.install(check_auth)
@@ -35,7 +59,14 @@ def callback(path):
@app.route("/", skip=[check_auth])
-def index():
+def index(db):
+ user = current_user(db)
+ if user:
+ if user["admin"]:
+ redirect("/list")
+ else:
+ redirect(f"/info/{user['id']}")
+
redirect("/login")
@@ -49,31 +80,71 @@ def login():
@app.post('/login', skip=[check_auth])
-def do_login():
- print(dict(request.forms))
- user = request.forms.get("user")
- print(f"user {user}")
+def do_login(db):
+ user_name = request.forms.get("user")
+ user = db.get_user_by_name(user_name)
if user:
- response.set_cookie("user", user, key=COOKIE_KEY)
- redirect("/list")
+ print(f"user {dict(user)}")
+ login_user(user["id"])
+ redirect("/")
else:
- response.set_cookie("error", "No user")
+ response.set_cookie("error", "Login Failed")
redirect("/login")
@app.route("/logout")
def logout():
- response.set_cookie("user", "", key=COOKIE_KEY)
+ logout_user()
redirect("/login")
@app.route("/list")
@view("list.html")
-def list():
- return {}
+def user_list(db):
+ user = request.current_user
+ if user and not user["admin"]:
+ users = [user]
+ else:
+ users = db.list_users()
+ return {"users": users}
+
+
+@app.route("/info")
+def user_info(db):
+ user = request.current_user
+ redirect(f"/info/{user['id']}")
+
+
+@app.route("/info/")
+@view("info.html")
+def info(db, user_id):
+ user_id = int(user_id)
+ c_user = request.current_user
+ if not c_user["admin"] and c_user["id"] != user_id:
+ raise HTTPError(403, "Logged in user is not admin")
+ user = db.get_user(user_id)
+ if not user:
+ raise HTTPError(404, "User does not exist")
+ return {**user, "keycards": []}
@app.route("/log")
-@view("log")
-def log():
- return {}
+@view("log.html")
+def log(db):
+ return {
+ "events":
+ [
+ {
+ "timestamp": 0,
+ "door": "Back Door",
+ "user_id":1,
+ "user_name": "Arti Zirk"
+ }
+ ]
+ }
+
+
+@app.route("/doors")
+@view("doors.html")
+def doors(db):
+ return {"doors":[]}