Some more cleanup, added modify_user to LDAP

This commit is contained in:
Ferdinand Thiessen 2020-09-04 00:55:23 +02:00
parent 7fbff30214
commit 365677697d
9 changed files with 115 additions and 351 deletions

View File

@ -69,7 +69,7 @@ def create_app():
for entry_point in pkg_resources.iter_entry_points('flaschengeist.plugin'):
logger.debug("Found plugin: %s", entry_point.name)
if config.get(entry_point.name, 'enabled', fallback=False):
logger.info('Loaded plugin > %s <', entry_point.name)
logger.info("Loaded plugin >{}<".format(entry_point.name))
app.register_blueprint(entry_point.load()())
@app.errorhandler(Exception)
@ -81,4 +81,4 @@ def create_app():
logger.error(str(e), exc_info=True)
return jsonify({"error": "Internal server error occurred"}), 500
return app
return app

View File

@ -9,7 +9,7 @@ AUTH = auth_plain
[DATABASE]
USER =
HOST =
PASSWD =
PASSWORD =
DATABASE =
# [LDAP]

View File

@ -3,29 +3,34 @@ class Auth:
pass
def login(self, user, pw):
"""
user User class containing at least the uid
pw given password
""" Login routine, MUST BE IMPLEMENTED!
MUST BE IMPLEMENTED!
should return False if not found or invalid credentials
should return True if success
Args:
user: User class containing at least the uid
pw: given password
Returns:
Must return False if not found or invalid credentials, True if success
"""
raise NotImplementedError
def update_user(self, user):
"""
user User class
"""If backend is using external data, then update this user instance with external data
If backend is using external data, then update this user instance with external data
Args:
user: User object
"""
pass
def modify_user(self, user):
"""
user User class
def modify_user(self, user, password, new_password=None):
"""If backend is using (writeable) external data, then update the external database with the user provided.
If backend is using (writeable) external data, then update the external database with the user provided.
Args:
user: User object
password: Password (some backends need the current password for changes)
new_password: If set a password change is requested
Raises:
NotImplemented: If backend does not support this feature (or no password change)
BadRequest: Logic error, e.g. password is wrong.
Error: Other errors if backend went mad (are not handled and will result in a 500 error)
"""
pass
raise NotImplemented

View File

@ -5,15 +5,14 @@
#############################################
from flask import Blueprint, request, jsonify
from werkzeug.exceptions import Forbidden, BadRequest
from werkzeug.exceptions import Forbidden, BadRequest, Unauthorized
from werkzeug.local import LocalProxy
from flaschengeist import logger
from flaschengeist.system.decorator import login_required
from flaschengeist.system.controller import mainController as mc
import flaschengeist.system.controller.accessTokenController as ac
from flaschengeist.system.controller import accessTokenController, userController
access_controller = LocalProxy(lambda: ac.AccessTokenController())
access_controller = LocalProxy(lambda: accessTokenController.AccessTokenController())
auth_bp = Blueprint('auth', __name__)
@ -46,8 +45,9 @@ def _create_token():
password = data['password']
logger.debug("search user {{ {} }} in database".format(username))
main_controller = mc.MainController()
user = main_controller.login_user(username, password)
user = userController.login_user(username, password)
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))

View File

@ -1,18 +1,23 @@
from ldap3.core.exceptions import LDAPException
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError
from ldap3.utils.hashed import hashed
from werkzeug.exceptions import BadRequest
import flaschengeist.modules as modules
from flaschengeist import logger
from flask import current_app as app
from flask_ldapconn import LDAPConn
from ldap3 import SUBTREE
from ldap3 import SUBTREE, MODIFY_REPLACE, HASHED_SALTED_SHA512
import ssl
from flaschengeist.system.models.user import User
class AuthLDAP(modules.Auth):
_default = {
'PORT': '389',
'USE_SSL': 'False'
}
ldap = None
dn = None
def configure(self, config):
for name in self._default:
@ -31,81 +36,13 @@ class AuthLDAP(modules.Auth):
)
if 'SECRET' in config:
app.config['LDAP_SECRET'] = config['SECRET'],
self.ldap = LDAPConn(app)
self.dn = config['BASEDN']
def login(self, user, password):
if not user:
return False
try:
r = self.ldap.authenticate(user.uid, password, 'uid', self.dn)
return r == True
except LDAPException as err:
logger.warning("Exception while login into ldap", exc_info=True)
return False
def modify_user(self, user):
try:
ldap_conn = self.ldap.bind(user.uid, password)
if attributes:
if 'username' in attributes:
debug.debug("change username, so change first in database")
db.changeUsername(user, attributes['username'])
ldap.modifyUser(user, ldap_conn, attributes)
if 'username' in attributes:
retVal = self.getUser(attributes['username'])
debug.debug("user is {{ {} }}".format(retVal))
return retVal
else:
retVal = self.getUser(user.uid)
debug.debug("user is {{ {} }}".format(retVal))
return retVal
return self.getUser(user.uid)
except UsernameExistLDAP as err:
debug.debug(
"username exists on ldap, rechange username on database", exc_info=True)
db.changeUsername(user, user.uid)
raise Exception(err)
except LDAPException as err:
if 'username' in attributes:
db.changeUsername(user, user.uid)
raise Exception(err)
except LDAPPasswordIsMandatoryError as err:
raise Exception('Password wurde nicht gesetzt!!')
except LDAPBindError as err:
raise Exception('Password ist falsch')
except Exception as err:
raise Exception(err)
debug.info(
"modify ldap data from user {{ {} }} with attributes (can't show because here can be a password)".format(
user))
try:
if 'username' in attributes:
debug.debug("change username")
conn.search('ou=user,{}'.format(self.dn), '(uid={})'.format(attributes['username']))
if conn.entries:
debug.warning("username already exists", exc_info=True)
raise UsernameExistLDAP("Username already exists in LDAP")
# create modifyer
mody = {}
if 'username' in attributes:
mody['uid'] = [(MODIFY_REPLACE, [attributes['username']])]
if 'firstname' in attributes:
mody['givenName'] = [(MODIFY_REPLACE, [attributes['firstname']])]
if 'lastname' in attributes:
mody['sn'] = [(MODIFY_REPLACE, [attributes['lastname']])]
if 'mail' in attributes:
mody['mail'] = [(MODIFY_REPLACE, [attributes['mail']])]
if 'password' in attributes:
salted_password = hashed(HASHED_SALTED_MD5, attributes['password'])
mody['userPassword'] = [(MODIFY_REPLACE, [salted_password])]
debug.debug("modyfier are (can't show because here can be a password)")
conn.modify(user.dn, mody)
except Exception as err:
debug.warning("exception in modify user data from ldap", exc_info=True)
raise LDAPExcetpion("Something went wrong in LDAP: {}".format(err))
return self.ldap.authenticate(user.uid, password, 'uid', self.dn)
def update_user(self, user):
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(user.uid), SUBTREE,
@ -123,80 +60,37 @@ class AuthLDAP(modules.Auth):
user.add_group(group)
def _get_groups(self, uid):
try:
groups = []
groups = []
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(uid), SUBTREE,
attributes=['gidNumber'])
main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber']
if main_group_number:
if type(main_group_number) is list:
main_group_number = main_group_number[0]
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number),
attributes=['cn'])
groups.append(self.ldap.connection.response[0]['attributes']['cn'][0])
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(uid), SUBTREE,
self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid={})'.format(uid), SUBTREE,
attributes=['gidNumber'])
main_group_number = self.ldap.connection.response[0]['attributes']['gidNumber']
if main_group_number:
if type(main_group_number) is list:
main_group_number = main_group_number[0]
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(gidNumber={})'.format(main_group_number),
attributes=['cn'])
groups_data = self.ldap.connection.response
for data in groups_data:
groups.append(data['attributes']['cn'][0])
return groups
except Exception as err:
logger.warning("exception in get groups from ldap", exc_info=True)
return []
groups.append(self.ldap.connection.response[0]['attributes']['cn'][0])
# def getAllUser(self):
# debug.info("get all users from ldap")
# retVal = []
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
# data = self.ldap.connection.response
# debug.debug("data is {{ {} }}".format(data))
# for user in data:
# if 'uid' in user['attributes']:
# username = user['attributes']['uid'][0]
# firstname = user['attributes']['givenName'][0]
# lastname = user['attributes']['sn'][0]
# retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname})
# debug.debug("users are {{ {} }}".format(retVal))
# return retVal
self.ldap.connection.search('ou=group,{}'.format(self.dn), '(memberUID={})'.format(uid), SUBTREE,
attributes=['cn'])
groups_data = self.ldap.connection.response
for data in groups_data:
groups.append(data['attributes']['cn'][0])
return groups
# def searchUser(self, searchString):
#
# name = searchString.split(" ")
#
# for i in range(len(name)):
# name[i] = "*"+name[i]+"*"
#
#
# print(name)
#
# name_result = []
#
# if len(name) == 1:
# if name[0] == "**":
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(uid=*)', SUBTREE,
# attributes=['uid', 'givenName', 'sn'])
# name_result.append(self.ldap.connection.response)
# else:
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
# name_result.append(self.ldap.connection.response)
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[0]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
# name_result.append(self.ldap.connection.response)
# else:
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(givenName={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn'])
# name_result.append(self.ldap.connection.response)
# self.ldap.connection.search('ou=user,{}'.format(self.dn), '(sn={})'.format(name[1]), SUBTREE, attributes=['uid', 'givenName', 'sn', 'mail'])
# name_result.append(self.ldap.connection.response)
# retVal = []
def modify_user(self, user: User, password, new_password=None):
try:
ldap_conn = self.ldap.connect(user.uid, password)
modifier = {'givenName': [(MODIFY_REPLACE, [user.firstname])],
'sn': [(MODIFY_REPLACE, [user.lastname])],
'mail': [(MODIFY_REPLACE, [user.mail])],
'displayName': [(MODIFY_REPLACE, [user.display_name])],
}
if new_password:
salted_password = hashed(HASHED_SALTED_SHA512, new_password)
modifier['userPassword'] = [(MODIFY_REPLACE, [salted_password])]
ldap_conn.modify(user.dn, modifier)
# for names in name_result:
# for user in names:
# if 'uid' in user['attributes']:
# username = user['attributes']['uid'][0]
# if not self.__isUserInList(retVal, username):
# firstname = user['attributes']['givenName'][0]
# lastname = user['attributes']['sn'][0]
# retVal.append({'username': username, 'firstname': firstname, 'lastname': lastname})
#
# return retVal
except (LDAPPasswordIsMandatoryError, LDAPBindError):
raise BadRequest

View File

@ -3,6 +3,7 @@ import hashlib
import os
import flaschengeist.modules as modules
from flaschengeist.system.models.user import User
def _hash_password(password):
@ -21,9 +22,10 @@ def _verify_password(stored_password, provided_password):
class AuthPlain(modules.Auth):
def login(self, user, password):
if not user:
return False
if 'password' in user.attributes:
def login(self, user: User, password: str):
if user and 'password' in user.attributes:
return _verify_password(user.attributes['password'].value, password)
return False
def modify_user(self, user, password, new_password=None):
pass

View File

@ -1,7 +1,6 @@
from .. import Singleton
from .. import Singleton, userController
from ...models.user import User
from datetime import datetime, timedelta
from . import mainUserController
from ...database import db
from flask import current_app
from werkzeug.local import LocalProxy
@ -10,7 +9,6 @@ logger = LocalProxy(lambda: current_app.logger)
class MainController(#mainJobKindController.Base,
#mainCreditListController.Base,
#mainPricelistController.Base,
mainUserController.Base,
#mainWorkerController.Base,
#mainWorkgroupController.Base,
#mainJobInviteController.Base,
@ -134,7 +132,7 @@ class MainController(#mainJobKindController.Base,
return retVal
def sendAllMail(self):
debug.info("send mail to all user")
debug.info("send mail to all users")
retVal = []
users = db.getAllUser()
debug.debug("users are {{ {} }}".format(users))

View File

@ -1,175 +0,0 @@
from flask import current_app
from flaschengeist.system.exceptions import PermissionDenied
from flaschengeist.system.models.user import User
from flaschengeist.system.database import db
from flaschengeist import logger
class Base:
def login_user(self, username, password):
logger.info("login user {{ {} }}".format(username))
user = User.query.filter_by(uid=username).first()
if user is None:
user = User(uid=username)
if current_app.config['FG_AUTH_BACKEND'].login(user, password):
db.session.add(user)
current_app.config['FG_AUTH_BACKEND'].update_user(user)
db.session.commit()
return user
raise PermissionDenied()
#def getAllStatus(self):
#debug.info("get all status for user")
#retVal = db.getAllStatus()
#debug.debug("status are {{ {} }}".format(retVal))
#return retVal
#def getStatus(self, name):
#debug.info("get status of user {{ {} }}".format(name))
#retVal = db.getStatus(name)
#debug.debug("status of user {{ {} }} is {{ {} }}".format(name, retVal))
#return retVal
#def setStatus(self, name):
#debug.info("set status of user {{ {} }}".format(name))
#retVal = db.setStatus(name)
#debug.debug(
#"settet status of user {{ {} }} is {{ {} }}".format(name, retVal))
#return retVal
#def deleteStatus(self, status):
#debug.info("delete status {{ {} }}".format(status))
#db.deleteStatus(status)
#def updateStatus(self, status):
#debug.info("update status {{ {} }}".format(status))
#retVal = db.updateStatus(status)
#debug.debug("updated status is {{ {} }}".format(retVal))
#return retVal
#def updateStatusOfUser(self, username, status):
#debug.info("update status {{ {} }} of user {{ {} }}".format(
#status, username))
#retVal = db.updateStatusOfUser(username, status)
#debug.debug(
#"updatet status of user {{ {} }} is {{ {} }}".format(username, retVal))
#return retVal
#def updateVotingOfUser(self, username, voting):
#debug.info("update voting {{ {} }} of user {{ {} }}".format(
#voting, username))
#retVal = db.updateVotingOfUser(username, voting)
#debug.debug(
#"updatet voting of user {{ {} }} is {{ {} }}".format(username, retVal))
#return retVal
#def lockUser(self, username, locked):
#debug.info("lock user {{ {} }} for credit with status {{ {} }}".format(
#username, locked))
#user = self.getUser(username)
#debug.debug("user is {{ {} }}".format(user))
#user.updateData({'locked': locked})
#db.updateUser(user)
#retVal = self.getUser(username)
#debug.debug("locked user is {{ {} }}".format(retVal))
#return retVal
#def updateConfig(self, username, data):
#debug.info(
#"update config of user {{ {} }} with config {{ {} }}".format(username, data))
#user = self.getUser(username)
#debug.debug("user is {{ {} }}".format(user))
#user.updateData(data)
#db.updateUser(user)
#retVal = self.getUser(username)
#debug.debug("updated config of user is {{ {} }}".format(retVal))
#return retVal
#def syncLdap(self):
#debug.info('sync Users from Ldap')
#ldap_users = ldap.getAllUser()
#for user in ldap_users:
#self.getUser(user['username'])
#def getAllUsersfromDB(self, extern=True):
#debug.info("get all users from database")
#if (len(ldap.getAllUser()) != len(db.getAllUser())):
#self.syncLdap()
#users = db.getAllUser()
#debug.debug("users are {{ {} }}".format(users))
#for user in users:
#try:
#debug.debug("update data from ldap")
#self.__updateDataFromLDAP(user)
#except:
#pass
#debug.debug("update creditlists")
#self.__updateGeruechte(user)
#retVal = db.getAllUser(extern=extern)
#debug.debug("all users are {{ {} }}".format(retVal))
#return retVal
#def getUser(self, username):
#debug.info("get user {{ {} }}".format(username))
#user = db.getUser(username)
#debug.debug("user is {{ {} }}".format(user))
#groups = ldap.getGroup(username)
#debug.debug("groups are {{ {} }}".format(groups))
#user_data = ldap.getUserData(username)
#debug.debug("user data from ldap is {{ {} }}".format(user_data))
#user_data['gruppe'] = groups
#user_data['group'] = groups
#if user is None:
#debug.debug("user not exists in database -> insert into database")
#user = User(user_data)
#db.insertUser(user)
#else:
#debug.debug("update database with user")
#user.updateData(user_data)
#db.updateUser(user)
#user = db.getUser(username)
#self.__updateGeruechte(user)
#debug.debug("user is {{ {} }}".format(user))
#return user
#def modifyUser(self, user, attributes, password):
#debug.info("modify user {{ {} }} with attributes (can't show because here can be a password)".format(
#user))
#try:
#ldap_conn = ldap.bind(user, password)
#if attributes:
#if 'username' in attributes:
#debug.debug("change username, so change first in database")
#db.changeUsername(user, attributes['username'])
#ldap.modifyUser(user, ldap_conn, attributes)
#if 'username' in attributes:
#retVal = self.getUser(attributes['username'])
#debug.debug("user is {{ {} }}".format(retVal))
#return retVal
#else:
#retVal = self.getUser(user.uid)
#debug.debug("user is {{ {} }}".format(retVal))
#return retVal
#return self.getUser(user.uid)
#except UsernameExistLDAP as err:
#debug.debug(
#"username exists on ldap, rechange username on database", exc_info=True)
#db.changeUsername(user, user.uid)
#raise Exception(err)
#except LDAPExcetpion as err:
#if 'username' in attributes:
#db.changeUsername(user, user.uid)
#raise Exception(err)
#except LDAPPasswordIsMandatoryError as err:
#raise Exception('Password wurde nicht gesetzt!!')
#except LDAPBindError as err:
#raise Exception('Password ist falsch')
#except Exception as err:
#raise Exception(err)
#def validateUser(self, username, password):
#debug.info("validate user {{ {} }}".format(username))
#ldap.login(username, password)

View File

@ -0,0 +1,40 @@
from flask import current_app
from flaschengeist.system.models.user import User
from flaschengeist.system.database import db
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()
if user is None:
user = User(uid=username)
db.session.add(user)
if current_app.config['FG_AUTH_BACKEND'].login(user, password):
current_app.config['FG_AUTH_BACKEND'].update_user(user)
db.session.commit()
return user
def modify_user(user, password, new_password=None):
"""Modify given user on the backend
Args:
user: User object to sync with backend
password: Current password (most backends are needing this)
new_password (optional): New password, if password should be changed
Raises:
NotImplemented: If backend is not capable of this operation
BadRequest: Password is wrong or other logic issues
"""
current_app.config['FG_AUTH_BACKEND'].modify_user(user, password, new_password)
def get_users():
return User.query.all()
def get_user(uid):
return User.query.filter(User.uid == uid).one_or_none()