chg: [user] refactor user creation and edit + edit user organisation

This commit is contained in:
terrtia 2024-09-03 15:49:09 +02:00
parent 5c903f9f88
commit 496d19aa19
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
8 changed files with 125 additions and 42 deletions

View file

@ -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}')

View file

@ -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,8 +350,11 @@ 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)
def edit_user(user_id, password_hash, chg_passwd=False, otp=True):
# 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)
@ -355,7 +364,16 @@ def edit_user(user_id, password_hash, chg_passwd=False, otp=True):
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 edit_otp:
if otp or is_2fa_enabled():
enable_user_2fa(user_id)
else:
@ -366,9 +384,7 @@ def edit_user(user_id, password_hash, chg_passwd=False, otp=True):
# 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)
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)

View file

@ -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'):

View file

@ -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"

View file

@ -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:

View file

@ -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')

View file

@ -30,7 +30,7 @@
<div class="text-center my-3 ">
<div class="card">
<div class="card-header">
{% if new_user['edited']=='True' %}
{% if new_user['edited'] %}
<h5 class="card-title">User Edited</h5>
{% else %}
<h5 class="card-title">User Created</h5>
@ -59,6 +59,17 @@
<div class="invalid-feedback">Please provide a valid email address</div>
{% endif %}
<label class="mt-3" for="role_selector">Organisation</label>
<select class="custom-select" id="role_selector" name="user_organisation">
{% for org in orgs %}
{% if meta['org'] == org['uuid'] %}
<option value="{{ org['uuid'] }}" selected>{{ org['uuid'] }} - {{ org['name'] }}</option>
{% else %}
<option value="{{ org['uuid'] }}">{{ org['uuid'] }} - {{ org['name'] }}</option>
{% endif %}
{% endfor %}
</select>
<label class="mt-3" for="role_selector">User Role</label>
<select class="custom-select" id="role_selector" name="user_role">
{% for role in all_roles %}

View file

@ -32,6 +32,7 @@
<thead class="thead-dark">
<tr>
<th>User</th>
<th>Org</th>
<th>Last Edit</th>
<th>Last Login</th>
<th>Last Seen</th>
@ -46,6 +47,10 @@
{% for user in meta['users'] %}
<tr>
<td>{{user['id']}}</td>
<td>
{{ user['org_name'] }}<br>
{{user['org']}}
</td>
<td>{{user['last_edit']}}</td>
<td>
{% if user['last_login'] %}