Rename AccessToken model to Session, same with controller.

This commit is contained in:
Ferdinand Thiessen 2020-10-19 01:41:54 +02:00
parent ec05cde746
commit c629f5abf3
18 changed files with 113 additions and 107 deletions

View File

@ -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

View File

@ -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:

View File

@ -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):

View File

@ -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/<token>", 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/<token>", 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/<token>", 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

View File

@ -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):

View File

@ -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)

View File

@ -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,
@ -51,7 +51,7 @@ def _bar(**kwargs):
"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:

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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
)

View File

@ -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,