diff --git a/parameciofast/libraries/db/simplequery.py b/parameciofast/libraries/db/simplequery.py
index 2268b91..7c8aaad 100644
--- a/parameciofast/libraries/db/simplequery.py
+++ b/parameciofast/libraries/db/simplequery.py
@@ -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
diff --git a/parameciofast/libraries/keyutils.py b/parameciofast/libraries/keyutils.py
new file mode 100644
index 0000000..7b0abda
--- /dev/null
+++ b/parameciofast/libraries/keyutils.py
@@ -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 .
+"""
+
+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
+
diff --git a/parameciofast/modules/fastadmin/app.py b/parameciofast/modules/fastadmin/app.py
index 4f76878..b7c0330 100644
--- a/parameciofast/modules/fastadmin/app.py
+++ b/parameciofast/modules/fastadmin/app.py
@@ -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):
diff --git a/parameciofast/modules/fastadmin/templates/login.phtml b/parameciofast/modules/fastadmin/templates/login.phtml
index 7cb5852..ec42901 100644
--- a/parameciofast/modules/fastadmin/templates/login.phtml
+++ b/parameciofast/modules/fastadmin/templates/login.phtml
@@ -44,8 +44,8 @@