mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-22 14:17:16 +00:00
chg: [user] refactor user creation and edit + edit user organisation
This commit is contained in:
parent
5c903f9f88
commit
496d19aa19
8 changed files with 125 additions and 42 deletions
|
@ -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}')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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'] %}
|
||||
|
|
Loading…
Reference in a new issue