258 lines
9.2 KiB
Python
258 lines
9.2 KiB
Python
import sqlite3
|
|
from typing import List
|
|
from sqlalchemy.engine import Engine
|
|
from sqlalchemy import ForeignKey, event
|
|
from sqlalchemy import Column, Integer, String, DateTime
|
|
import datetime, decimal
|
|
from db import dbauth
|
|
|
|
|
|
SYS_ADMIN = "SysAdmin"
|
|
APP_ADMIN = "AppAdmin"
|
|
APP_USER = "User"
|
|
APP_DOZENT = "Doz"
|
|
|
|
BINDKEY = 'auth'
|
|
#c = Connection()
|
|
|
|
def columns2dict(record, excludefields=[]):
|
|
if not isinstance(excludefields, list):
|
|
excludefields = []
|
|
attribs = dict()
|
|
if hasattr(record, '__table__'):
|
|
if hasattr(record.__table__, 'columns'):
|
|
for col in record.__table__.columns:
|
|
if col.name in excludefields:
|
|
continue
|
|
value = getattr(record, col.name)
|
|
if isinstance(value, datetime.date):
|
|
value = value.isoformat()
|
|
if isinstance(value, decimal.Decimal):
|
|
value = "{0:.2f}".format(value)
|
|
attribs[col.name] = value
|
|
return attribs
|
|
|
|
@event.listens_for(Engine, "connect")
|
|
def set_sqlite_pragma(dbapi_connection, connection_record):
|
|
# Dieser Trigger darf nur bei einer Sqlite-Verbindung zünden.
|
|
# Bei einer VErbindugn zum MS SQL-Server darf er nicht ausgelöst werden
|
|
if isinstance(dbapi_connection, sqlite3.Connection):
|
|
cursor = dbapi_connection.cursor()
|
|
cursor.execute("PRAGMA foreign_keys=ON")
|
|
cursor.close()
|
|
|
|
class Systeminfo(dbauth.Model):
|
|
__tablename__ = 'systeminfo'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
version = Column(String(20), nullable=False)
|
|
lastupdate = Column(DateTime, nullable=True)
|
|
initialinstall = Column(DateTime, nullable=True)
|
|
|
|
# diese Tabelle enthält für alle Apps diejenigen Rollen, die in der App notwendig sind
|
|
# Da die Rollen nur einmal in der Tabelle "Role" definiert sind und mehrere Apps die gleichen
|
|
# Rollen haben können, handelt es sich im eine "many-to-many"-Relation, die in dieser
|
|
# Hilfstabelle verwaltet werden
|
|
apps_roles = dbauth.Table(
|
|
'apps_roles',
|
|
Column('appid', Integer, ForeignKey('applications.id'), primary_key=True),
|
|
Column('roleid', Integer, ForeignKey('roles.id'), primary_key=True),
|
|
bind_key=BINDKEY
|
|
)
|
|
|
|
# diese Tabelle enthält für alle Apps, in denen der User eine Rolle hat, die Rollen,
|
|
# die der User in der jeweiligen App hat.
|
|
# Da die Rollen nur einmal in der Tabelle "Role" definiert sind und verschiedene einem User
|
|
# zugeordnete Apps die gleichen Rollen haben können, handelt es sich im eine
|
|
# "many-to-many"-Relation, die in dieser Hilfstabelle verwaltet werden
|
|
userapps_roles = dbauth.Table(
|
|
'userapps_roles',
|
|
Column('userappid', Integer, ForeignKey('userapps.id'), primary_key=True),
|
|
Column('roleid', Integer, ForeignKey('roles.id'), primary_key=True),
|
|
bind_key=BINDKEY
|
|
)
|
|
|
|
class Role(dbauth.Model):
|
|
__tablename__ = 'roles'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
roleid = Column(String(80), index=True, unique=True, nullable=False, default=None)
|
|
name = Column(String(80), nullable=False, default=None)
|
|
level = Column(Integer, default=0)
|
|
beschreibung = Column(String(800))
|
|
|
|
def to_dict(self):
|
|
if self.level is None:
|
|
self.level = 0
|
|
return columns2dict(self)
|
|
|
|
# Die Tabelle "UserApp" enthält für jeden User für jede App, in der der User eine Rolle hat,
|
|
# einen Datensatz.
|
|
class Userapp(dbauth.Model):
|
|
__tablename__ = 'userapps'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
userid = Column(ForeignKey('users.id'), index=True)
|
|
appid = Column(ForeignKey('applications.id'))
|
|
userapp_roles = dbauth.relationship('Role', secondary=userapps_roles, lazy='subquery', order_by="asc(Role.roleid)")
|
|
application = dbauth.relationship('Application', uselist=False)
|
|
|
|
def get_role(self, roleid: str) -> Role:
|
|
"""
|
|
Die Methode gibt die Rolle aus der userapp zurück, die mit der roleid (nicht id) angegeben wurde.
|
|
Gibt es keine Rolle mit der übergebenen roleid, wird 'None' zurückgegeben
|
|
- roleid: str
|
|
- die roleid (nicht id) der Rollue, die gesucht wird
|
|
"""
|
|
for role in self.userapp_roles:
|
|
if role.id == roleid:
|
|
return role
|
|
return None
|
|
|
|
def to_dict(self, withrelations=True):
|
|
result = columns2dict(self)
|
|
result["userapp_roles"] = []
|
|
if withrelations:
|
|
for userapprole in self.userapp_roles:
|
|
result["userapp_roles"].append(userapprole.to_dict())
|
|
return result
|
|
|
|
class User(dbauth.Model):
|
|
__tablename__ = 'users'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
userid = Column(String(80), index=True, unique=True, nullable=False, default='')
|
|
username = Column(String(80), nullable=False)
|
|
password = Column(String(40), nullable=False)
|
|
email = Column(String(120))
|
|
telefon = Column(String(800))
|
|
# Jeder User kann mehrere "User-Apps" haben. Dies sind die Apps, in denen dem User Rollen
|
|
# zugewiesen wurden. Es handelt sich um eine "one-to-many"-Relation, da ein User zwar mehrere
|
|
# User-Apps haben kann, jede User-App aber genau einem User gehört
|
|
userapps = dbauth.relationship('Userapp')
|
|
|
|
def get_userapp(self, appid:str) -> Userapp:
|
|
"""
|
|
Die Funktion liefert die userapp mit Hilfe der appid (nicht der id) zurück.
|
|
Wenn der User keine userapp für die Application hat, wird 'None' zurückgegeben
|
|
|
|
- appid: string
|
|
- Der Kurzname der Application (nicht die id)
|
|
"""
|
|
for userapp in self.userapps:
|
|
app = userapp.application
|
|
if app.appid == appid:
|
|
return userapp
|
|
return None
|
|
|
|
def get_userapp_by_id(self, id:int) -> Userapp:
|
|
"""
|
|
Die Funktion liefert die userapp mit Hilfe der id (nicht der appid) zurück
|
|
|
|
- appid: int
|
|
- die interne Record-ID der Application (nicht die userid)
|
|
"""
|
|
for userapp in self.userapps:
|
|
app = userapp.application
|
|
if app.id == id:
|
|
return userapp
|
|
return None
|
|
|
|
def get_userapproles(self, appid:str) -> List[Role]:
|
|
"""
|
|
Die Funktion liefert die roles zurück, die der User in der durch die appid (nicht id) bestimmten Application hat
|
|
|
|
- appid: str
|
|
- Der Kurzname der Application
|
|
"""
|
|
userapp: Userapp = self.get_userapp(appid)
|
|
if userapp is not None:
|
|
return userapp.userapp_roles
|
|
else:
|
|
return []
|
|
|
|
def has_roles(self, roleids: List[str]) -> bool:
|
|
"""
|
|
Die Funktion liefert 'True' zurück, falls der User in irgendeiner Application eine der Rollen hat, deren roleids
|
|
übergeben wurden, ansonsten 'False'
|
|
|
|
- roleids: List[str]
|
|
- eine Liste mit roleids (nicht ids)
|
|
"""
|
|
for userapp in self.userapps:
|
|
for role in userapp.userapp_roles:
|
|
if role.roleid in roleids:
|
|
return True
|
|
return False
|
|
|
|
def is_SysAdmin(self) -> bool:
|
|
"""
|
|
die Methode liefert 'True' zurück, wenn der User in irgendeiner Anwendung SysAdmin ist
|
|
"""
|
|
return self.has_roles([SYS_ADMIN])
|
|
|
|
def __repr__(self):
|
|
return '<User %r>' % self.username
|
|
|
|
def to_dict(self, withrelations=False):
|
|
result = columns2dict(self, excludefields=['password'])
|
|
result['userapps'] = []
|
|
if withrelations:
|
|
for userapp in self.userapps:
|
|
result['userapps'].append(userapp.to_dict())
|
|
return result
|
|
|
|
def to_relationaldict(self):
|
|
return self.to_dict(withrelations=True)
|
|
|
|
def has_permission(self, appid:str, allowed_roles: List[str]):
|
|
"""
|
|
Gibt an, ob der User in einer bestimmten Anwendung eine der gewünschten Rollen hat
|
|
- appid: string
|
|
- Die appid (nicht id) der gewünschten Application
|
|
- allowed_roles: List[str]
|
|
- Eine Liste mit den roleids (nicht ids) der erlaubten Rollen
|
|
"""
|
|
userapp = self.get_userapp(appid)
|
|
if userapp is not None:
|
|
rolleerlaubt = any([role.roleid in allowed_roles for role in userapp.userapp_roles])
|
|
if rolleerlaubt:
|
|
return True
|
|
return False
|
|
|
|
|
|
|
|
# Die nachfolgenden Properties werden für flask_login benötigt
|
|
@property
|
|
def is_authenticated(self):
|
|
return True
|
|
|
|
@property
|
|
def is_active(self):
|
|
return True
|
|
|
|
@property
|
|
def is_anonymous(self):
|
|
return False
|
|
|
|
def get_id(self):
|
|
return self.userid
|
|
|
|
class Application(dbauth.Model):
|
|
__tablename__ = 'applications'
|
|
__bind_key__ = BINDKEY
|
|
id = Column(Integer, primary_key=True)
|
|
appid = Column(String(80), index=True, unique=True, nullable=False, default=None)
|
|
name = Column(String(80), nullable=False, default=None)
|
|
minversion = Column(String(20))
|
|
beschreibung = Column(String(800))
|
|
roles = dbauth.relationship('Role', secondary=apps_roles, lazy='subquery', order_by="asc(Role.roleid)")
|
|
|
|
def to_dict(self, withrelations=True):
|
|
result = columns2dict(self)
|
|
result['roles'] = []
|
|
for role in self.roles:
|
|
result['roles'].append(role.to_dict())
|
|
return result
|
|
|
|
print("########## auth/model importiert") |