chg: [settings] refactor UI settings

otp
terrtia 2024-05-08 14:09:53 +02:00
parent 0b8ff17c5b
commit 8a0c18c575
No known key found for this signature in database
GPG Key ID: 1E1B1F50D84613D0
6 changed files with 249 additions and 181 deletions

View File

@ -174,6 +174,14 @@ def exists_token(token):
# TODO USER LAST LOGIN TIME # TODO USER LAST LOGIN TIME
# TODO Check if logged # TODO Check if logged
# TODO USER: - Creation Date
# - Last Login
# - Last Request
# - Last API Usage
# - Organisation ???
# - Disabled / Lock
class AILUser(UserMixin): class AILUser(UserMixin):
def __init__(self, user_id): def __init__(self, user_id):
self.user_id = user_id self.user_id = user_id
@ -200,8 +208,13 @@ class AILUser(UserMixin):
def exists(self): # TODO CHECK USAGE def exists(self): # TODO CHECK USAGE
return r_serv_db.exists(f'ail:user:metadata:{self.user_id}') return r_serv_db.exists(f'ail:user:metadata:{self.user_id}')
def get_meta(self): def get_meta(self, options=set()): # TODO user creation date
return {'email': self.user_id,} meta = {'id': self.user_id}
if 'api_key' in options: # TODO add option to censor key
meta['api_key'] = self.get_api_key()
if 'role' in options:
meta['role'] = get_user_role(self.user_id)
return meta
## SESSION ## ## SESSION ##
@ -253,6 +266,17 @@ class AILUser(UserMixin):
# create new token # create new token
generate_new_token(self.user_id) generate_new_token(self.user_id)
## TOKEN ##
def get_api_key(self):
return get_user_token(self.user_id)
def new_api_key(self):
_delete_user_token(self.user_id)
new_api_key = gen_token()
_set_user_token(self.user_id, new_api_key)
return new_api_key
## ROLE ## ## ROLE ##
def is_in_role(self, role): # TODO Get role via user alternative ID def is_in_role(self, role): # TODO Get role via user alternative ID
@ -266,14 +290,45 @@ class AILUser(UserMixin):
def get_role(self): def get_role(self):
return r_serv_db.hget(f'ail:user:metadata:{self.user_id}', 'role') return r_serv_db.hget(f'ail:user:metadata:{self.user_id}', 'role')
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## ##
def delete(self): # TODO DESTROY SESSION def delete(self):
kill_session_user(self.user_id) kill_session_user(self.user_id)
for role_id in get_all_roles():
r_serv_db.srem(f'ail:users:role:{role_id}', self.user_id)
user_token = self.get_api_key()
if user_token:
r_serv_db.hdel('ail:users:tokens', user_token)
r_serv_db.delete(f'ail:user:metadata:{self.user_id}')
r_serv_db.hdel('ail:users:all', self.user_id)
# def create_user(user_id): # def create_user(user_id):
#### API ####
def api_get_users_meta():
meta = {'users': []}
options = {'api_key', 'role'}
for user_id in get_users():
user = AILUser(user_id)
meta['users'].append(user.get_meta(options=options))
return meta
def api_create_user_api_key(user_id, admin_id): # TODO LOG ADMIN ID
user = AILUser(user_id)
if not user.exists():
return {'status': 'error', 'reason': 'User not found'}, 404
print(admin_id)
return user.new_api_key(), 200
def api_delete_user(user_id, admin_id): # TODO LOG ADMIN ID
user = AILUser(user_id)
if not user.exists():
return {'status': 'error', 'reason': 'User not found'}, 404
print(admin_id)
return user.delete(), 200
######################################################################################################################## ########################################################################################################################
######################################################################################################################## ########################################################################################################################

View File

@ -20,6 +20,7 @@ sys.path.append(os.environ['AIL_BIN'])
# Import Project packages # Import Project packages
################################## ##################################
from lib import ail_updates from lib import ail_updates
from lib import ail_users
from packages import git_status from packages import git_status
# ============ BLUEPRINT ============ # ============ BLUEPRINT ============
@ -40,9 +41,9 @@ def create_json_response(data, status_code):
def settings_page(): def settings_page():
git_metadata = git_status.get_git_metadata() git_metadata = git_status.get_git_metadata()
ail_version = ail_updates.get_ail_version() ail_version = ail_updates.get_ail_version()
admin_level = current_user.is_in_role('admin') acl_admin = current_user.is_in_role('admin')
return render_template("settings_index.html", git_metadata=git_metadata, return render_template("settings_index.html", git_metadata=git_metadata,
ail_version=ail_version, admin_level=admin_level) ail_version=ail_version, acl_admin=acl_admin)
@settings_b.route("/settings/background_update/json", methods=['GET']) @settings_b.route("/settings/background_update/json", methods=['GET'])
@login_required @login_required
@ -54,12 +55,46 @@ def get_background_update_metadata_json():
@login_required @login_required
@login_read_only @login_read_only
def settings_modules(): def settings_modules():
admin_level = current_user.is_in_role('admin') acl_admin = current_user.is_in_role('admin')
return render_template("settings/modules.html", admin_level=admin_level) return render_template("settings/modules.html", acl_admin=acl_admin)
@settings_b.route("/settings/user/profile", methods=['GET'])
@login_required
@login_read_only
def user_profile():
acl_admin = current_user.is_in_role('admin')
@settings_b.route("/settings/new_user_api_key", methods=['GET'])
@login_required
@login_admin
def new_token_user():
user_id = request.args.get('user_id')
admin_id = current_user.get_user_id()
r = ail_users.api_create_user_api_key(user_id, admin_id)
if r[1] != 200:
return create_json_response(r[0], r[1])
else:
return redirect(url_for('settings_b.users_list'))
@settings_b.route("/settings/delete_user", methods=['GET'])
@login_required
@login_admin
def delete_user():
user_id = request.args.get('user_id')
admin_id = current_user.get_user_id()
r = ail_users.api_delete_user(user_id, admin_id)
if r[1] != 200:
return create_json_response(r[0], r[1])
else:
return redirect(url_for('settings_b.users_list'))
@settings_b.route("/settings/users", methods=['GET'])
@login_required
@login_admin
def users_list():
meta = ail_users.api_get_users_meta()
new_user = {}
return render_template("users_list.html", meta=meta, new_user=new_user, acl_admin=True)

View File

@ -61,17 +61,6 @@ def new_token():
Users.generate_new_token(current_user.get_id()) Users.generate_new_token(current_user.get_id())
return redirect(url_for('settings.edit_profile')) return redirect(url_for('settings.edit_profile'))
@settings.route("/settings/new_token_user", methods=['POST'])
@login_required
@login_admin
def new_token_user():
user_id = request.form.get('user_id')
if Users.exists_user(user_id):
Users.generate_new_token(user_id)
return redirect(url_for('settings.users_list'))
@settings.route("/settings/create_user", methods=['GET']) @settings.route("/settings/create_user", methods=['GET'])
@login_required @login_required
@login_admin @login_admin
@ -133,18 +122,18 @@ def create_user_post():
return render_template("create_user.html", all_roles=all_roles, error_mail=True, admin_level=True) return render_template("create_user.html", all_roles=all_roles, error_mail=True, admin_level=True)
@settings.route("/settings/users_list", methods=['GET']) # @settings.route("/settings/users_list", methods=['GET'])
@login_required # @login_required
@login_admin # @login_admin
def users_list(): # def users_list():
all_users = Users.get_users_metadata(Users.get_all_users()) # all_users = Users.get_users_metadata(Users.get_all_users())
new_user = request.args.get('new_user') # new_user = request.args.get('new_user')
new_user_dict = {} # new_user_dict = {}
if new_user: # if new_user:
new_user_dict['email'] = new_user # new_user_dict['email'] = new_user
new_user_dict['edited'] = request.args.get('new_user_edited') # new_user_dict['edited'] = request.args.get('new_user_edited')
new_user_dict['password'] = request.args.get('new_user_password') # new_user_dict['password'] = request.args.get('new_user_password')
return render_template("users_list.html", all_users=all_users, new_user=new_user_dict, admin_level=True) # return render_template("users_list.html", all_users=all_users, new_user=new_user_dict, admin_level=True)
@settings.route("/settings/edit_user", methods=['POST']) @settings.route("/settings/edit_user", methods=['POST'])
@ -155,15 +144,6 @@ def edit_user():
return redirect(url_for('settings.create_user', user_id=user_id)) return redirect(url_for('settings.create_user', user_id=user_id))
@settings.route("/settings/delete_user", methods=['POST'])
@login_required
@login_admin
def delete_user():
user_id = request.form.get('user_id')
Users.delete_user(user_id)
return redirect(url_for('settings.users_list'))
@settings.route("/settings/passivedns", methods=['GET']) @settings.route("/settings/passivedns", methods=['GET'])
@login_required @login_required
@login_read_only @login_read_only

View File

@ -1,139 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Server Management - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap4.min.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'settings/menu_sidebar.html' %}
<div class="col-12 col-lg-10" id="core_content">
{% if new_user %}
<div class="text-center my-3 ">
<div class="card">
<div class="card-header">
{% if new_user['edited']=='True' %}
<h5 class="card-title">User Edited</h5>
{% else %}
<h5 class="card-title">User Created</h5>
{% endif %}
</div>
<div class="card-body">
<p>User: {{new_user['email']}}</p>
<p>Password: {{new_user['password']}}</p>
<a href="{{url_for('settings.users_list')}}" class="btn btn-primary"><i class="fas fa-eye-slash"></i> Hide</a>
</div>
</div>
</div>
{% endif %}
<div class="table-responsive mt-1 table-hover table-borderless table-striped">
<table class="table">
<thead class="thead-dark">
<tr>
<th>Email</th>
<th>Role</th>
<th>Api Key</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tbody_last_crawled">
{% for user in all_users %}
<tr>
<td>{{user['email']}}</td>
<td>{{user['role']}}</td>
<td>
<form action="{{ url_for('settings.new_token_user') }}" id="post_new_token" method=POST>
<span id="censored_key_{{loop.index0}}">
{{user['api_key'][:4]}}*********************************{{user['api_key'][-4:]}}
</span>
<span id="uncensored_key_{{loop.index0}}" style="display: none;">
{{user['api_key']}}
</span>
<input type="hidden" name="user_id" value="{{user['email']}}">
<button class="btn btn-outline-info ml-3 px-1 py-0" type="submit">
<i class="fas fa-random"></i>
</button>
<span class="btn btn-outline-secondary ml-1 px-1 py-0" id="btn_key_{{loop.index0}}" onclick="show_api_key({{loop.index0}})">
<i class="fas fa-eye"></i>
</span>
</form>
</td>
<td>
<div class="d-flex justify-content-start">
<form action="{{ url_for('settings.edit_user') }}" id="post_edit_user" method=POST>
<input type="hidden" name="user_id" value="{{user['email']}}">
<button class="btn btn-outline-primary ml-3 px-1 py-0" type="submit">
<i class="fas fa-pencil-alt"></i>
</button>
</form>
<form action="{{ url_for('settings.delete_user') }}" id="post_delete_user" method=POST>
<input type="hidden" name="user_id" value="{{user['email']}}">
<button class="btn btn-outline-danger ml-3 px-1 py-0" type="submit">
<i class="fas fa-trash-alt"></i>
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<script>
$(document).ready(function(){
$("#nav_users_list").addClass("active");
$("#nav_user_management").removeClass("text-muted");
} );
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
function show_api_key(key_id) {
$('#censored_key_' + key_id).hide();
$('#btn_key_' + key_id).hide();
$('#uncensored_key_' + key_id).show();
}
</script>
</html>

View File

@ -83,7 +83,7 @@
</a> </a>
</li> </li>
</ul> </ul>
{% if admin_level %} {% if acl_admin %}
<h5 class="d-flex text-muted w-100 py-2" id="nav_user_management"> <h5 class="d-flex text-muted w-100 py-2" id="nav_user_management">
<span>User Management</span> <span>User Management</span>
</h5> </h5>
@ -95,7 +95,7 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{url_for('settings.users_list')}}" id="nav_users_list"> <a class="nav-link" href="{{url_for('settings_b.users_list')}}" id="nav_users_list">
<i class="fas fa-users"></i> <i class="fas fa-users"></i>
<span>Users List</span> <span>Users List</span>
</a> </a>

View File

@ -0,0 +1,137 @@
<!DOCTYPE html>
<html>
<head>
<title>Users Settings - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'settings/menu_sidebar.html' %}
<div class="col-12 col-lg-10" id="core_content">
{% if new_user %}
<div class="text-center my-3 ">
<div class="card">
<div class="card-header">
{% if new_user['edited']=='True' %}
<h5 class="card-title">User Edited</h5>
{% else %}
<h5 class="card-title">User Created</h5>
{% endif %}
</div>
<div class="card-body">
<p>User: {{new_user['email']}}</p>
<p>Password: {{new_user['password']}}</p>
<a href="{{url_for('settings.users_list')}}" class="btn btn-primary"><i class="fas fa-eye-slash"></i> Hide</a>
</div>
</div>
</div>
{% endif %}
<h3>AIL Users:</h3>
<table id="tableusers" class="table table-hover table-striped">
<thead class="thead-dark">
<tr>
<th>User</th>
<th>Role</th>
<th>Api Key</th>
<th></th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tbody_last_crawled">
{% for user in meta['users'] %}
<tr>
<td>{{user['id']}}</td>
<td>{{user['role']}}</td>
<td>
<span id="censored_key_{{loop.index0}}">
{{user['api_key'][:4]}}*********************************{{user['api_key'][-4:]}}
</span>
<span id="uncensored_key_{{loop.index0}}" style="display: none;">
{{user['api_key']}}
</span>
</td>
<td>
<a class="btn btn-outline-info ml-3 px-1 py-0" href="{{ url_for('settings_b.new_token_user', user_id=user['id']) }}">
<i class="fas fa-random"></i>
</a>
<span class="btn btn-outline-secondary ml-1 px-1 py-0" id="btn_key_{{loop.index0}}" onclick="show_api_key({{loop.index0}})">
<i class="fas fa-eye"></i>
</span>
</td>
<td>
<div class="d-flex justify-content-start">
<form action="{{ url_for('settings.edit_user') }}" id="post_edit_user" method=POST>
<input type="hidden" name="user_id" value="{{user['id']}}">
<button class="btn btn-outline-primary ml-3 px-1 py-0" type="submit">
<i class="fas fa-pencil-alt"></i>
</button>
</form>
<a class="btn btn-outline-danger ml-3 px-1 py-0" href="{{ url_for('settings_b.delete_user', user_id=user['id']) }}">
<i class="fas fa-trash-alt"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
<script>
$(document).ready(function(){
$("#nav_users_list").addClass("active");
$("#nav_user_management").removeClass("text-muted");
$('#tableusers').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
"iDisplayLength": 10,
"order": [[ 0, "asc" ]]
});
});
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
function show_api_key(key_id) {
$('#censored_key_' + key_id).hide();
$('#btn_key_' + key_id).hide();
$('#uncensored_key_' + key_id).show();
}
</script>
</html>