Fixes in user login

This commit is contained in:
Antonio de la Rosa 2025-03-17 01:52:03 +01:00
parent d6fc5b0c4b
commit 5066f5320c
7 changed files with 319 additions and 9 deletions

View file

@ -5,7 +5,7 @@ from paramecio.libraries.db.coreforms import PasswordForm
from paramecio.libraries.i18n import I18n from paramecio.libraries.i18n import I18n
from paramecio.libraries.sessionplugin import get_session from paramecio.libraries.sessionplugin import get_session
from paramecio.libraries.keyutils import create_key_encrypt from paramecio.libraries.keyutils import create_key_encrypt
from bottle import request from bottle import request, abort
# Need unittest # Need unittest
@ -144,3 +144,13 @@ def request_type():
return request.environ['REQUEST_METHOD'] return request.environ['REQUEST_METHOD']
def check_csrf(name_csrf_token='csrf_token'):
session=get_session()
csrf_token=session.get('csrf_token', '')
if csrf_token=='' or csrf_token!=request.forms.get(name_csrf_token):
abort(403)

View file

@ -17,6 +17,8 @@ from paramecio.libraries.sessionplugin import SessionPlugin
from paramecio.libraries.httputils import GetPostFiles from paramecio.libraries.httputils import GetPostFiles
from paramecio.libraries.db.formsutils import check_form, csrf_token from paramecio.libraries.db.formsutils import check_form, csrf_token
from paramecio.libraries.db.coreforms import PasswordForm from paramecio.libraries.db.coreforms import PasswordForm
from paramecio.libraries.sendmail import SendMail
from paramecio.libraries.db.formsutils import check_csrf
admin_app=Bottle() admin_app=Bottle()
admin_app.install(SessionPlugin()) admin_app.install(SessionPlugin())
@ -138,6 +140,7 @@ def check_login_admin(session={}):
username=getpost.post.get('username') username=getpost.post.get('username')
password=getpost.post.get('password') password=getpost.post.get('password')
remember_login=getpost.post.get('remember_login') remember_login=getpost.post.get('remember_login')
token_auth=''
if username!='' and password!='' and not no_login: if username!='' and password!='' and not no_login:
@ -162,7 +165,20 @@ def check_login_admin(session={}):
now_str=now() now_str=now()
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str) date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
db.query('update useradmin2 set token_login=%s, last_login=%s WHERE id=%s', [remember_key, date_now, result['id']]) token_auth=''
if result['double_auth']:
token_auth=create_key(8)
session['verify_auth']=True
# Send email
sendmail=SendMail(ssl=True)
sendmail.send(config.portal_email, [result['email']], i18n.tlang('Code for complete login'), i18n.tlang('We send to you a code for activate your account using double authentication:')+"\n"+token_auth, content_type='plain', attachments=[])
db.query('update useradmin2 set token_login=%s, last_login=%s, token_auth=%s WHERE id=%s', [remember_key, date_now, usermodel.fields['password'].check(token_auth), result['id']])
session['login_admin']=True session['login_admin']=True
session['user_id']=result['id'] session['user_id']=result['id']
@ -264,6 +280,59 @@ def change_theme(session={}):
return {'error': error, 'message': ''} return {'error': error, 'message': ''}
@admin_app.get('/need_auth', skip=[check_login], name='admin_app.need_auth')
def need_auth(session={}):
i18n=I18n('admin2')
return t.load_template('need_auth.phtml', title=i18n.tlang('Auth check'))
@admin_app.post('/auth_check', skip=[check_login], name='admin_app.auth_check')
def auth_check(session={}):
error=1
#check_csrf()
you_cannot_login=0
db=WebModel.connection()
if 'login_admin' in session:
code=request.forms.get('code', '')
user_admin=UserAdmin2(db)
user_admin.check_user=False
arr_user=user_admin.set_conditions('WHERE id=%s', [session.get('user_id', 0)]).select_a_row_where()
#print(arr_user)
if arr_user:
if user_admin.fields['token_auth'].verify(code, arr_user['token_auth']):
user_admin.safe_query()
#user_admin.set_conditions('WHERE id=%s', [session['user_id']]).update({'token_auth': ''})
user_admin.query('update useradmin2 set token_auth="" WHERE id=%s', [session['user_id']])
session['verify_auth']=False
error=0
else:
#you_cannot_login=check_login_tries()
you_cannot_login=check_login_tries(request, db)
else:
you_cannot_login=check_login_tries()
db.close()
return {'error': error, 'you_cannot_login': you_cannot_login}
def check_login_tries(request, db): def check_login_tries(request, db):

View file

@ -3,7 +3,7 @@ from paramecio.modules.admin2.app import admin_app
from paramecio.modules.admin2.libraries.config import modules_admin, modules_admin_icons from paramecio.modules.admin2.libraries.config import modules_admin, modules_admin_icons
from paramecio.libraries.mtemplates import PTemplate, env_theme from paramecio.libraries.mtemplates import PTemplate, env_theme
from paramecio.libraries.i18n import I18n from paramecio.libraries.i18n import I18n
import paramecio.modules.admin2.libraries.i18n import paramecio.modules.admin2.libraries.i18n as i18n_lang
import os import os
#from paramecio.libraries.lists import SimpleList #from paramecio.libraries.lists import SimpleList
from paramecio.libraries.generate_admin_class import GenerateAdminClass from paramecio.libraries.generate_admin_class import GenerateAdminClass
@ -13,11 +13,14 @@ from paramecio.libraries.db.coreforms import PasswordForm
from paramecio.wsgiapp import app from paramecio.wsgiapp import app
from paramecio.libraries.db.webmodel import WebModel from paramecio.libraries.db.webmodel import WebModel
from paramecio.modules.admin2.models.admin import UserAdmin2 from paramecio.modules.admin2.models.admin import UserAdmin2
from paramecio.libraries.urls import add_get_parameters
env=env_theme(__file__) env=env_theme(__file__)
t=PTemplate(env) t=PTemplate(env)
i18n=I18n('admin2')
#t.env.directories=admin_t.env.directories #t.env.directories=admin_t.env.directories
""" """
tpl_path=os.path.dirname(__file__).replace('/admin', '')+'/templates/admin' tpl_path=os.path.dirname(__file__).replace('/admin', '')+'/templates/admin'
@ -45,11 +48,15 @@ def admin_users(session={}):
user_admin.fields['dark_theme'].name_form=SelectForm user_admin.fields['dark_theme'].name_form=SelectForm
user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'dark_theme']) user_admin.fields['double_auth'].name_form=SelectForm
user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'dark_theme', 'double_auth'])
user_admin.forms['privileges'].arr_select={0: I18n.lang('admin', 'without_privileges', 'Without privileges'), 1: I18n.lang('admin', 'selected_privileges', 'Selected privileges'), 2: I18n.lang('admin', 'administrator', 'Administrator')} user_admin.forms['privileges'].arr_select={0: I18n.lang('admin', 'without_privileges', 'Without privileges'), 1: I18n.lang('admin', 'selected_privileges', 'Selected privileges'), 2: I18n.lang('admin', 'administrator', 'Administrator')}
user_admin.forms['dark_theme'].arr_select={0: _('Light theme'), 1: _('Dark theme')} user_admin.forms['dark_theme'].arr_select={0: i18n.tlang('Light theme'), 1: i18n.tlang('Dark theme')}
user_admin.forms['double_auth'].arr_select={0: i18n.tlang('No'), 1: i18n.tlang('Yes')}
user_admin.fields['password'].protected=False user_admin.fields['password'].protected=False
@ -61,7 +68,9 @@ def admin_users(session={}):
admin.list.search_fields=['username'] admin.list.search_fields=['username']
admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'privileges', 'lang', 'dark_theme'] admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'privileges', 'lang', 'dark_theme', 'double_auth']
admin.list.arr_extra_options=[user_options]
slist=admin.show() slist=admin.show()
@ -69,4 +78,20 @@ def admin_users(session={}):
return t.load_template('users.phtml', title=i18n.tlang('Admin users'), tlang=i18n.tlang, module_selected='admin_app.admin_users', slist=slist, session=session) return t.load_template('users.phtml', title=i18n.tlang('Admin users'), tlang=i18n.tlang, module_selected='admin_app.admin_users', slist=slist, session=session)
@admin_app.get('/ausers/permissions/<user_id:int>', name="admin_app.admin_permissions")
def admin_users(user_id, session={}):
db=WebModel.connection()
db.close()
return ""
def user_options(url, id, arr_row):
options=[]
options.append('<a href="'+add_get_parameters(url, op_admin=1, id=id)+'">'+I18n.lang('common', 'edit', 'Edit')+'</a>')
if not arr_row['privileges']:
options.append(f'<a href="{app.get_url('admin_app.admin_permissions', user_id=id)}">'+i18n.tlang('User access')+'</a>')
options.append('<a href="'+add_get_parameters(url, op_admin=3, id=id)+'">'+I18n.lang('common', 'delete', 'Delete')+'</a>')
return options

View file

@ -8,12 +8,15 @@ def check_login(callback):
if 'session' in request.environ: if 'session' in request.environ:
if request.environ['session'].get('login_admin', 0): if request.environ['session'].get('login_admin', False) and not request.environ['session'].get('verify_auth', False):
result = callback(*args, **kwargs) result = callback(*args, **kwargs)
return result return result
if request.environ['session'].get('verify_auth', False):
redirect(app.get_url('admin_app.need_auth'))
redirect(app.get_url('admin_app.login_admin')) redirect(app.get_url('admin_app.login_admin'))
return wrapper return wrapper

View file

@ -49,7 +49,9 @@ class UserAdmin2(UserModel):
self.register(corefields.CharField('token_login')) self.register(corefields.CharField('token_login'))
#self.register(PasswordField('token_auth')) self.register(PasswordField('token_auth'))
self.register(PasswordField('token_key'))
self.register(PrivilegesField2('privileges')) self.register(PrivilegesField2('privileges'))

View file

@ -0,0 +1,4 @@
<%inherit file="layout.phtml"/>
<%block name="content">
</%block>

View file

@ -0,0 +1,197 @@
<%inherit file="login.phtml"/>
<%block name="content">
<div class="card">
<div class="card-header bg-primary">
${tlang('Auth code')}
</div>
<div class="card-body">
<form method="POST" action="#" id="auth_code_form" class="needs-validation" novalidate>
<div class="mb-3">
<label for="code_form" class="form-label">${tlang('Check your email for get instructions for complete login with double auth or')} <a href="${url_for('admin_app.logout_admin')}">logout</a> and login again with other user</label>
<input type="text" class="form-control form-control-lg has-validation" id="code_form" name="code" aria-describedby="code" autocomplete="off" required>
<div class="invalid-feedback" id="txt_error_code">
${tlang('You need a valid code')}
</div>
</div>
<button type="submit" id="code_submit" class="btn btn-primary">${tlang('Check code')}</button>
${csrf_token()|n}
</form>
</div>
<!--<div class="form">
<p align="center">${tlang('Check your email for get instructions for complete login with double auth or')} <a href="${url_for('admin_app.logout_admin')}">logout</a> and login again with other user</p>
<p><label>${tlang('Code')} *</label><input type="text" class="" name="code" id="code_form" value="" /> <span class="error" id="code_error"></span></p>
${csrf_token()|n}
</div>
<div id="submit_block">
<input type="submit" value="${tlang('Send code')}" class="submit" id="code_submit"/>
<span id="loading">&nbsp;</span>
</div>-->
</%block>
<%block name="jscript">
<script language="javascript">
$(document).ready( function () {
var forms = document.querySelectorAll('.needs-validation');
var error=false;
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
error=false;
/*if (!form.checkValidity()) {
error=true;
}*/
event.preventDefault();
event.stopPropagation();
//form.classList.add('was-validated');
if(!error) {
$('#code_submit').prop('disabled', true);
console.log('Checking done!');
$('#loading').show();
data_form={'code': $('#code_form').val(), 'csrf_token': $("#csrf_token").val()};
$.ajax({
url: "${url_for('admin_app.auth_check')}",
method: "POST",
dataType: "json",
data: data_form,
error: function(jqXHR, textStatus, errorThrown ) {
form.classList.add('was-validated');
alert(textStatus);
$('#code_submit').prop('disabled', false);
$('#code_form').get(0).setCustomValidity("${tlang('Error, please try again later')}");
}
}).done(function(data) {
form.classList.add('was-validated');
if(data.error==0)
{
//location.reload()
location.href="${url_for('admin_app.home_admin')}";
}
else
{
$('#code_submit').prop('disabled', false);
// Firefox have a horrible and stupid bug and you need attr for set de new csrf_token
$('#csrf_token').attr('value', data.csrf_token);
$('#loading').hide('slow');
if(data.hasOwnProperty('disable')) {
//$('#code_error').html("${tlang('Error, your user is disabled, you need support of web administration')}");
$('#code_form').get(0).setCustomValidity("${tlang('Error, your user is disabled, you need support of web administration')}");
$('#txt_error_code').html("${tlang('Error, your user is disabled, you need support of web administration')}");
} else {
$('#code_form').get(0).setCustomValidity("${tlang('Error, wrong code')}");
//$('#code_error').html("${tlang('Error, wrong code')}");
$('#txt_error_code').html("${tlang('Error, wrong code')}");
}
if(data.you_cannot_login) {
//$('#code_error').html("${tlang('Error, excessive tries, wait some minutes for login again')}");
$('#code_form').get(0).setCustomValidity("${tlang('Error, excessive tries, wait some minutes for login again')}");
$('#txt_error_code').html("${tlang('Error, excessive tries, wait some minutes for login again')}");
}
}
});
}
}
)
});
/*$('#code_submit').click( function () {
$('#code_submit').prop('disabled', true);
$('#loading').show();
data_form={'code': $('#code_form').val(), 'csrf_token': $("#csrf_token").val()};
$.ajax({
url: "",
method: "POST",
dataType: "json",
data: data_form,
error: function(jqXHR, textStatus, errorThrown ) {
alert(textStatus);
$('#code_submit').prop('disabled', false);
}
}).done(function(data) {
if(data.error==0)
{
//location.reload()
location.href="";
}
else
{
$('#code_submit').prop('disabled', false);
// Firefox have a horrible and stupid bug and you need attr for set de new csrf_token
$('#csrf_token').attr('value', data.csrf_token);
$('#loading').hide('slow');
if(data.hasOwnProperty('disable')) {
$('#code_error').html("${tlang('Error, your user is disabled, you need support of web administration')}");
} else {
$('#code_error').html("${tlang('Error, wrong code')}");
}
if(data.you_cannot_login) {
$('#code_error').html("${tlang('Error, excessive tries, wait some minutes for login again')}");
}
}
});
return false;
});
*/
});
</script>
</%block>