diff --git a/flaschengeist/__init__.py b/flaschengeist/__init__.py index afbfbdc..1f13a7c 100644 --- a/flaschengeist/__init__.py +++ b/flaschengeist/__init__.py @@ -1,7 +1,6 @@ """ Server-package Initialize app, CORS, database and add it to the application. - Initialize also a singleton for the AccessTokenController and start the Thread. """ import yaml diff --git a/flaschengeist/app.py b/flaschengeist/app.py index f776167..19b0254 100644 --- a/flaschengeist/app.py +++ b/flaschengeist/app.py @@ -56,11 +56,14 @@ def __load_plugins(app): def install_all(): from flaschengeist.system.database import db - from flaschengeist.system.models import user, event, accessToken + from flaschengeist.system.models import user, event, session db.create_all() db.session.commit() for name, plugin in current_app.config["FG_PLUGINS"].items(): + if not plugin: + logger.debug("Skip disabled plugin {}".format(name)) + continue logger.info("Install plugin {}".format(name)) plugin.install() if plugin.permissions: diff --git a/flaschengeist/modules/__init__.py b/flaschengeist/modules/__init__.py index fa0bb92..e84ac55 100644 --- a/flaschengeist/modules/__init__.py +++ b/flaschengeist/modules/__init__.py @@ -7,6 +7,7 @@ class Plugin: def __init__(self, config=None, blueprint=None, permissions={}): self.blueprint = blueprint self.permissions = permissions + self.version = "dummy" def install(self): """Installation routine @@ -14,6 +15,11 @@ class Plugin: """ pass + def serialize(self): + return { + "version": self.version, + } + class AuthPlugin(Plugin): def login(self, user, pw): diff --git a/flaschengeist/modules/auth/__init__.py b/flaschengeist/modules/auth/__init__.py index 0d57e72..0c68b1d 100644 --- a/flaschengeist/modules/auth/__init__.py +++ b/flaschengeist/modules/auth/__init__.py @@ -11,9 +11,9 @@ from werkzeug.local import LocalProxy from flaschengeist import logger from flaschengeist.modules import Plugin from flaschengeist.system.decorator import login_required -from flaschengeist.system.controller import accessTokenController, userController, messageController +from flaschengeist.system.controller import sessionController, userController, messageController -access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController()) +session_controller = LocalProxy(lambda: sessionController.SessionController()) auth_bp = Blueprint("auth", __name__) @@ -33,10 +33,10 @@ class AuthRoutePlugin(Plugin): @auth_bp.route("/auth", methods=["POST"]) -def _create_token(): +def _login(): """Login User - Login in User and create an AccessToken for the User. + Login in User and create a Session for the User. Requires POST data {'userid': string, 'password': string} Returns: A JSON-File with user information and created token or errors @@ -54,55 +54,55 @@ def _create_token(): if not user: raise Unauthorized logger.debug("user is {{ {} }}".format(user)) - token = access_controller.create(user, user_agent=request.user_agent) - logger.debug("access token is {{ {} }}".format(token)) + session = session_controller.create(user, user_agent=request.user_agent) + logger.debug("token is {{ {} }}".format(session.token)) logger.info("User {{ {} }} success login.".format(userid)) # Lets cleanup the DB - access_controller.clear_expired() - return jsonify({"user": user, "token": token, "permissions": user.get_permissions()}) + session_controller.clear_expired() + return jsonify({"session": session, "permissions": user.get_permissions()}) @auth_bp.route("/auth", methods=["GET"]) @login_required() def _get_tokens(access_token, **kwargs): - tokens = access_controller.get_users_tokens(access_token.user) + tokens = session_controller.get_users_sessions(access_token.user) a = messageController.Message(access_token.user, "Go", "Bar") messageController.send_message(a) return jsonify(tokens) -@login_required() @auth_bp.route("/auth/", methods=["DELETE"]) -def _delete_token(token, access_token, **kwargs): +@login_required() +def _delete_token(access_token, token, **kwargs): logger.debug("Try to delete access token {{ {} }}".format(token)) - token = access_controller.get_token(token, access_token.user) + token = session_controller.get_session(token, access_token.user) if not token: logger.debug("Token not found in database!") # Return 403 error, so that users can not bruteforce tokens # Valid tokens from other users and invalid tokens now are looking the same raise Forbidden - access_controller.delete_token(token) - access_controller.clear_expired() + session_controller.delete_session(token) + session_controller.clear_expired() return jsonify({"ok": "ok"}) -@login_required() @auth_bp.route("/auth/", methods=["GET"]) +@login_required() def _get_token(token, access_token, **kwargs): logger.debug("get token {{ {} }}".format(token)) - token = access_controller.get_token(token, access_token.user) + session = session_controller.get_session(token, access_token.user) if not token: # Return 403 error, so that users can not bruteforce tokens # Valid tokens from other users and invalid tokens now are looking the same raise Forbidden - return jsonify(token) + return jsonify({"session": session, "permissions": session.user.get_permissions()}) -@login_required() @auth_bp.route("/auth/", methods=["PUT"]) +@login_required() def _set_lifetime(token, access_token, **kwargs): - token = access_controller.get_token(token, access_token.user) + token = session_controller.get_token(token, access_token.user) if not token: # Return 403 error, so that users can not bruteforce tokens # Valid tokens from other users and invalid tokens now are looking the same @@ -110,7 +110,7 @@ def _set_lifetime(token, access_token, **kwargs): try: lifetime = request.get_json()["value"] logger.debug("set lifetime {{ {} }} to access token {{ {} }}".format(lifetime, token)) - access_controller.set_lifetime(token, lifetime) + session_controller.set_lifetime(token, lifetime) return jsonify({"ok": "ok"}) except (KeyError, TypeError): raise BadRequest diff --git a/flaschengeist/modules/auth_ldap/__init__.py b/flaschengeist/modules/auth_ldap/__init__.py index 2a97a77..d8cd710 100644 --- a/flaschengeist/modules/auth_ldap/__init__.py +++ b/flaschengeist/modules/auth_ldap/__init__.py @@ -37,17 +37,17 @@ class AuthLDAP(AuthPlugin): def login(self, user, password): if not user: return False - return self.ldap.authenticate(user.uid, password, "uid", self.dn) + return self.ldap.authenticate(user.userid, password, "uid", self.dn) def update_user(self, user): self.ldap.connection.search( "ou=user,{}".format(self.dn), - "(uid={})".format(user.uid), + "(uid={})".format(user.userid), SUBTREE, attributes=["uid", "givenName", "sn", "mail"], ) r = self.ldap.connection.response[0]["attributes"] - if r["uid"][0] == user.uid: + if r["uid"][0] == user.userid: user.set_attribute("DN", self.ldap.connection.response[0]["dn"]) user.firstname = r["givenName"][0] user.lastname = r["sn"][0] @@ -55,7 +55,7 @@ class AuthLDAP(AuthPlugin): user.mail = r["mail"][0] if "displayName" in r: user.display_name = r["displayName"][0] - for group in self._get_groups(user.uid): + for group in self._get_groups(user.userid): user.add_role(group) def _get_groups(self, uid): diff --git a/flaschengeist/modules/auth_plain/__init__.py b/flaschengeist/modules/auth_plain/__init__.py index 872d723..4bb87c0 100644 --- a/flaschengeist/modules/auth_plain/__init__.py +++ b/flaschengeist/modules/auth_plain/__init__.py @@ -2,7 +2,7 @@ import binascii import hashlib import os -import flaschengeist.modules as modules +from flaschengeist.modules import AuthPlugin from flaschengeist.system.models.user import User @@ -21,7 +21,7 @@ def _verify_password(stored_password, provided_password): return pass_hash == stored_password -class AuthPlain(modules.Auth): +class AuthPlain(AuthPlugin): def login(self, user: User, password: str): if user and "password" in user.attributes: return _verify_password(user.attributes["password"].value, password) diff --git a/flaschengeist/modules/geruecht/baruser/routes.py b/flaschengeist/modules/geruecht/baruser/routes.py index dc7c383..0d9dbf1 100644 --- a/flaschengeist/modules/geruecht/baruser/routes.py +++ b/flaschengeist/modules/geruecht/baruser/routes.py @@ -42,7 +42,7 @@ def _bar(**kwargs): type = 'credit' else: type = 'amount' - dic[user.uid] = {"username": user.uid, + dic[user.userid] = {"username": user.userid, "firstname": user.firstname, "lastname": user.lastname, "amount": all, @@ -50,8 +50,8 @@ def _bar(**kwargs): "type": type, "limit": user.limit, "autoLock": user.autoLock - } - dic[user.uid]['last_seen'] = {"year": user.last_seen.year, "month": user.last_seen.month, "day": user.last_seen.day, "hour": user.last_seen.hour, "minute": user.last_seen.minute, "second": user.last_seen.second} if user.last_seen else None + } + dic[user.userid]['last_seen'] = {"year": user.last_seen.year, "month": user.last_seen.month, "day": user.last_seen.day, "hour": user.last_seen.hour, "minute": user.last_seen.minute, "second": user.last_seen.second} if user.last_seen else None debug.debug("return {{ {} }}".format(dic)) return jsonify(dic) except Exception as err: diff --git a/flaschengeist/modules/geruecht/databaseController/dbCreditListController.py b/flaschengeist/modules/geruecht/databaseController/dbCreditListController.py index 62a3528..7011ea7 100644 --- a/flaschengeist/modules/geruecht/databaseController/dbCreditListController.py +++ b/flaschengeist/modules/geruecht/databaseController/dbCreditListController.py @@ -10,7 +10,7 @@ class Base: def getCreditListFromUser(self, user, **kwargs): try: if type(user) is User: - if user.uid == 'extern': + if user.userid == 'extern': return [] cursor = self.db.connection.cursor() if 'year' in kwargs: diff --git a/flaschengeist/modules/geruecht/databaseController/dbUserController.py b/flaschengeist/modules/geruecht/databaseController/dbUserController.py index 4c1dd1c..f93110e 100644 --- a/flaschengeist/modules/geruecht/databaseController/dbUserController.py +++ b/flaschengeist/modules/geruecht/databaseController/dbUserController.py @@ -83,7 +83,7 @@ class Base: cursor = self.db.connection.cursor() groups = self._convertGroupToString(user.group) cursor.execute("insert into user (uid, dn, firstname, lastname, gruppe, lockLimit, locked, autoLock, mail) VALUES ('{}','{}','{}','{}','{}',{},{},{},'{}')".format( - user.uid, user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail)) + user.userid, user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail)) self.db.connection.commit() except Exception as err: traceback.print_exc() @@ -96,7 +96,7 @@ class Base: cursor = self.db.connection.cursor() groups = self._convertGroupToString(user.group) sql = "update user set dn='{}', firstname='{}', lastname='{}', gruppe='{}', lockLimit={}, locked={}, autoLock={}, mail='{}' where uid='{}'".format( - user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail, user.uid) + user.dn, user.firstname, user.lastname, groups, user.limit, user.locked, user.autoLock, user.mail, user.userid) print(sql) cursor.execute(sql) self.db.connection.commit() @@ -109,7 +109,7 @@ class Base: try: cursor = self.db.connection.cursor() sql = "update user set last_seen='{}' where uid='{}'".format( - time, user.uid) + time, user.userid) print(sql) cursor.execute(sql) self.db.connection.commit() diff --git a/flaschengeist/modules/geruecht/finanzer/routes.py b/flaschengeist/modules/geruecht/finanzer/routes.py index 8b2e2fa..2e8d561 100644 --- a/flaschengeist/modules/geruecht/finanzer/routes.py +++ b/flaschengeist/modules/geruecht/finanzer/routes.py @@ -29,8 +29,8 @@ def _getFinanzer(**kwargs): users = mainController.getAllUsersfromDB() dic = {} for user in users: - dic[user.uid] = user.toJSON() - dic[user.uid]['creditList'] = { + dic[user.userid] = user.toJSON() + dic[user.userid]['creditList'] = { credit.year: credit.toJSON() for credit in user.geruechte} debug.debug("return {{ {} }}".format(dic)) return jsonify(dic) @@ -168,8 +168,8 @@ def _finanzerAddUser(**kwargs): users = mainController.getAllUsersfromDB() dic = {} for user in users: - dic[user.uid] = user.toJSON() - dic[user.uid]['creditList'] = { + dic[user.userid] = user.toJSON() + dic[user.userid]['creditList'] = { credit.year: credit.toJSON() for credit in user.geruechte} debug.debug("return {{ {} }}".format(dic)) return jsonify(dic), 200 diff --git a/flaschengeist/modules/geruecht/mainController/__init__.py b/flaschengeist/modules/geruecht/mainController/__init__.py index 61ce1b5..1d6f3f8 100644 --- a/flaschengeist/modules/geruecht/mainController/__init__.py +++ b/flaschengeist/modules/geruecht/mainController/__init__.py @@ -94,9 +94,9 @@ class MainController(#mainJobKindController.Base, def __updateDataFromLDAP(self, user): logger.info("update data from ldap for user {{ {} }}".format(user)) - groups = ldap.getGroup(user.uid) + groups = ldap.getGroup(user.userid) logger.debug("ldap gorups are {{ {} }}".format(groups)) - user_data = ldap.getUserData(user.uid) + user_data = ldap.getUserData(user.userid) logger.debug("ldap data is {{ {} }}".format(user_data)) user_data['gruppe'] = groups user_data['group'] = groups diff --git a/flaschengeist/modules/geruecht/mainController/mainCreditListController.py b/flaschengeist/modules/geruecht/mainController/mainCreditListController.py index 7e6f19f..9c56e6e 100644 --- a/flaschengeist/modules/geruecht/mainController/mainCreditListController.py +++ b/flaschengeist/modules/geruecht/mainController/mainCreditListController.py @@ -34,7 +34,7 @@ class Base: amount, username, month, year)) user = self.getUser(username) debug.debug("user is {{ {} }}".format(user)) - if user.uid == 'extern': + if user.userid == 'extern': debug.debug("user is extern user, so exit add amount") return if not user.locked or finanzer: @@ -60,7 +60,7 @@ class Base: credit, username, month, year)) user = self.getUser(username) debug.debug("user is {{ {} }}".format(user)) - if user.uid == 'extern': + if user.userid == 'extern': debug.debug("user is extern user, so exit add credit") return user.addCredit(credit, year=year, month=month) diff --git a/flaschengeist/modules/users/__init__.py b/flaschengeist/modules/users/__init__.py index d168c3d..28f2247 100644 --- a/flaschengeist/modules/users/__init__.py +++ b/flaschengeist/modules/users/__init__.py @@ -57,7 +57,7 @@ def __edit_user(uid, **kwargs): if not user: raise NotFound - if uid != kwargs["access_token"].user.uid and user.has_permissions(permissions["EDIT_USER"]): + if uid != kwargs["access_token"].user.userid and user.has_permissions(permissions["EDIT_USER"]): return Forbidden data = request.get_json() diff --git a/flaschengeist/system/controller/accessTokenController.py b/flaschengeist/system/controller/sessionController.py similarity index 55% rename from flaschengeist/system/controller/accessTokenController.py rename to flaschengeist/system/controller/sessionController.py index 17fe644..f92ace7 100644 --- a/flaschengeist/system/controller/accessTokenController.py +++ b/flaschengeist/system/controller/sessionController.py @@ -1,5 +1,5 @@ import secrets -from ..models.accessToken import AccessToken +from ..models.session import Session from flaschengeist.system.database import db from flaschengeist import logger from werkzeug.exceptions import Forbidden @@ -7,22 +7,22 @@ from datetime import datetime, timezone from . import Singleton -class AccessTokenController(metaclass=Singleton): - """Control all created AccessToken +class SessionController(metaclass=Singleton): + """Control all created Sessions - This Class create, delete, find and manage AccessToken. + This Class create, delete, find and manage Sessions. Attributes: - lifetime: Variable for the Lifetime of one AccessToken in seconds. + lifetime: Variable for the Lifetime of a Session in seconds. """ def __init__(self, lifetime=1800): self.lifetime = lifetime def validate_token(self, token, user_agent, permissions): - """Verify access token + """Verify session - Verify an AccessToken and Roles so if the User has permission or not. + Verify a Session and Roles so if the User has permission or not. Retrieves the access token if valid else retrieves False Args: @@ -30,10 +30,10 @@ class AccessTokenController(metaclass=Singleton): user_agent: User agent of browser to check permissions: Permissions needed to access restricted routes Returns: - An the AccessToken for this given Token or False. + A Session for this given Token or False. """ logger.debug("check token {{ {} }} is valid".format(token)) - access_token = AccessToken.query.filter_by(token=token).one_or_none() + access_token = Session.query.filter_by(token=token).one_or_none() if access_token: logger.debug("token found, check if expired or invalid user agent differs") if access_token.expires >= datetime.utcnow() and ( @@ -45,34 +45,34 @@ class AccessTokenController(metaclass=Singleton): return access_token else: logger.debug("access token is out of date or invalid client used") - self.delete_token(access_token) + self.delete_session(access_token) logger.debug("no valid access token with token: {{ {} }} and permissions: {{ {} }}".format(token, permissions)) return False - def create(self, user, user_agent=None) -> AccessToken: - """Create an AccessToken + def create(self, user, user_agent=None) -> Session: + """Create a Session Args: - user: For which User is to create an AccessToken + user: For which User is to create a Session user_agent: User agent to identify session Returns: - AccessToken: A created Token for User + Session: A created Token for User """ logger.debug("create access token") token_str = secrets.token_hex(16) - token = AccessToken( + session = Session( token=token_str, user=user, lifetime=self.lifetime, browser=user_agent.browser, platform=user_agent.platform ) - token.refresh() - db.session.add(token) + session.refresh() + db.session.add(session) db.session.commit() - logger.debug("access token is {{ {} }}".format(token)) - return token + logger.debug("access token is {{ {} }}".format(session.token)) + return session - def get_token(self, token, owner=None): - """Retrieves AccessToken from token string + def get_session(self, token, owner=None): + """Retrieves Session from token string Args: token (str): Token string @@ -81,38 +81,38 @@ class AccessTokenController(metaclass=Singleton): Raises: Forbidden: Raised if owner is set but does not match Returns: - AccessToken: Token object identified by given token string + Session: Token object identified by given token string """ - access_token = AccessToken.query.filter(AccessToken.token == token).one_or_none() - if access_token and (owner and owner != access_token.user): + session = Session.query.filter(Session.token == token).one_or_none() + if session and (owner and owner != session.user): raise Forbidden - return access_token + return session - def get_users_tokens(self, user): - return AccessToken.query.filter(AccessToken.user == user) + def get_users_sessions(self, user): + return Session.query.filter(Session.user == user) @staticmethod - def delete_token(token: AccessToken): - """Deletes given AccessToken + def delete_session(token: Session): + """Deletes given Session Args: - token (AccessToken): Token to delete + token (Session): Token to delete """ db.session.delete(token) db.session.commit() @staticmethod - def update_token(token): - token.refresh() + def update_session(session): + session.refresh() db.session.commit() - def set_lifetime(self, token, lifetime): - token.lifetime = lifetime - self.update_token(token) + def set_lifetime(self, session, lifetime): + session.lifetime = lifetime + self.update_session(session) def clear_expired(self): """Remove expired tokens from database""" - logger.debug("Clear expired AccessToken") - deleted = AccessToken.query.filter(AccessToken.expires < datetime.utcnow()).delete() - logger.debug("{} tokens have been removed".format(deleted)) + logger.debug("Clear expired Sessions") + deleted = Session.query.filter(Session.expires < datetime.utcnow()).delete() + logger.debug("{} sessions have been removed".format(deleted)) db.session.commit() diff --git a/flaschengeist/system/controller/userController.py b/flaschengeist/system/controller/userController.py index f986534..67a2008 100644 --- a/flaschengeist/system/controller/userController.py +++ b/flaschengeist/system/controller/userController.py @@ -7,9 +7,9 @@ from flaschengeist import logger def login_user(username, password): logger.info("login user {{ {} }}".format(username)) - user = User.query.filter_by(uid=username).one_or_none() + user = User.query.filter(User.userid == username).one_or_none() if user is None: - user = User(uid=username) + user = User(userid=username) db.session.add(user) if current_app.config["FG_AUTH_BACKEND"].login(user, password): update_user(user) @@ -49,4 +49,4 @@ def get_user_by_role(role: Role): def get_user(uid): - return User.query.filter(User.uid == uid).one_or_none() + return User.query.filter(User.userid == uid).one_or_none() diff --git a/flaschengeist/system/decorator.py b/flaschengeist/system/decorator.py index 9a8a6f4..4b703f5 100644 --- a/flaschengeist/system/decorator.py +++ b/flaschengeist/system/decorator.py @@ -6,9 +6,9 @@ from flaschengeist import logger def login_required(**kwargs): - from .controller.accessTokenController import AccessTokenController + from .controller.sessionController import SessionController - ac_controller = AccessTokenController() + ac_controller = SessionController() permissions = None if "permissions" in kwargs: permissions = kwargs["roles"] @@ -16,7 +16,7 @@ def login_required(**kwargs): def real_decorator(func): @wraps(func) def wrapper(*args, **kwargs): - token = request.headers.get("Token") + token = request.headers.get("Authorization").split(" ")[-1] access_token = ac_controller.validate_token(token, request.user_agent, permissions) if access_token: kwargs["access_token"] = access_token diff --git a/flaschengeist/system/models/accessToken.py b/flaschengeist/system/models/session.py similarity index 65% rename from flaschengeist/system/models/accessToken.py rename to flaschengeist/system/models/session.py index f94212c..2a1857a 100644 --- a/flaschengeist/system/models/accessToken.py +++ b/flaschengeist/system/models/session.py @@ -1,11 +1,13 @@ from datetime import datetime, timedelta, timezone + +from .user import User from ..database import db from secrets import compare_digest from flaschengeist import logger -class AccessToken(db.Model): - """Model for an AccessToken +class Session(db.Model): + """Model for a Session Args: expires: Is a Datetime from current Time. @@ -16,20 +18,20 @@ class AccessToken(db.Model): __tablename__ = "session" id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey("user.id")) - user = db.relationship("User", back_populates="sessions") + user: User = db.relationship("User", back_populates="sessions") - expires = db.Column(db.DateTime) - token = db.Column(db.String(32), unique=True) - lifetime = db.Column(db.Integer) - browser = db.Column(db.String(30)) - platform = db.Column(db.String(30)) + expires: datetime = db.Column(db.DateTime) + token: str = db.Column(db.String(32), unique=True) + lifetime: int = db.Column(db.Integer) + browser: str = db.Column(db.String(30)) + platform: str = db.Column(db.String(30)) def refresh(self): """Update the Timestamp Update the Timestamp to the current Time. """ - logger.debug("update timestamp from access token {{ {} }}".format(self)) + logger.debug("update timestamp from session with token {{ {} }}".format(self)) self.expires = datetime.utcnow() + timedelta(seconds=self.lifetime) def serialize(self): @@ -42,14 +44,10 @@ class AccessToken(db.Model): "token": self.token, "expires": self.expires.replace(tzinfo=timezone.utc), "lifetime": self.lifetime, + "user": self.user, "browser": self.browser, "platform": self.platform, } def __eq__(self, token): return compare_digest(self.token, token) - - def __str__(self): - return "AccessToken(user={}, token={}, expires={}, lifetime={})".format( - self.user, self.token, self.expires, self.lifetime - ) diff --git a/flaschengeist/system/models/user.py b/flaschengeist/system/models/user.py index 522611c..8fb1624 100644 --- a/flaschengeist/system/models/user.py +++ b/flaschengeist/system/models/user.py @@ -28,13 +28,13 @@ class User(db.Model): __tablename__ = "user" id = db.Column(db.Integer, primary_key=True) - uid = db.Column(db.String(30)) + userid = db.Column(db.String(30)) display_name = db.Column(db.String(30)) firstname = db.Column(db.String(30)) lastname = db.Column(db.String(30)) mail = db.Column(db.String(30)) roles = db.relationship("Role", secondary=association_table) - sessions = db.relationship("AccessToken", back_populates="user") + sessions = db.relationship("Session", back_populates="user") attributes = db.relationship( "UserAttribute", collection_class=attribute_mapped_collection("name"), cascade="all, delete" ) @@ -53,8 +53,8 @@ class User(db.Model): def update_data(self, data): logger.debug("update data of user") - if "uid" in data: - self.uid = data["uid"] + if "userid" in data: + self.userid = data["userid"] if "firstname" in data: self.firstname = data["firstname"] if "lastname" in data: @@ -76,7 +76,7 @@ class User(db.Model): def serialize(self): return { - "userid": self.uid, + "userid": self.userid, "display_name": self.display_name, "firstname": self.firstname, "lastname": self.lastname,