[Plugin] Added plugin function when roles are modified

LDAP: Use same config style as the rest.
This commit is contained in:
Ferdinand Thiessen 2020-11-13 01:20:25 +01:00
parent 96765ee932
commit cbcd5b39a3
5 changed files with 62 additions and 52 deletions

View File

@ -24,23 +24,23 @@ enabled = true
#[auth_ldap] #[auth_ldap]
# enabled = true # enabled = true
# URL = # host =
# PORT = # port =
# BINDDN = # bind_dn =
# BASEDN = # base_dn =
# SECRET = # secret =
# USE_SSL = # use_ssl =
# ADMIN_DN = # admin_dn =
# ADMIN_SECRET = # admin_dn =
# gidNumber = # default_gid =
#[users] #[users]
# allways enabled # always enabled
# #
## allowed values: false, "managed", "public" ## allowed values: false, "managed", "public"
## false: Disable registration ## false: Disable registration
## "managed": only users with matching permission are allowed to register new users ## "managed": only users with matching permission are allowed to register new users
## "public": Also unautheticated users can register an account ## "public": Also unauthenticated users can register an account
# registration = False # registration = False
############################ ############################

View File

@ -1,3 +1,4 @@
from flask import current_app
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from werkzeug.exceptions import BadRequest, NotFound from werkzeug.exceptions import BadRequest, NotFound
@ -24,7 +25,12 @@ def get_permissions():
return Permission.query.all() return Permission.query.all()
def update_role(role): def rename(role, new_name):
if db.session.query(db.exists().where(Role.name == new_name)).scalar():
raise BadRequest("Name already used")
current_app.config["FG_AUTH_BACKEND"].modify_role(role.name, new_name)
role.name = new_name
db.session.commit() db.session.commit()
@ -62,3 +68,4 @@ def delete(role):
except IntegrityError: except IntegrityError:
logger.debug("IntegrityError: Role might still be in use", exc_info=True) logger.debug("IntegrityError: Role might still be in use", exc_info=True)
raise BadRequest("Role still in use") raise BadRequest("Role still in use")
current_app.config["FG_AUTH_BACKEND"].modify_role(role.name, None)

View File

@ -1,4 +1,5 @@
import pkg_resources import pkg_resources
from typing import Optional
from werkzeug.exceptions import MethodNotAllowed from werkzeug.exceptions import MethodNotAllowed
from flaschengeist.utils.hook import HookCall from flaschengeist.utils.hook import HookCall
@ -67,6 +68,7 @@ class AuthPlugin(Plugin):
def modify_user(self, user, password, new_password=None): 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.
User might have roles not existing on the external database, so you might have to create those.
Args: Args:
user: User object user: User object
@ -79,6 +81,16 @@ class AuthPlugin(Plugin):
""" """
raise NotImplemented raise NotImplemented
def modify_role(self, old_name: str, new_name: Optional[str]):
"""A call to this function indicated that a role was deleted (and has no users)
Might be used if modify_user is implemented.
Args:
old_name: Name of the modified role
new_name: New role name or None if deleted
"""
pass
def create_user(self, user, password): def create_user(self, user, password):
"""If backend is using (writeable) external data, then create a new user on the external database. """If backend is using (writeable) external data, then create a new user on the external database.

View File

@ -6,8 +6,9 @@ from ldap3 import SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, HASHED_SAL
from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError from ldap3.core.exceptions import LDAPPasswordIsMandatoryError, LDAPBindError
from flask import current_app as app from flask import current_app as app
from flask_ldapconn import LDAPConn from flask_ldapconn import LDAPConn
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest, InternalServerError
from flaschengeist import logger
from flaschengeist.plugins import AuthPlugin from flaschengeist.plugins import AuthPlugin
from flaschengeist.models.user import User from flaschengeist.models.user import User
import flaschengeist.controller.userController as userController import flaschengeist.controller.userController as userController
@ -17,28 +18,30 @@ class AuthLDAP(AuthPlugin):
def __init__(self, cfg): def __init__(self, cfg):
super().__init__() super().__init__()
config = {"PORT": 389, "USE_SSL": False} config = {"port": 389, "use_ssl": False}
config.update(cfg) config.update(cfg)
app.config.update( app.config.update(
LDAP_SERVER=config["URL"], LDAP_SERVER=config["host"],
LDAP_PORT=config["PORT"], LDAP_PORT=config["port"],
LDAP_BINDDN=config["BINDDN"], LDAP_BINDDN=config["bind_dn"],
LDAP_USE_TLS=False, LDAP_USE_TLS=False,
LDAP_USE_SSL=config["USE_SSL"], LDAP_USE_SSL=config["use_ssl"],
LDAP_TLS_VERSION=ssl.PROTOCOL_TLSv1_2, LDAP_TLS_VERSION=ssl.PROTOCOL_TLSv1_2,
LDAP_REQUIRE_CERT=ssl.CERT_NONE, LDAP_REQUIRE_CERT=ssl.CERT_NONE,
FORCE_ATTRIBUTE_VALUE_AS_LIST=True, FORCE_ATTRIBUTE_VALUE_AS_LIST=True,
) )
if "SECRET" in config: if "SECRET" in config:
app.config["LDAP_SECRET"] = (config["SECRET"],) app.config["LDAP_SECRET"] = (config["secret"],)
self.ldap = LDAPConn(app) self.ldap = LDAPConn(app)
self.dn = config["BASEDN"] self.dn = config["base_dn"]
self.gidNumber = config['gidNumber'] self.default_gid = config['default_gid']
# TODO: might not be set if modify is called # TODO: might not be set if modify is called
if "ADMIN_DN" in config: if "admin_dn" in config:
self.admin_dn = config["ADMIN_DN"] self.admin_dn = config["admin_dn"]
self.admin_secret = config["ADMIN_SECRET"] self.admin_secret = config["admin_secret"]
else:
self.admin_dn = None
def login(self, user, password): def login(self, user, password):
if not user: if not user:
@ -64,6 +67,10 @@ class AuthLDAP(AuthPlugin):
userController.set_roles(user, self._get_groups(user.userid), create=True) userController.set_roles(user, self._get_groups(user.userid), create=True)
def create_user(self, user, password): def create_user(self, user, password):
if self.admin_dn is None:
logger.error("admin_dn missing in ldap config!")
raise InternalServerError
try: try:
ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret) ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret)
self.ldap.connection.search( self.ldap.connection.search(
@ -76,7 +83,7 @@ class AuthLDAP(AuthPlugin):
attributes = { attributes = {
'sn': user.firstname, 'sn': user.firstname,
'givenName': user.lastname, 'givenName': user.lastname,
'gidNumber': self.gidNumber, 'gidNumber': self.default_gid,
'homeDirectory': f'/home/{user.userid}', 'homeDirectory': f'/home/{user.userid}',
'loginShell': '/bin/bash', 'loginShell': '/bin/bash',
'uid': user.userid, 'uid': user.userid,
@ -85,30 +92,12 @@ class AuthLDAP(AuthPlugin):
} }
ldap_conn.add(dn, object_class, attributes) ldap_conn.add(dn, object_class, attributes)
self.set_roles(user) self._set_roles(user)
except (LDAPPasswordIsMandatoryError, LDAPBindError): except (LDAPPasswordIsMandatoryError, LDAPBindError):
raise BadRequest raise BadRequest
except Exception as e:
pass
def _get_groups(self, uid): def _get_groups(self, uid):
groups = [] groups = []
self.ldap.connection.search(
"ou=user,{}".format(self.dn), "(uid={})".format(uid), SUBTREE, attributes=["gidNumber"]
)
# Maingroup ist uninteressant
#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( self.ldap.connection.search(
"ou=group,{}".format(self.dn), "(memberUID={})".format(uid), SUBTREE, attributes=["cn"] "ou=group,{}".format(self.dn), "(memberUID={})".format(uid), SUBTREE, attributes=["cn"]
) )
@ -117,18 +106,18 @@ class AuthLDAP(AuthPlugin):
groups.append(data["attributes"]["cn"][0]) groups.append(data["attributes"]["cn"][0])
return groups return groups
def set_roles(self, user: User): def _set_roles(self, user: User):
try: try:
ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret) ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret)
self.ldap.connection.search(f"ou=group,{self.dn}", "(cn=*)", SUBTREE, attributes=["cn", "gidNumber"]) self.ldap.connection.search(f"ou=group,{self.dn}", "(cn=*)", SUBTREE, attributes=["cn", "gidNumber"])
ldap_roles = self.ldap.response() ldap_roles = self.ldap.response()
gidNumbers = sorted(ldap_roles, key=lambda i: i['attributes']['gidNumber'], reverse=True) gid_numbers = sorted(ldap_roles, key=lambda i: i['attributes']['gidNumber'], reverse=True)
gidNumber = gidNumbers[0]['attributes']['gidNumber'] + 1 gid_number = gid_numbers[0]['attributes']['gidNumber'] + 1
for user_role in user.roles: for user_role in user.roles:
if user_role not in [role["attributes"]["cn"][0] for role in ldap_roles]: if user_role not in [role["attributes"]["cn"][0] for role in ldap_roles]:
ldap_conn.add(f"cn={user_role},ou=group,{self.dn}", ["posixGroup"], attributes={"gidNumber": gidNumber}) ldap_conn.add(f"cn={user_role},ou=group,{self.dn}", ["posixGroup"], attributes={"gidNumber": gid_number})
ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret) ldap_conn = self.ldap.connect(self.admin_dn, self.admin_secret)
self.ldap.connection.search(f"ou=group,{self.dn}", "(cn=*)", SUBTREE, attributes=["cn", "gidNumber"]) self.ldap.connection.search(f"ou=group,{self.dn}", "(cn=*)", SUBTREE, attributes=["cn", "gidNumber"])
@ -144,8 +133,11 @@ class AuthLDAP(AuthPlugin):
except (LDAPPasswordIsMandatoryError, LDAPBindError): except (LDAPPasswordIsMandatoryError, LDAPBindError):
raise BadRequest raise BadRequest
def modify_user(self, user: User, password=None, new_password=None): def modify_user(self, user: User, password=None, new_password=None):
if self.admin_dn is None:
logger.error("admin_dn missing in ldap config!")
raise InternalServerError
try: try:
dn = user.get_attribute("DN") dn = user.get_attribute("DN")
if password: if password:
@ -166,6 +158,6 @@ class AuthLDAP(AuthPlugin):
salted_password = hashed(HASHED_SALTED_MD5, new_password) salted_password = hashed(HASHED_SALTED_MD5, new_password)
modifier["userPassword"] = [(MODIFY_REPLACE, [salted_password])] modifier["userPassword"] = [(MODIFY_REPLACE, [salted_password])]
ldap_conn.modify(dn, modifier) ldap_conn.modify(dn, modifier)
self.set_roles(user) self._set_roles(user)
except (LDAPPasswordIsMandatoryError, LDAPBindError): except (LDAPPasswordIsMandatoryError, LDAPBindError):
raise BadRequest raise BadRequest

View File

@ -116,10 +116,9 @@ def edit_role(role_id, current_session):
data = request.get_json() data = request.get_json()
if "name" in data: if "name" in data:
role.name = data["name"] roleController.rename(role, data["name"])
if "permissions" in data: if "permissions" in data:
roleController.set_permissions(role, data["permissions"]) roleController.set_permissions(role, data["permissions"])
roleController.update_role(role)
return "", NO_CONTENT return "", NO_CONTENT