Files
kbcrm/App_Server/root/auth/model/__init__.py
T

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