From 496d19aa1948b14698c2e9f65b54fb4ceffce307 Mon Sep 17 00:00:00 2001 From: terrtia Date: Tue, 3 Sep 2024 15:49:09 +0200 Subject: [PATCH] chg: [user] refactor user creation and edit + edit user organisation --- bin/lib/ail_orgs.py | 18 +++++ bin/lib/ail_users.py | 88 ++++++++++++++------- update/v5.0/DB_KVROCKS_MIGRATION.py | 2 +- var/www/blueprints/root.py | 12 ++- var/www/blueprints/settings_b.py | 13 ++- var/www/create_default_user.py | 16 +++- var/www/templates/settings/create_user.html | 13 ++- var/www/templates/settings/users_list.html | 5 ++ 8 files changed, 125 insertions(+), 42 deletions(-) diff --git a/bin/lib/ail_orgs.py b/bin/lib/ail_orgs.py index 0c6c1f9a..dcc068d5 100755 --- a/bin/lib/ail_orgs.py +++ b/bin/lib/ail_orgs.py @@ -71,6 +71,20 @@ def get_orgs(): def is_user_in_org(org_uuid, user_id): return r_serv_db.sadd(f'ail:org:{org_uuid}:users', user_id) +def get_orgs_selector(): + orgs = [] + for org_uuid in get_orgs(): + org = Organisation(org_uuid) + name = org.get_name() + orgs.append({'uuid': org_uuid, 'name': name}) + return orgs + +def create_default_org(): + # org = Organisation(generate_uuid()) + name = 'Default AIL Organisation' + description = 'Default AIL Organisation' + return create_org(name, description) + #### ORGANISATION #### class Organisation: @@ -138,6 +152,9 @@ class Organisation: meta['date_created'] = self._get_field('date_created') return meta + def is_user(self, user_id): + return r_serv_db.sismember(f'ail:org:{self.uuid}:users', user_id) + def add_user(self, user_id): r_serv_db.sadd(f'ail:org:{self.uuid}:users', user_id) r_serv_db.hset(f'ail:user:metadata:{user_id}', 'org', self.uuid) @@ -189,6 +206,7 @@ def create_org(name, description, uuid=None, nationality=None, sector=None, org_ org = Organisation(uuid) org.create(name, description, nationality=nationality, sector=sector, org_type=org_type, logo=logo) + return org def get_org_objs_by_type(org_uuid, obj_type): return r_serv_db.smembers(f'org:{org_uuid}:{obj_type}') diff --git a/bin/lib/ail_users.py b/bin/lib/ail_users.py index ced71619..b954adc6 100755 --- a/bin/lib/ail_users.py +++ b/bin/lib/ail_users.py @@ -104,6 +104,12 @@ def hashing_password(password): def get_user_passwd_hash(user_id): return r_serv_db.hget('ail:users:all', user_id) +def remove_default_password(): + # Remove default user password file + default_passwd_file = os.path.join(os.environ['AIL_HOME'], 'DEFAULT_PASSWORD') + if os.path.isfile(default_passwd_file): + os.remove(default_passwd_file) + ## --PASSWORDS-- ## def check_email(email): @@ -304,7 +310,7 @@ def disable_user(user_id): def enable_user(user_id): r_serv_db.srem(f'ail:users:disabled', user_id) -def create_user(user_id, password=None, admin_id=None, chg_passwd=True, role=None, otp=False): +def create_user(user_id, password=None, admin_id=None, chg_passwd=True, org_uuid=None, role=None, otp=False): # # TODO: check password strength if password: new_password = password @@ -312,14 +318,8 @@ def create_user(user_id, password=None, admin_id=None, chg_passwd=True, role=Non new_password = gen_password() password_hash = hashing_password(new_password) - # EDIT - if exists_user(user_id): - if password or chg_passwd: - edit_user(user_id, password_hash, chg_passwd=chg_passwd, otp=otp) - if role: - edit_user_role(user_id, role) # CREATE USER - elif admin_id: + if admin_id: r_serv_db.hset(f'ail:user:metadata:{user_id}', 'creator', admin_id) date = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') r_serv_db.hset(f'ail:user:metadata:{user_id}', 'created_at', date) @@ -334,6 +334,12 @@ def create_user(user_id, password=None, admin_id=None, chg_passwd=True, role=Non r_serv_db.sadd(f'ail:users:role:{role_to_add}', user_id) r_serv_db.hset(f'ail:user:metadata:{user_id}', 'role', role) + # ORG + org = ail_orgs.Organisation(org_uuid) + if not org.exists(): + raise Exception('Organisation does not exist') + org.add_user(user_id) + r_serv_db.hset('ail:users:all', user_id, password_hash) if chg_passwd: r_serv_db.hset(f'ail:user:metadata:{user_id}', 'change_passwd', 'True') @@ -344,31 +350,41 @@ def create_user(user_id, password=None, admin_id=None, chg_passwd=True, role=Non if otp or is_2fa_enabled(): enable_user_2fa(user_id) +# TODO edit_org +# TODO LOG +def edit_user(admin_id, user_id, password=None, chg_passwd=False, org_uuid=None, edit_otp=False, otp=True): + if password: + password_hash = hashing_password(password) + if chg_passwd: + r_serv_db.hset(f'ail:user:metadata:{user_id}', 'change_passwd', 'True') + r_serv_db.hset('ail:users:all', user_id, password_hash) -def edit_user(user_id, password_hash, chg_passwd=False, otp=True): - if chg_passwd: - r_serv_db.hset(f'ail:user:metadata:{user_id}', 'change_passwd', 'True') - r_serv_db.hset('ail:users:all', user_id, password_hash) + # create new token + generate_new_token(user_id) + else: + r_serv_db.hdel(f'ail:user:metadata:{user_id}', 'change_passwd') - # create new token - generate_new_token(user_id) - else: - r_serv_db.hdel(f'ail:user:metadata:{user_id}', 'change_passwd') + if org_uuid: + org = ail_orgs.Organisation(org_uuid) + if org.exists(): + if not org.is_user(user_id): + current_org = ail_orgs.Organisation(get_user_org(user_id)) + current_org.remove_user(user_id) + org.add_user(user_id) # 2FA OTP - if otp or is_2fa_enabled(): - enable_user_2fa(user_id) - else: - disable_user_2fa(user_id) + if edit_otp: + if otp or is_2fa_enabled(): + enable_user_2fa(user_id) + else: + disable_user_2fa(user_id) date = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') r_serv_db.hset(f'ail:user:metadata:{user_id}', 'last_edit', date) # Remove default user password file if user_id == 'admin@admin.test': - default_passwd_file = os.path.join(os.environ['AIL_HOME'], 'DEFAULT_PASSWORD') - if os.path.isfile(default_passwd_file): - os.remove(default_passwd_file) + remove_default_password() ## --USER-- ## @@ -445,6 +461,8 @@ class AILUser(UserMixin): meta['is_logged'] = is_user_logged(self.user_id) if 'org' in options: meta['org'] = self.get_org() + if 'org_name' in options and meta['org']: + meta['org_name'] = ail_orgs.Organisation(self.get_org()).get_name() return meta ## SESSION ## @@ -564,14 +582,14 @@ class AILUser(UserMixin): def api_get_users_meta(): meta = {'users': []} - options = {'api_key', 'creator', 'created_at', 'is_logged', 'last_edit', 'last_login', 'last_seen', 'last_seen_api', 'role', '2fa', 'otp_setup'} + options = {'api_key', 'creator', 'created_at', 'is_logged', 'last_edit', 'last_login', 'last_seen', 'last_seen_api', 'org', 'org_name', 'role', '2fa', 'otp_setup'} for user_id in get_users(): user = AILUser(user_id) meta['users'].append(user.get_meta(options=options)) return meta def api_get_user_profile(user_id): - options = {'api_key', 'role', '2fa'} + options = {'api_key', 'role', '2fa', 'org'} user = AILUser(user_id) if not user.exists(): return {'status': 'error', 'reason': 'User not found'}, 404 @@ -663,9 +681,23 @@ def api_create_user_api_key(user_id, admin_id, ip_address): access_logger.info(f'New api key for user {user_id}', extra={'user_id': admin_id, 'ip_address': ip_address}) return user.new_api_key(), 200 -def api_create_user(admin_id, ip_address, user_id, password, role, otp): - create_user(user_id, password=password, admin_id=admin_id, role=role, otp=otp) - access_logger.info(f'Create user {user_id}', extra={'user_id': admin_id, 'ip_address': ip_address}) +def api_create_user(admin_id, ip_address, user_id, password, org_uuid, role, otp): + user = AILUser(user_id) + if not user.exists(): + create_user(user_id, password=password, admin_id=admin_id, org_uuid=org_uuid, role=role, otp=otp) + access_logger.info(f'Create user {user_id}', extra={'user_id': admin_id, 'ip_address': ip_address}) + # Edit + else: + edit_user(admin_id, user_id, password, chg_passwd=True, org_uuid=org_uuid, edit_otp=True, otp=otp) + access_logger.info(f'Edit user {user_id}', extra={'user_id': admin_id, 'ip_address': ip_address}) + +def api_change_user_self_password(user_id, password): + if not check_password_strength(password): + return {'status': 'error', 'reason': 'Invalid Password'}, 400 + password_hash = hashing_password(password) + user = AILUser(user_id) + user.edit_password(password_hash, chg_passwd=False) + return user_id, 200 def api_delete_user(user_id, admin_id, ip_address): user = AILUser(user_id) diff --git a/update/v5.0/DB_KVROCKS_MIGRATION.py b/update/v5.0/DB_KVROCKS_MIGRATION.py index d8d43a48..2d2f0a88 100755 --- a/update/v5.0/DB_KVROCKS_MIGRATION.py +++ b/update/v5.0/DB_KVROCKS_MIGRATION.py @@ -123,7 +123,7 @@ def user_migration(): if not chg_passwd: chg_passwd = None - ail_users.create_user(user_id, password=password_hash, chg_passwd=chg_passwd, role=role) + ail_users.create_user(user_id, password=password_hash, chg_passwd=chg_passwd, org_uuid=get_ail_uuid(), role=role) print(user_id, token) for invite_row in r_crawler.smembers('telegram:invite_code'): diff --git a/var/www/blueprints/root.py b/var/www/blueprints/root.py index f48a8483..65309f38 100644 --- a/var/www/blueprints/root.py +++ b/var/www/blueprints/root.py @@ -5,6 +5,7 @@ Blueprint Flask: root endpoints: login, ... """ +import json import os import sys import time @@ -13,6 +14,8 @@ from flask import render_template, jsonify, request, Blueprint, redirect, url_fo from flask import session from flask_login import login_required, current_user, login_user, logout_user +from blueprints.settings_b import create_json_response + sys.path.append('modules') # Import Role_Manager @@ -22,7 +25,7 @@ sys.path.append(os.environ['AIL_BIN']) ################################## # Import Project packages ################################## -from lib.ail_users import AILUser, kill_sessions, create_user, check_password_strength, check_user_role_integrity +from lib.ail_users import AILUser, kill_sessions, api_change_user_self_password, check_password_strength, check_user_role_integrity from lib.ConfigLoader import ConfigLoader from lib import ail_logger @@ -48,7 +51,6 @@ root = Blueprint('root', __name__, template_folder='templates') # ============ FUNCTIONS ============ - # ============= ROUTES ============== @root.route('/login', methods=['POST', 'GET']) # TODO LOG BRUTEFORCE ATTEMPT def login(): @@ -273,13 +275,15 @@ def change_password(): if password1 == password2: if check_password_strength(password1): user_id = current_user.get_user_id() - create_user(user_id, password=password1, chg_passwd=False) # TODO RENAME ME + res = api_change_user_self_password(user_id, password1) + if res != 200: + return create_json_response(res[0], res[1]) access_logger.info(f'Password change', extra={'user_id': user_id, 'ip_address': request.remote_addr}) # update Note # dashboard return redirect(url_for('dashboard.index', update_note=True)) else: - error = 'Incorrect password' + error = 'Invalid password' return render_template("change_password.html", error=error) else: error = "Passwords don't match" diff --git a/var/www/blueprints/settings_b.py b/var/www/blueprints/settings_b.py index 9b9cf445..a9775ce4 100644 --- a/var/www/blueprints/settings_b.py +++ b/var/www/blueprints/settings_b.py @@ -220,7 +220,8 @@ def create_user(): return create_json_response(r[0], r[1]) meta = r[0] all_roles = ail_users.get_all_roles() - return render_template("create_user.html", all_roles=all_roles, meta=meta, + orgs = ail_orgs.get_orgs_selector() + return render_template("create_user.html", all_roles=all_roles, orgs=orgs, meta=meta, error=error, error_mail=error_mail, acl_admin=True) @@ -240,6 +241,7 @@ def create_user_post(): admin_id = current_user.get_user_id() email = request.form.get('username') + org_uuid = request.form.get('user_organisation') role = request.form.get('user_role') password1 = request.form.get('password1') password2 = request.form.get('password2') @@ -266,14 +268,17 @@ def create_user_post(): else: password = ail_users.gen_password() - if current_user.is_in_role('admin'): + if current_user.is_admin(): str_password = password if ail_users.exists_user(email): if not password1 and not password2: password = None str_password = 'Password not changed' - ail_users.api_create_user(admin_id, request.remote_addr, email, password, role, enable_2_fa) - new_user = {'email': email, 'password': str_password, 'otp': enable_2_fa} + edit = True + else: + edit = False + ail_users.api_create_user(admin_id, request.remote_addr, email, password, org_uuid, role, enable_2_fa) + new_user = {'email': email, 'password': str_password, 'org': org_uuid, 'otp': enable_2_fa, 'edited': edit} return render_template("create_user.html", new_user=new_user, meta={}, all_roles=all_roles, acl_admin=True) else: diff --git a/var/www/create_default_user.py b/var/www/create_default_user.py index db6a1909..81d0903b 100755 --- a/var/www/create_default_user.py +++ b/var/www/create_default_user.py @@ -8,17 +8,25 @@ sys.path.append(os.environ['AIL_BIN']) ################################## # Import Project packages ################################## +from lib import ail_orgs from lib import ail_users if __name__ == "__main__": - # create role_list - ail_users._create_roles_list() - user_id = 'admin@admin.test' password = ail_users.gen_password() - ail_users.create_user(user_id, password=password, admin_id='admin@admin.test', role='admin') + # create role_list + ail_users._create_roles_list() + + if not ail_users.exists_user(user_id): + # Create Default Org + org = ail_orgs.create_default_org() + ail_users.create_user(user_id, password=password, admin_id='admin@admin.test', org_uuid=org.get_uuid(), role='admin') + # EDIT Password + else: + ail_users.edit_user('admin@admin.test', user_id, password=password, chg_passwd=True) + token = ail_users.get_default_admin_token() default_passwd_file = os.path.join(os.environ['AIL_HOME'], 'DEFAULT_PASSWORD') diff --git a/var/www/templates/settings/create_user.html b/var/www/templates/settings/create_user.html index 35e7b8df..96ceea7b 100644 --- a/var/www/templates/settings/create_user.html +++ b/var/www/templates/settings/create_user.html @@ -30,7 +30,7 @@
- {% if new_user['edited']=='True' %} + {% if new_user['edited'] %}
User Edited
{% else %}
User Created
@@ -59,6 +59,17 @@
Please provide a valid email address
{% endif %} + + +