diff --git a/app/commands.py b/app/commands.py new file mode 100644 index 0000000000000000000000000000000000000000..204a2c874ef13b9b2ff5282384e8e8642d2f3b3e --- /dev/null +++ b/app/commands.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +""" +app.commands +~~~~~~~~~~~~ + +This module defines extra flask commands. + +:copyright: (c) 2018 European Spallation Source ERIC +:license: BSD 2-Clause, see LICENSE for more details. + +""" +import ldap3 +import sqlalchemy as sa +from flask import current_app +from .extensions import db, ldap_manager +from .defaults import defaults +from .models import User +from . import utils + + +def sync_user(connection, user): + """Synchronize the user from the database with information from the LDAP server""" + search_attr = current_app.config.get('LDAP_USER_LOGIN_ATTR') + object_filter = current_app.config.get('LDAP_USER_OBJECT_FILTER') + search_filter = f'(&{object_filter}({search_attr}={user.username}))' + connection.search( + search_base=ldap_manager.full_user_search_dn, + search_filter=search_filter, + search_scope=getattr( + ldap3, current_app.config.get('LDAP_USER_SEARCH_SCOPE')), + attributes=current_app.config.get('LDAP_GET_USER_ATTRIBUTES') + ) + if len(connection.response) == 1: + ldap_user = connection.response[0] + attributes = ldap_user['attributes'] + user.display_name = utils.attribute_to_string(attributes['cn']) + user.email = utils.attribute_to_string(attributes['mail']) + groups = ldap_manager.get_user_groups(dn=ldap_user['dn'], _connection=connection) + user.groups = sorted([utils.attribute_to_string(group['cn']) for group in groups]) + current_app.logger.info(f'{user} updated') + else: + # Clear user's groups + user.groups = [] + # Revoke all user's tokens + for token in user.tokens: + db.session.delete(token) + current_app.logger.info(f'{user} disabled') + return user + + +def register_cli(app): + @app.cli.command() + def initdb(): + """Create the database tables and initialize them with default values""" + db.engine.execute('CREATE EXTENSION IF NOT EXISTS citext') + db.create_all() + for instance in defaults: + db.session.add(instance) + try: + db.session.commit() + except sa.exc.IntegrityError as e: + db.session.rollback() + app.logger.debug(f'{instance} already exists') + + @app.cli.command() + def syncusers(): + """Synchronize all users from the database with information the LDAP server""" + try: + connection = ldap_manager.connection + except ldap3.core.exceptions.LDAPException as e: + current_app.logger.warning(f'Failed to connect to the LDAP server: {e}') + return + for user in User.query.all(): + sync_user(connection, user) + db.session.commit() diff --git a/app/factory.py b/app/factory.py index 79bc8567227c267035a24cd4e428b40586811936..6a05d14dcbd16fab267bd23f62c62b4051561b3f 100644 --- a/app/factory.py +++ b/app/factory.py @@ -24,22 +24,7 @@ from .user.views import bp as user from .api.user import bp as user_api from .api.inventory import bp as inventory_api from .api.network import bp as network_api -from .defaults import defaults - - -def register_cli(app): - @app.cli.command() - def initdb(): - """Create the database tables and initialize them with default values""" - db.engine.execute('CREATE EXTENSION IF NOT EXISTS citext') - db.create_all() - for instance in defaults: - db.session.add(instance) - try: - db.session.commit() - except sa.exc.IntegrityError as e: - db.session.rollback() - app.logger.debug(f'{instance} already exists') +from .commands import register_cli def create_app(config=None): diff --git a/app/models.py b/app/models.py index 4d931f1ca5166cb40035a9121b1aa60ecf6bb309..ed58eedcb497bd64e90742984354a1a14d59ad96 100644 --- a/app/models.py +++ b/app/models.py @@ -89,7 +89,7 @@ def save_user(dn, username, data, memberships): display_name=utils.attribute_to_string(data['cn']), email=utils.attribute_to_string(data['mail'])) # Always update the user groups to keep them up-to-date - user.groups = [utils.attribute_to_string(group['cn']) for group in memberships] + user.groups = sorted([utils.attribute_to_string(group['cn']) for group in memberships]) db.session.add(user) db.session.commit() return user