Compare commits
2 Commits
c981161cd7
...
1e87406fdb
Author | SHA1 | Date | |
---|---|---|---|
1e87406fdb | |||
e79713e094 |
|
@ -3,13 +3,14 @@ import argparse
|
||||||
|
|
||||||
from imaginaerraum_door_admin.webapp import create_application
|
from imaginaerraum_door_admin.webapp import create_application
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--token_file", default="/etc/door_tokens", help="path to the file with door tokens and users")
|
parser.add_argument("--token_file", default="/etc/door_tokens", help="path to the file with door tokens and users")
|
||||||
parser.add_argument("--nfc_socket", default="/tmp/nfc.sock", help="socket for handling NFC reader commands")
|
parser.add_argument("--nfc_socket", default="/tmp/nfc.sock", help="socket for handling NFC reader commands")
|
||||||
parser.add_argument("--template_folder", default="templates", help="path to Flask templates folder")
|
parser.add_argument("--template_folder", default="templates", help="path to Flask templates folder")
|
||||||
parser.add_argument("--static_folder", default="static", help="path to Flask static folder")
|
parser.add_argument("--static_folder", default="static", help="path to Flask static folder")
|
||||||
parser.add_argument("--admin_file", help="Path to file for creating initial admin users")
|
parser.add_argument("--admin_file", help="Path to file for creating initial admin users")
|
||||||
|
parser.add_argument("--ldap_url", default="ldaps://do.imaginaerraum.de",
|
||||||
|
help="URL for LDAP server for alternative user authorization")
|
||||||
parser.add_argument("--mqtt_host", default="10.10.21.2", help="IP address of MQTT broker")
|
parser.add_argument("--mqtt_host", default="10.10.21.2", help="IP address of MQTT broker")
|
||||||
parser.add_argument("--port", default=80, help="Port for running the Flask server")
|
parser.add_argument("--port", default=80, help="Port for running the Flask server")
|
||||||
parser.add_argument("--mail_server", default="smtp.googlemail.com", help="email server for sending security messages")
|
parser.add_argument("--mail_server", default="smtp.googlemail.com", help="email server for sending security messages")
|
||||||
|
|
27
imaginaerraum_door_admin/templates/delete.html
Normal file
27
imaginaerraum_door_admin/templates/delete.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block header %}
|
||||||
|
{% block title %}<h1>Token löschen</h1>{% endblock %}
|
||||||
|
<script src="../static/jquery-3.6.0.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div>
|
||||||
|
Achtung, der Token von NutzerIn '{{ token['name'] }}' wird gelöscht.
|
||||||
|
Bitte zur Bestätigung den Nutzernamen eingeben:
|
||||||
|
<form method="POST">
|
||||||
|
<table>
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ form.name.label }}</td>
|
||||||
|
<td>{{ form.name(size=20) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<input type="submit" value="Bestätigen">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -22,7 +22,7 @@
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('edit_token', token=t) }}"><img src="static/edit.png" title="Editieren" alt="Edit"></a>
|
<a href="{{ url_for('edit_token', token=t) }}"><img src="static/edit.png" title="Editieren" alt="Edit"></a>
|
||||||
<a href="{{ url_for('deactivate_token', token=t) }}"><img src="static/stop.png" title="Deaktivieren" alt="Deactivate"></a>
|
<a href="{{ url_for('deactivate_token', token=t) }}"><img src="static/stop.png" title="Deaktivieren" alt="Deactivate"></a>
|
||||||
<img src="static/delete.png" title="Löschen" alt="Delete" onclick="confirmDelete('{{ t }}')">
|
<a href="{{ url_for('delete_token', token=t) }}"><img src="static/delete.png" title="Löschen" alt="Delete"></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -39,20 +39,4 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<script>
|
|
||||||
function confirmDelete(t) {
|
|
||||||
debugger
|
|
||||||
if (confirm('Token wirklich löschen?')) {
|
|
||||||
console.log('confirmed');
|
|
||||||
console.log(t);
|
|
||||||
$.post('{{ url_for('delete_token') }}', {token: t},
|
|
||||||
function (data) {
|
|
||||||
if (data === 'success') {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,16 +1,18 @@
|
||||||
import os
|
import os
|
||||||
from flask import Flask, render_template, request, flash, redirect, session
|
from flask import Flask, render_template, request, flash, redirect, session, url_for
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms.fields.html5 import DateField, EmailField
|
from wtforms.fields.html5 import DateField, EmailField
|
||||||
from wtforms.fields import StringField, BooleanField
|
from wtforms.fields import StringField, BooleanField
|
||||||
from wtforms.validators import DataRequired, ValidationError
|
from wtforms.validators import DataRequired, ValidationError, EqualTo
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_security import Security, SQLAlchemyUserDatastore, auth_required, hash_password, uia_email_mapper
|
from flask_security import Security, SQLAlchemyUserDatastore, auth_required, hash_password, uia_email_mapper
|
||||||
from flask_security.models import fsqla_v2 as fsqla
|
from flask_security.models import fsqla_v2 as fsqla
|
||||||
from flask_security.forms import LoginForm, Required, PasswordField
|
from flask_security.forms import LoginForm, Required, PasswordField
|
||||||
from flask_mail import Mail
|
from flask_mail import Mail
|
||||||
|
from flask_security.utils import find_user
|
||||||
from email_validator import validate_email
|
from email_validator import validate_email
|
||||||
import bleach
|
import bleach
|
||||||
|
import ldap3
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from .door_handle import DoorHandle
|
from .door_handle import DoorHandle
|
||||||
|
@ -36,6 +38,9 @@ class TokenForm(FlaskForm):
|
||||||
active = BooleanField('Aktiv?')
|
active = BooleanField('Aktiv?')
|
||||||
dsgvo = BooleanField('Einwilligung Nutzungsbedingungen erfragt?', validators=[DataRequired()])
|
dsgvo = BooleanField('Einwilligung Nutzungsbedingungen erfragt?', validators=[DataRequired()])
|
||||||
|
|
||||||
|
class TokenDeleteForm(FlaskForm):
|
||||||
|
name = StringField('Name', validators=[DataRequired(), EqualTo('name_confirm', 'Name stimmt nicht überein')])
|
||||||
|
name_confirm = StringField('Name confirm')
|
||||||
|
|
||||||
def uia_username_mapper(identity):
|
def uia_username_mapper(identity):
|
||||||
# we allow pretty much anything - but we bleach it.
|
# we allow pretty much anything - but we bleach it.
|
||||||
|
@ -93,10 +98,42 @@ def create_application(config):
|
||||||
class User(db.Model, fsqla.FsUserMixin):
|
class User(db.Model, fsqla.FsUserMixin):
|
||||||
username = db.Column(db.String(255))
|
username = db.Column(db.String(255))
|
||||||
|
|
||||||
|
# LDAP
|
||||||
|
ldap_server = ldap3.Server(config.ldap_url)
|
||||||
|
|
||||||
|
def validate_ldap(user, password):
|
||||||
|
# try to connect to the LDAP server
|
||||||
|
# if the connection completes successfully the given user and password is authorized
|
||||||
|
try:
|
||||||
|
con = ldap3.Connection(ldap_server, user="uid=%s,ou=Users,dc=imaginaerraum,dc=de" % (user.username,),
|
||||||
|
password=password, auto_bind=True)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return con is not None
|
||||||
|
|
||||||
class ExtendedLoginForm(LoginForm):
|
class ExtendedLoginForm(LoginForm):
|
||||||
email = StringField('Benutzername oder E-Mail', [Required()])
|
email = StringField('Benutzername oder E-Mail', [Required()])
|
||||||
password = PasswordField('Passwort', [Required()])
|
password = PasswordField('Passwort', [Required()])
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
# authorization in LDAP uses username -> get username associated with email from the database
|
||||||
|
user = find_user(self.email.data)
|
||||||
|
# try authorizing using LDAP
|
||||||
|
response_ldap = validate_ldap(user, self.password.data)
|
||||||
|
|
||||||
|
if response_ldap:
|
||||||
|
# if LDAP authorization succeeds we update the currently stored password in the Flask user datastore
|
||||||
|
# with the one used for LDAP authorization. This way we can authorize with the LDAP password later
|
||||||
|
# even if the server is not reachable
|
||||||
|
user.password = hash_password(self.password.data)
|
||||||
|
|
||||||
|
# try authorizing using Flask security
|
||||||
|
response_orig = super(ExtendedLoginForm, self).validate()
|
||||||
|
|
||||||
|
# if any of the authorization methods is successful we authorize the user
|
||||||
|
return response_ldap or response_orig
|
||||||
|
|
||||||
|
app.config['SECURITY_MSG_USERID_NOT_PROVIDED'] = ('User ID not provided', 'error')
|
||||||
|
|
||||||
# Setup Flask-Security
|
# Setup Flask-Security
|
||||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
||||||
|
@ -245,13 +282,16 @@ def create_application(config):
|
||||||
'valid_thru': session['valid_thru'],
|
'valid_thru': session['valid_thru'],
|
||||||
'inactive': session['inactive'],
|
'inactive': session['inactive'],
|
||||||
'organization': session['organization']}
|
'organization': session['organization']}
|
||||||
|
try:
|
||||||
door.store_tokens(tokens)
|
door.store_tokens(tokens)
|
||||||
|
except Exception as e:
|
||||||
|
flash(f"Error during store_tokens. Exception: {e}")
|
||||||
return redirect('/tokens')
|
return redirect('/tokens')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/delete-token', methods=['POST'])
|
@app.route('/delete-token/<token>', methods=['GET', 'POST'])
|
||||||
@auth_required()
|
@auth_required()
|
||||||
def delete_token():
|
def delete_token(token):
|
||||||
"""Delete the given token from the token file and store the new token file to disk
|
"""Delete the given token from the token file and store the new token file to disk
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -259,12 +299,34 @@ def create_application(config):
|
||||||
token : str
|
token : str
|
||||||
The token to delete from the database.
|
The token to delete from the database.
|
||||||
"""
|
"""
|
||||||
token = request.form.get('token')
|
|
||||||
tokens = door.get_tokens()
|
tokens = door.get_tokens()
|
||||||
if token in tokens: # check if token exists
|
|
||||||
|
if token in tokens:
|
||||||
|
token_to_delete = tokens[token]
|
||||||
|
|
||||||
|
# set up form for confirming deletion
|
||||||
|
form = TokenDeleteForm()
|
||||||
|
form.name_confirm.data = token_to_delete['name']
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
# return page asking the user to confirm delete
|
||||||
|
return render_template('delete.html', token=token_to_delete, form=form)
|
||||||
|
elif form.validate():
|
||||||
|
# form validation successful -> can delete the token
|
||||||
tokens.pop(token)
|
tokens.pop(token)
|
||||||
|
try:
|
||||||
door.store_tokens(tokens)
|
door.store_tokens(tokens)
|
||||||
return "success"
|
except Exception as e:
|
||||||
|
flash(f"Error during store_tokens. Exception: {e}")
|
||||||
|
flash(f"Token {token} wurde gelöscht!")
|
||||||
|
return redirect('/tokens')
|
||||||
|
else:
|
||||||
|
# form validation failed -> return to token overview and flash message
|
||||||
|
flash(f"Der eingegebene Name stimmt nicht überein. Der Token {token} von {token_to_delete['name']} wurde nicht gelöscht.")
|
||||||
|
return redirect('/tokens')
|
||||||
|
else:
|
||||||
|
flash(f'Ungültiger Token {token} für Löschung.')
|
||||||
|
return redirect('/tokens')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/deactivate-token/<token>')
|
@app.route('/deactivate-token/<token>')
|
||||||
|
@ -280,7 +342,10 @@ def create_application(config):
|
||||||
tokens = door.get_tokens()
|
tokens = door.get_tokens()
|
||||||
if token in tokens:
|
if token in tokens:
|
||||||
tokens[token]['inactive'] = True
|
tokens[token]['inactive'] = True
|
||||||
|
try:
|
||||||
door.store_tokens(tokens)
|
door.store_tokens(tokens)
|
||||||
|
except Exception as e:
|
||||||
|
flash(f"Error during store_tokens. Exception: {e}")
|
||||||
return redirect('/tokens')
|
return redirect('/tokens')
|
||||||
|
|
||||||
@app.route('/open')
|
@app.route('/open')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user