[Plugin] Added plugin function when roles are modified
LDAP: Use same config style as the rest.
This commit is contained in:
parent
96765ee932
commit
cbcd5b39a3
|
@ -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
|
||||||
|
|
||||||
############################
|
############################
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue