summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJavier Sánchez Parra <jsanchez@soleta.eu>2022-04-26 17:16:52 +0200
committerJavier Sánchez Parra <jsanchez@soleta.eu>2022-04-27 17:27:50 +0200
commit661254b76edd51c36090edd0f898bdca16f23277 (patch)
tree7882d0097515358435206a795d4f8b815965f529
parentd8bac16a980634f182e9c753a01e114c3a482af0 (diff)
Add 'Add user' to Users section
Creates "Add user" form with the following inputs: username, password, password confirmation, role (administrator or regular), allowed scopes.
-rw-r--r--ogcp/forms/auth.py31
-rw-r--r--ogcp/static/js/ogcp.js22
-rw-r--r--ogcp/templates/auth/add_user.html26
-rw-r--r--ogcp/templates/users.html2
-rw-r--r--ogcp/views.py71
5 files changed, 150 insertions, 2 deletions
diff --git a/ogcp/forms/auth.py b/ogcp/forms/auth.py
index c02ecc7..b10d8fe 100644
--- a/ogcp/forms/auth.py
+++ b/ogcp/forms/auth.py
@@ -7,7 +7,7 @@
from wtforms import (
Form, SubmitField, HiddenField, SelectField, BooleanField, IntegerField,
- StringField, RadioField, PasswordField
+ StringField, RadioField, PasswordField, SelectMultipleField
)
from wtforms.validators import InputRequired
from flask_wtf import FlaskForm
@@ -28,3 +28,32 @@ class LoginForm(FlaskForm):
submit_btn = SubmitField(
label=_l('Login')
)
+
+
+class UserForm(FlaskForm):
+ username = StringField(
+ label=_l('Username'),
+ validators=[InputRequired()]
+ )
+ pwd = PasswordField(
+ label=_l('Password'),
+ )
+ pwd_hash = HiddenField(
+ validators=[InputRequired()]
+ )
+ pwd_confirm = PasswordField(
+ label=_l('Repeat password'),
+ )
+ pwd_hash_confirm = HiddenField(
+ validators=[InputRequired()]
+ )
+ admin = BooleanField(
+ label=_l('Administrator'),
+ )
+ scopes = SelectMultipleField(
+ label=_l('Allowed scopes'),
+ description=_l('Leave this empty to give full permissions'),
+ )
+ submit_btn = SubmitField(
+ label=_l('Submit')
+ )
diff --git a/ogcp/static/js/ogcp.js b/ogcp/static/js/ogcp.js
index 6a7e625..4605e5d 100644
--- a/ogcp/static/js/ogcp.js
+++ b/ogcp/static/js/ogcp.js
@@ -231,3 +231,25 @@ function digestLoginPassword() {
$(this).submit()
});
}
+
+function digestUserFormPassword() {
+ const loginForm = $('#user-form')
+ loginForm.one('submit', async function (event) {
+ event.preventDefault()
+
+ const pwdInput = $('#pwd');
+ const pwdHashInput = $('#pwd_hash');
+ const pwdStr = pwdInput.val();
+ const pwdStrHash = await digestMessage(pwdStr);
+
+ const pwdConfirmInput = $('#pwd_confirm');
+ const pwdHashConfirmInput = $('#pwd_hash_confirm');
+ const pwdConfirmStr = pwdConfirmInput.val();
+ const pwdConfirmStrHash = await digestMessage(pwdConfirmStr);
+
+ pwdInput.prop( "disabled", true );
+ pwdHashInput.val(pwdStrHash);
+ pwdHashConfirmInput.val(pwdConfirmStrHash);
+ $(this).submit()
+ });
+}
diff --git a/ogcp/templates/auth/add_user.html b/ogcp/templates/auth/add_user.html
new file mode 100644
index 0000000..af44caa
--- /dev/null
+++ b/ogcp/templates/auth/add_user.html
@@ -0,0 +1,26 @@
+{% extends 'users.html' %}
+{% import "bootstrap/wtf.html" as wtf %}
+
+{% set sidebar_state = 'disabled' %}
+{% set btn_back = true %}
+
+{% block nav_user_add %}active{% endblock %}
+{% block content %}
+
+<h1 class="m-5">{{_('Add a user')}}</h1>
+
+{{ wtf.quick_form(form,
+ action=url_for('user_add_post'),
+ method='post',
+ button_map={'submit_btn':'primary'},
+ id='user-form') }}
+
+<script>
+ document.addEventListener('readystatechange', () => {
+ if (document.readyState === 'complete') {
+ digestUserFormPassword()
+ }
+ });
+</script>
+
+{% endblock %}
diff --git a/ogcp/templates/users.html b/ogcp/templates/users.html
index c97f113..c14aae6 100644
--- a/ogcp/templates/users.html
+++ b/ogcp/templates/users.html
@@ -24,6 +24,8 @@
{% endblock %}
{% block commands %}
+ <input class="btn btn-light {% block nav_user_add %}{% endblock %}" type="submit" value="{{ _('Add user') }}"
+ form="usersForm" formaction="{{ url_for('user_add_get') }}" formmethod="get">
{% if btn_back %}
<button class="btn btn-danger ml-3" type="button" id="backButton" onclick="history.back()">
{{ _("Back") }}
diff --git a/ogcp/views.py b/ogcp/views.py
index c10d2c0..48486c7 100644
--- a/ogcp/views.py
+++ b/ogcp/views.py
@@ -23,13 +23,15 @@ from flask_login import (
from pathlib import Path
from ogcp.models import User
-from ogcp.forms.auth import LoginForm
+from ogcp.forms.auth import LoginForm, UserForm
from ogcp.og_server import OGServer
from flask_babel import lazy_gettext as _l
from flask_babel import _
from ogcp import app
import requests
import datetime
+import json
+import os
import re
FS_CODES = {
@@ -1194,6 +1196,73 @@ def users():
return render_template('users.html', users=users)
+def get_available_scopes():
+ resp = g.server.get('/scopes')
+ centers = parse_scopes_from_tree(resp.json(), 'center')
+ centers = [(center['name'], center['name']) for center in centers]
+ rooms = parse_scopes_from_tree(resp.json(), 'room')
+ rooms = [(room['name'], room['name']) for room in rooms]
+ return centers + rooms
+
+
+def save_user(form):
+ username = form.username.data
+
+ pwd_hash = form.pwd_hash.data
+ pwd_hash_confirm = form.pwd_hash_confirm.data
+ if not pwd_hash == pwd_hash_confirm:
+ flash(_('Passwords do not match'), category='error')
+ return redirect(url_for('users'))
+
+ admin = form.admin.data
+ scopes = form.scopes.data
+
+ user = {
+ 'USER': username,
+ 'PASS': pwd_hash,
+ 'ADMIN': admin,
+ 'SCOPES': scopes,
+ }
+
+ filename = os.path.join(app.root_path, 'cfg', 'ogcp.json')
+ with open(filename, 'r+') as file:
+ config = json.load(file)
+
+ config['USERS'].append(user)
+
+ file.seek(0)
+ json.dump(config, file, indent='\t')
+ file.truncate()
+
+ app.config['USERS'].append(user)
+
+ return redirect(url_for('users'))
+
+
+@app.route('/user/add', methods=['GET'])
+@login_required
+def user_add_get():
+ form = UserForm()
+ form.scopes.choices = get_available_scopes()
+ return render_template('auth/add_user.html', form=form)
+
+
+@app.route('/user/add', methods=['POST'])
+@login_required
+def user_add_post():
+ form = UserForm(request.form)
+ form.scopes.choices = get_available_scopes()
+ if not form.validate():
+ flash(form.errors, category='error')
+ return redirect(url_for('users'))
+
+ if get_user(form.username.data):
+ flash(_('This username already exists'), category='error')
+ return redirect(url_for('users'))
+
+ return save_user(form)
+
+
@app.route('/action/image/info', methods=['GET'])
@login_required
def action_image_info():