Added keyutils and fixes in autologin

This commit is contained in:
Antonio de la Rosa 2025-01-10 22:59:51 +01:00
parent cd0fdacbb7
commit 192e8b4b0b
4 changed files with 155 additions and 10 deletions

View file

@ -1,7 +1,7 @@
# A more simple set for make queries
def insert(model, dict_values, db):
def insert(model, db, dict_values):
final_values={}
@ -25,3 +25,20 @@ def insert(model, dict_values, db):
return success
def select(model, db, dict_fields=[], where_sql='', limit='', dict_values=[]):
if len(dict_fields)==0:
dict_fields=['`'+field+'`' for field in model.fields.keys()]
str_fields=", ".join(dict_fields)
str_query='select {} from {} {} limit 1'.format(str_fields, model.name, where_sql)
arr_result=[]
with db.query(str_query, dict_values) as cursor:
arr_result=cursor.fetchall()
return arr_result

View file

@ -0,0 +1,86 @@
"""
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
Copyright (C) 2023 Antonio de la Rosa Caballero
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from hashlib import sha512, sha256
from base64 import b64encode
from os import urandom
import string
import secrets
# Functions for create random strings usando urandom
def create_key_encrypt(n=10):
""" Simple function for create a random string
Simple function for create a random string based in sha512
Args:
n (int): size of string random bytes (view urandom function in Python3 Help)
"""
return sha512(urandom(n)).hexdigest()
def create_key_encrypt_256(n=10):
""" Simple function for create a random string
Simple function for create a random string based in sha256
Args:
n (int): size of string random bytes (view urandom function in Python3 Help)
"""
return sha256(urandom(n)).hexdigest()
def create_key(n=10):
""" Simple function for create a random string
Simple function for create a random string based in urandom function and base64 encoding
Args:
n (int): size of string random bytes (view urandom function in Python3 Help)
"""
rand_bytes=urandom(n)
return b64encode(rand_bytes).decode('utf-8')[0:-2]
def create_simple_password(n=14):
""" Based in python3 documentation for create passwords using secrets module
https://docs.python.org/3/library/secrets.html
Args:
n (int): Number of random elements of the password
"""
password=''
alphabet=string.ascii_letters+string.digits+string.punctuation
while True:
password=''.join(secrets.choice(alphabet) for i in range(n))
if (any(c.islower() for c in password) and any(c.isupper() for c in password) and sum(c.isdigit() for c in password) >= 3):
break
return password

View file

@ -12,6 +12,8 @@ from parameciofast.libraries.fastutils import ResponseData
from parameciofast.libraries.db import simplequery
from settings import config
from parameciofast.libraries.datetime import now, format_local_strtime, timestamp_to_datetime, obtain_timestamp
from parameciofast.libraries.keyutils import create_key_encrypt, create_key
from time import time
env=env_theme(__file__)
t=PTemplate(env, app.url_path_for)
@ -28,15 +30,16 @@ seconds_login=300
if hasattr(config, 'seconds_login'):
seconds_login=config.seconds_login
cookie_name='paramecio_session'
#useradmin.create_forms()
#useradmin.safe_query=True
if hasattr(config, 'cookie_name'):
cookie_name=config.cookie_name
@admin_app.get('/', response_class=HTMLResponse)
def home_admin(request: Request, paramecio_session: Annotated[str | None, Cookie(description='Cookie for validate into the admin site. The cookie name can change in you settings/config.py')] = None, remote_address: Annotated[str | None, Header()] = None):
if not request.session.get('login_admin', None):
return RedirectResponse(app.url_path_for('login_admin'))
return "Hello world!"
@ -47,6 +50,22 @@ def login_admin(request: Request):
db=WebModel.connection()
if cookie_name+'_remember' in request.cookies:
arr_user=simplequery.select(usermodel, db, dict_fields=['id', 'username'], where_sql='WHERE token_login=%s', dict_values=[request.cookies[cookie_name+'_remember']])
if len(arr_user)>0:
now_str=now()
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
db.query('update useradmin set last_login=%s WHERE id=%s', [date_now, arr_user[0]['id']])
request.session['login_admin']=True
db.close()
return RedirectResponse(app.url_path_for('home_admin'))
with db.query('select count(id) as num_users from useradmin', []) as cursor:
num_users=cursor.fetchone()['num_users']
@ -85,7 +104,7 @@ class ResponseDataLogin(ResponseData):
no_login: bool
@admin_app.post('/login')
def check_login_admin(user: UserAdmin, request: Request) -> ResponseDataLogin:
def check_login_admin(user: UserAdmin, request: Request, response: Response) -> ResponseDataLogin:
db=WebModel.connection()
@ -106,6 +125,22 @@ def check_login_admin(user: UserAdmin, request: Request) -> ResponseDataLogin:
if usermodel.fields['password'].verify(user.password, result['password']):
remember_key=''
if user.remember_login==True:
# Send cookies
remember_key=create_key_encrypt()
timestamp=int(time())+315360000
response.set_cookie(key=cookie_name+'_remember', value=remember_key, expires=timestamp, max_age=315360000, httponly=True, path=config.application_root)
now_str=now()
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
db.query('update useradmin set token_login=%s, last_login=%s WHERE id=%s', [remember_key, date_now, result['id']])
request.session['login_admin']=True
error=0
message=''
@ -156,7 +191,7 @@ def signup_insert_admin(user: UserSignup, request: Request) -> ResponseData:
if not error:
if simplequery.insert(usermodel, dict(user), db):
if simplequery.insert(usermodel, db, dict(user)):
error=0
message="User added!"
@ -168,10 +203,15 @@ def signup_insert_admin(user: UserSignup, request: Request) -> ResponseData:
@admin_app.get('/logout')
def logout_admin(request: Request) -> RedirectResponse:
response=RedirectResponse(app.url_path_for('login_admin'))
if 'login_admin' in request.session:
del request.session['login_admin']
return RedirectResponse(app.url_path_for('login_admin'))
if cookie_name+'_remember' in request.cookies:
response.delete_cookie(cookie_name+'_remember', path=config.application_root)
return response
def check_login_tries(request, db):

View file

@ -44,8 +44,8 @@
</div>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="autologin" name="autologin">
<label class="form-check-label" for="autologin">${tlang('Autologin')}</label>
<input type="checkbox" class="form-check-input" id="remember_login_form" name="remember_login" value="1">
<label class="form-check-label" for="autologin">${tlang('Remember login')}</label>
</div>
<button type="submit" id="login_submit" class="btn btn-primary">Submit</button>
</form>
@ -100,7 +100,9 @@
$('#login_submit').prop('disabled', true);
data_form={'username': $('#username_form').val(), 'password': $('#password_form').val(), 'csrf_token': $("#csrf_token").val(), 'remember_login': 0};
const is_checked=document.getElementById('remember_login_form').checked;
data_form={'username': $('#username_form').val(), 'password': $('#password_form').val(), 'csrf_token': $("#csrf_token").val(), 'remember_login': is_checked};
$.ajax({
url: "${url_for('check_login_admin')}",