Skip to content
Permalink
Browse files

Allow userdb management with pycroft backend

  • Loading branch information...
Georgeto committed May 9, 2019
1 parent 16a644b commit 37c1616110d33542f03868cfca399c3dfe977ac7
@@ -4,10 +4,10 @@

from sipa.backends import DataSource, Dormitory
from sipa.backends.exceptions import InvalidConfiguration
from . import user, api
from . import user, api, userdb


def init_context(app):
def init_pycroft_api(app):
try:
app.extensions['pycroft_api'] = api.PycroftApi(
endpoint=app.config['PYCROFT_ENDPOINT'],
@@ -17,6 +17,15 @@ def init_context(app):
raise InvalidConfiguration(*exception.args)


def init_userdb(app):
userdb.register_userdb_extension(app)


def init_context(app):
init_pycroft_api(app)
init_userdb(app)


datasource = DataSource(
name='pycroft',
user_class=user.User,
@@ -12,6 +12,7 @@
from .exc import PycroftBackendError
from .schema import UserData, UserStatus
from .unserialize import UnserializationError
from .userdb import UserDB

from flask_login import AnonymousUserMixin
from flask.globals import current_app
@@ -30,6 +31,7 @@ class User(BaseUser):
def __init__(self, user_data: dict):
try:
self.user_data: UserData = UserData(user_data)
self._userdb: UserDB = UserDB(self)
except UnserializationError as e:
raise PycroftBackendError("Error when parsing user lookup response") from e
super().__init__(uid=str(self.user_data.id))
@@ -198,13 +200,24 @@ def hostname(self):
def hostalias(self):
raise NotImplementedError

@unsupported_prop
@active_prop
def userdb_status(self):
raise NotImplementedError
status = self.userdb.has_db

@unsupported_prop
if status is None:
return {'value': gettext("Datenbank nicht erreichbar"),
'style': 'danger', 'empty': True}
if status:
return {'value': gettext("Aktiviert"),
'style': 'success'}
return {'value': gettext("Nicht aktiviert"),
'empty': True}

userdb_status = userdb_status.fake_setter()

@property
def userdb(self):
raise NotImplementedError
return self._userdb

@property
def has_connection(self):
@@ -0,0 +1,121 @@
import logging
from ipaddress import IPv4Address, AddressValueError

from flask import current_app
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError

from sipa.model.user import BaseUserDB
from sipa.backends.exceptions import InvalidConfiguration

logger = logging.getLogger(__name__)


class UserDB(BaseUserDB):
def __init__(self, user):
super().__init__(user)

mask = current_app.config.get('DB_HELIOS_IP_MASK')
self.test_ipmask_validity(mask)
self.ip_mask = mask

@staticmethod
def test_ipmask_validity(mask):
"""Test whether a valid ip mask (at max one consecutive '%') was given
This is being done by replacing '%' with the maximum possible
value ('255'). Thus, everything surrounding the '%' except
for dots causes an invalid IPv4Address and thus a
`ValueError`.
"""
try:
IPv4Address(mask.replace("%", "255"))
except AddressValueError:
raise ValueError("Mask {} is not a valid IP address or contains "
"more than one consecutive '%' sign".format(mask))

@staticmethod
def sql_query(query, args=()):
"""Prepare and execute a raw sql query.
'args' is a tuple needed for string replacement.
"""
database = current_app.extensions['db_helios']
conn = database.connect()
result = conn.execute(query, args)
conn.close()
return result

@property
def has_db(self):
try:
userdb = self.sql_query(
"SELECT SCHEMA_NAME "
"FROM INFORMATION_SCHEMA.SCHEMATA "
"WHERE SCHEMA_NAME = %s",
(self.db_name(),),
).fetchone()

return userdb is not None
except OperationalError:
logger.critical("User db of user %s unreachable", self.db_name())
return None

def create(self, password):
self.sql_query(
"CREATE DATABASE "
"IF NOT EXISTS `%s`" % self.db_name(),
)
self.change_password(password)

def drop(self):
self.sql_query(
"DROP DATABASE "
"IF EXISTS `%s`" % self.db_name(),
)

self.sql_query(
"DROP USER %s@%s",
(self.db_name(), self.ip_mask),
)

def change_password(self, password):
user = self.sql_query(
"SELECT user "
"FROM mysql.user "
"WHERE user = %s",
(self.db_name(),),
).fetchall()

if not user:
self.sql_query(
"CREATE USER %s@%s "
"IDENTIFIED BY %s",
(self.db_name(), self.ip_mask, password,),
)
else:
self.sql_query(
"SET PASSWORD "
"FOR %s@%s = PASSWORD(%s)",
(self.db_name(), self.ip_mask, password,),
)

self.sql_query(
"GRANT SELECT, INSERT, UPDATE, DELETE, "
"ALTER, CREATE, DROP, INDEX, LOCK TABLES "
"ON `{}`.* "
"TO %s@%s".format(self.db_name()),
(self.db_name(), self.ip_mask),
)

def db_name(self):
return self.user.login.value


def register_userdb_extension(app):
try:
app.extensions['db_helios'] = create_engine(
app.config['DB_HELIOS_URI'],
echo=False, connect_args={'connect_timeout': app.config['SQL_TIMEOUT']}
)
except KeyError as exception:
raise InvalidConfiguration(*exception.args)
@@ -296,8 +296,11 @@ def __init__(self, user: BaseUser):

@property
@abstractmethod
def has_db(self) -> bool:
"""**[Abstract]** Wheter the database is enabled"""
def has_db(self) -> Optional[bool]:
"""**[Abstract]** Whether the database is enabled
Returns None, if the user database is unreachable.
"""
pass

@abstractmethod
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-02-11 02:40+0100\n"
"POT-Creation-Date: 2019-05-10 01:31+0200\n"
"PO-Revision-Date: 2014-08-01 19:22+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@@ -379,6 +379,9 @@ msgstr ""
msgid "Nicht aktiviert"
msgstr ""

msgid "Datenbank nicht erreichbar"
msgstr ""

msgid "Verstoß gegen Netzordnung"
msgstr ""

Binary file not shown.
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Sipa\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2019-02-11 02:40+0100\n"
"PO-Revision-Date: 2019-02-11 02:41+0100\n"
"POT-Creation-Date: 2019-05-10 01:31+0200\n"
"PO-Revision-Date: 2019-05-10 01:31+0200\n"
"Last-Translator: Lukas Juhrich <lukasjuhrich@wh2.tu-dresden.de>\n"
"Language: en\n"
"Language-Team: \n"
@@ -387,6 +387,9 @@ msgstr "Passive"
msgid "Nicht aktiviert"
msgstr "Not activated"

msgid "Datenbank nicht erreichbar"
msgstr "Database is unreachable"

msgid "Verstoß gegen Netzordnung"
msgstr "Violation of network usage agreement"

@@ -776,9 +779,6 @@ msgstr "Outgoing"
#~ msgid "Unbekannt"
#~ msgstr "Unknown"

#~ msgid "Datenbank nicht erreichbar"
#~ msgstr "Database cannot be reached"

#~ msgid "Für Details klicken"
#~ msgstr "Click for details"

0 comments on commit 37c1616

Please sign in to comment.
You can’t perform that action at this time.