Compare commits

...

2 Commits

Author SHA1 Message Date
cbb7ed4498 2fa-0.7 2023-11-18 20:45:37 +01:00
4d2cf64f19 2fa-0.7 2023-11-18 20:44:06 +01:00
5 changed files with 87 additions and 32 deletions

1
.gitignore vendored
View File

@ -5,4 +5,5 @@
*.swo *.swo
*~ *~
session session
static/tmp
libs/__pycache__ libs/__pycache__

View File

@ -16,11 +16,12 @@
<h1>2FA</h1> <h1>2FA</h1>
% if data['secureAuth'] == True: % if data['secureAuth'] == True:
<div class="qr-code">
<img src="{{'static/tmp/'+data['authCode']+'.png'}}" />
</div>
<form name="disable2faForm" method="post" action="/disable_2fa"> <form name="disable2faForm" method="post" action="/disable_2fa">
<label for="2fa">2FA</label> <input id="2fa" name="2fa" type="text" value="{{data['authCode']}}" readonly>
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}">
<div class="form-buttons"> <div class="form-buttons">
<a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a> <a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a>
@ -30,11 +31,10 @@
</form> </form>
% else: % else:
<form name="enable2faForm" method="post" action="/enable_2fa"> <form name="enable2faForm" method="post" action="/enable_2fa">
<label for="2fa">2FA</label> <input id="2fa" name="2fa" type="text" value="{{data['authCode']}}" readonly>
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}">
<div class="form-buttons"> <div class="form-buttons">
<a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a> <a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a>

92
app.py
View File

@ -27,9 +27,10 @@ from ldap3 import Server, Connection, ALL
from ldap3 import SIMPLE, SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, ALL_ATTRIBUTES from ldap3 import SIMPLE, SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, ALL_ATTRIBUTES
from ldap3.core.exceptions import LDAPBindError, LDAPConstraintViolationResult, \ from ldap3.core.exceptions import LDAPBindError, LDAPConstraintViolationResult, \
LDAPInvalidCredentialsResult, LDAPUserNameIsMandatoryError, \ LDAPInvalidCredentialsResult, LDAPUserNameIsMandatoryError, \
LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult, \
LDAPNoSuchAttributeResult
import logging import logging
from os import getenv, environ, path from os import getenv, environ, path, remove
from libs import flist, slist from libs import flist, slist
from libs.localization import * from libs.localization import *
from libs.helper import tools from libs.helper import tools
@ -118,11 +119,22 @@ def get_index():
@get('/_2fa') @get('/_2fa')
def get_index(): def get_index():
#newSession().get()
try: try:
reload(newSession().get()['username'], None, None)
#add_auth_attribute_step1(newSession().get()['username'], None, None)
return _2fa_tpl(data=newSession().get(), str=i18n.str) return _2fa_tpl(data=newSession().get(), str=i18n.str)
except Exception as e: except Exception as e:
return index_tpl(str=i18n.str) return index_tpl(str=i18n.str)
@get('/enable_2fa')
@get('/disable_2fa')
def get_index():
try:
return user_tpl(data=newSession().get(), str=i18n.str)
except Exception as e:
return index_tpl(str=i18n.str)
@post('/user') @post('/user')
def post_user(): def post_user():
form = request.forms.getunicode form = request.forms.getunicode
@ -276,33 +288,47 @@ def post_edit_email():
@post('/enable_2fa') @post('/enable_2fa')
def post_enable_2fa(): def post_enable_2fa():
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='enable')
'''
add attribute authCode
set session data
'''
except Error as e:
LOG.warning("akatsa")
return error(str(e))
return _2fa_tpl(data=newSession().get(), str=i18n.str) def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(not newSession().get()['secureAuth']):
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='enable')
except Error as e:
#add_auth_attribute_step1(newSession().get()['username'], None, None)
reload(newSession().get()['username'], None, None)
LOG.warning(e)
return error('2 urratseko autentifikazioa birgaitua izan da.')
except Error as e:
LOG.warning(e)
return index_tpl(alerts=[('error', e, 'fadeOut')], str=i18n.str)
return _2fa_tpl(alerts=[('success', 'Bikain, 2 urratseko autentifikazioa gaitu da.', 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/disable_2fa') @post('/disable_2fa')
def post_disable_2fa(): def post_disable_2fa():
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='disable')
'''
add attribute authCode
set session data
'''
except Error as e:
LOG.warning("akatsa")
return error(str(e))
return _2fa_tpl(data=newSession().get(), str=i18n.str) def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(newSession().get()['secureAuth']):
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, None, action='disable')
except Error as e:
#add_auth_attribute_step1(newSession().get()['username'], None, None)
reload(newSession().get()['username'], None, None)
LOG.warning(e)
return error(str(e))
except Error as e:
LOG.warning(e)
return index_tpl(alerts=[('error', e, 'fadeOut')], str=i18n.str)
return _2fa_tpl(alerts=[('error', '2 urratseko autentifikazioa desgaitua izan da.', 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/change_pwd') @post('/change_pwd')
def post_change_pwd(): def post_change_pwd():
@ -366,6 +392,10 @@ def serve_static(filename):
def font(filepath): def font(filepath):
return static_file(filepath, root="static/fonts") return static_file(filepath, root="static/fonts")
@get("/static/tmp/<filepath:re:.*\.(png|svg)>")
def font(filepath):
return static_file(filepath, root="static/tmp")
def index_tpl(**kwargs): def index_tpl(**kwargs):
return template('index', **kwargs) return template('index', **kwargs)
@ -728,6 +758,10 @@ def add_auth_attribute_step2(conf, *args):
LOG.error('{}: {!s}'.format(e.__class__.__name__, e)) LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[23]) raise Error(i18n.msg[23])
except LDAPNoSuchAttributeResult as e:
LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error('Dagoeneko desgaiturik zeneukan 2 urratseko autentifikazioa.')
except LDAPExceptionError as e: except LDAPExceptionError as e:
LOG.error('{}: {!s}'.format(e.__class__.__name__, e)) LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[23]) raise Error(i18n.msg[23])
@ -746,8 +780,18 @@ def add_auth_attribute_step3(conf, username, code, action):
elif(action == 'disable'): elif(action == 'disable'):
c.modify(user_dn,{'authCode': [(MODIFY_DELETE, [])]}) c.modify(user_dn,{'authCode': [(MODIFY_DELETE, [])]})
c.modify(user_dn,{'secureAuth': [MODIFY_REPLACE, [False]]}) c.modify(user_dn,{'secureAuth': [MODIFY_REPLACE, [False]]})
#remove file
try:
remove('static/tmp/'+newSession().get()['authCode']+'.png')
except OSError as e:
LOG.warning(str(e))
#raise Error(e)
pass
newSession().set(get_user_data(user_dn, c)) newSession().set(get_user_data(user_dn, c))
reload=add_auth_attribute_step1
#CHANGE PASSWORD #CHANGE PASSWORD
def change_passwords(username, old_pass, new_pass): def change_passwords(username, old_pass, new_pass):
changed = [] changed = []

View File

@ -4,6 +4,7 @@ import sqlite3
import re import re
from onetimepass import valid_totp from onetimepass import valid_totp
from secrets import choice from secrets import choice
import segno
class Tools(): class Tools():
@ -44,12 +45,14 @@ class Tools():
def pwd_validation(self, e): def pwd_validation(self, e):
regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{8,18}$' regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{8,18}$'
return(bool(re.fullmatch(regex, e))) return(bool(re.fullmatch(regex, e)))
# 2FA
def generate_secret(self): # Function to return a random string with length 16. def generate_secret(self): # Function to return a random string with length 16.
secret = '' secret = ''
while len(secret) < 16: while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
qrcode = segno.make(secret, micro=False)
qrcode.save('static/tmp/'+secret+'.png', scale=10)
return secret return secret
tools = Tools() tools = Tools()

View File

@ -194,6 +194,13 @@ button.red:hover{
animation-name: fadeOut; animation-name: fadeOut;
} }
/**/
.qr-code {
margin: 0 auto;
width: max-content;
text-align: center;
}
/**/ /**/
.grid-container { .grid-container {
display: grid; display: grid;