More fixes for modernize codebase

This commit is contained in:
Antonio de la Rosa 2024-09-29 18:26:51 +02:00
parent e93adee075
commit aa1ad66525
4 changed files with 109 additions and 127 deletions

View file

@ -1,4 +1,23 @@
#!/usr/bin/env python3
"""
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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/>.
"""
import os
import smtplib
import mimetypes
@ -13,6 +32,8 @@ import ssl as ssl_module
import sys
class SendMail:
"""Class for send email
"""
port=587
@ -25,6 +46,20 @@ class SendMail:
#ssl=True
def __init__(self, ssl=True):
"""Class for send email
Class for send email using standard python library
Attributes:
port (int): The port used for send email, by default 587
host (str): The hostname of mail server used for send the email
username (str): The username for login in mail server
password (str): The password for login in mail server
smtp (smtplib.SMTP): The python SMTP object used for send emails
txt_error: (str): If error, is saved in this attribute
"""
self.smtp=None #smtplib.SMTP(host=self.host, port=self.port)
self.txt_error=''
@ -79,9 +114,9 @@ class SendMail:
return False
except smtplib.SMTPAuthenticationError:
except smtplib.SMTPAuthenticationError as eauth:
self.txt_error='Error: cannot login. Wrong username or password'
self.txt_error='Error: cannot login. Wrong username or password '+eauth.__str__()
return False
@ -94,6 +129,18 @@ class SendMail:
return True
def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[]):
""" Method that send email
With this method you can send email to multiple address, html, and add attachments to email
Args:
from_address (str): The adress used for send the email
to_address (list): A list of emails where the email will be sended.
subject (str): The subject of the email
message (str): The content of the message
content_type (str): The type of mail content, can be plain or html.
attachments (list): A list with a serie of file paths for attach to the email.
"""
if self.smtp==None:
if not self.connect():
@ -174,12 +221,14 @@ class SendMail:
return True
def quit(self):
"""Function used when you need quit connection for any reason"""
if self.smtp!=None:
self.smtp.quit()
self.smtp=None
def __del__(self):
"""Method for clean the connection when the object is closed"""
if self.smtp!=None:

View file

@ -1,9 +1,38 @@
#!/usr/bin/python3
"""
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
Copyright (C) 2024 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 paramecio.cromosoma.corefields import PhangoField
from paramecio.cromosoma.coreforms import PasswordForm
from hmac import compare_digest as compare_hash
import crypt
from hmac import compare_digest as compare_hash
#try:
# import crypt
#except:
# pass
#import bcrypt
from argon2 import PasswordHasher
class PasswordField(PhangoField):
"""Field for check and save passwords"""
def __init__(self, name, size=1024, required=False):
@ -43,7 +72,10 @@ class PasswordField(PhangoField):
#salt=crypt.mksalt(crypt.METHOD_SHA512)
if self.encrypt_password:
value=crypt.crypt(value)
#value=crypt.crypt(value)
ph=PasswordHasher()
final_value=ph.hash(value)
return final_value
"""
else:
@ -57,8 +89,14 @@ class PasswordField(PhangoField):
@staticmethod
def verify( password, h):
"""Static method used for verify a password save using PasswordField"""
#return bcrypt_sha256.verify(password, h)
return compare_hash(h, crypt.crypt(password, h))
#return compare_hash(h, crypt.crypt(password, h))
ph=PasswordHasher()
try:
return ph.verify(h, password)
except:
return False
# Old function bcrypt

View file

@ -24,6 +24,8 @@ from paramecio.wsgiapp import app
import copy
from paramecio.i18n import admin
admin_app=Bottle()
#from citoplasma.login import LoginClass
# Check login
@ -70,11 +72,11 @@ if hasattr(config, 'admin_templates_index'):
num_template+=1
@app.get('/'+config.admin_folder)
@app.get('/'+config.admin_folder+'/<module>')
@app.post('/'+config.admin_folder+'/<module>')
@app.get('/'+config.admin_folder+'/<module>/<submodule>')
@app.post('/'+config.admin_folder+'/<module>/<submodule>')
@app.get('/admin')
@admin_app.get('/<module>')
@admin_app.post('/<module>')
@admin_app.get('/<module>/<submodule>')
@admin_app.post('/<module>/<submodule>')
def home(module='', submodule='', t=t):
# A simple boolean used for show or not the code of admin module in standard template
@ -248,121 +250,12 @@ def home(module='', submodule='', t=t):
return ""
@app.post('/'+config.admin_folder+'/login')
@admin_app.post('/login')
def login():
return login_model(UserAdmin)
"""
connection=WebModel.connection()
user_admin=UserAdmin(connection)
getpostfiles=GetPostFiles()
getpostfiles.obtain_post()
getpostfiles.post['username']=getpostfiles.post.get('username', '')
getpostfiles.post['password']=getpostfiles.post.get('password', '')
username=user_admin.fields['username'].check(getpostfiles.post['username'])
password=getpostfiles.post['password'].strip()
user_admin.conditions=['WHERE username=%s', [username]]
arr_user=user_admin.select_a_row_where(['id', 'password', 'privileges', 'lang', 'num_tries', 'email'])
if arr_user==False:
s=get_session()
s['csrf_token']=create_key_encrypt()
s.save()
connection.close()
return {'error': 1, 'csrf_token': s['csrf_token']}
else:
num_tries=int(arr_user['num_tries'])
if arr_user['num_tries']<3:
if user_admin.fields['password'].verify(password, arr_user['password']):
s=get_session()
s['id']=arr_user['id']
s['login']=1
s['privileges']=arr_user['privileges']
s['lang']=arr_user['lang']
s['email']=arr_user['email']
if s['lang']=='':
s['lang']=I18n.default_lang
remember_login=getpostfiles.post.get('remember_login', '0')
if remember_login=='1':
timestamp=time()+315360000
random_text=create_key_encrypt()
#Update user with autologin token
user_admin.check_user=False
user_admin.conditions=['WHERE username=%s', [username]]
user_admin.valid_fields=['token_login']
user_admin.reset_require()
if user_admin.update({'token_login': random_text}):
response.set_cookie('remember_login', random_text, path=config.session_opts['session.path'], expires=timestamp, secret=key_encrypt)
#else:
#print(user_admin.query_error)
s.save()
connection.close()
return {'error': 0}
else:
user_admin.check_user=False
user_admin.conditions=['WHERE username=%s', [username]]
user_admin.valid_fields=['num_tries']
user_admin.reset_require()
user_admin.update({'num_tries': arr_user['num_tries']+1})
s=get_session()
s['csrf_token']=create_key_encrypt()
s.save()
connection.close()
return {'error': 1, 'csrf_token': s['csrf_token']}
else:
s=get_session()
s['csrf_token']=create_key_encrypt()
s.save()
connection.close()
return {'error': 1, 'csrf_token': s['csrf_token']}
"""
@app.post('/'+config.admin_folder+'/register')
@admin_app.post('/register')
def register():
getpostfiles=GetPostFiles()
@ -423,7 +316,7 @@ def register():
return {'error': 1}
@app.get('/'+config.admin_folder+'/logout')
@admin_app.get('/logout')
def logout():
s=get_session()
@ -446,7 +339,7 @@ def logout():
redirect(make_url(config.admin_folder))
@app.get('/'+config.admin_folder+'/recovery_password')
@admin_app.get('/recovery_password')
def recovery_password():
t=PTemplate(env)
@ -465,7 +358,7 @@ def recovery_password():
connection.close()
return t.load_template('admin/recovery.phtml', forms=forms)
@app.post('/'+config.admin_folder+'/recovery_password')
@admin_app.post('/recovery_password')
def send_password():
connection=WebModel.connection()
@ -522,13 +415,13 @@ def send_password():
return {'email': '', 'error': 0}
@app.get('/'+config.admin_folder+'/check_token')
@admin_app.get('/check_token')
def check_token():
t=PTemplate(env)
return t.load_template('admin/check_token.phtml')
@app.post('/'+config.admin_folder+'/check_token')
@admin_app.post('/check_token')
def check_code_token():
t=PTemplate(env)
@ -583,3 +476,5 @@ def check_code_token():
s.save()
return {'token': 'Error: token is not valid', 'error': 1, 'csrf_token': s['csrf_token']}
app.mount('/'+config.admin_folder+'/', admin_app)

View file

@ -21,7 +21,7 @@ setup(name='paramecio',
url='https://git.cuchulu.com/paramecio/parameciofm/',
packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'],
include_package_data=True,
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', 'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', 'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow', 'argon2-cffi', 'pillow'],
entry_points={'console_scripts': [
'paramecio = paramecio.console:start',
'parameciodb = paramecio.cromosoma.dbamin.start'