diff --git a/paramecio/citoplasma/sendmail.py b/paramecio/citoplasma/sendmail.py
index 84f7374..588e7ae 100644
--- a/paramecio/citoplasma/sendmail.py
+++ b/paramecio/citoplasma/sendmail.py
@@ -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 .
+"""
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:
diff --git a/paramecio/cromosoma/extrafields/passwordfield.py b/paramecio/cromosoma/extrafields/passwordfield.py
index 9c4cce0..bd4ae06 100644
--- a/paramecio/cromosoma/extrafields/passwordfield.py
+++ b/paramecio/cromosoma/extrafields/passwordfield.py
@@ -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 .
+"""
+
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
diff --git a/paramecio/modules/admin/index.py b/paramecio/modules/admin/index.py
index 7f1e713..0fae535 100644
--- a/paramecio/modules/admin/index.py
+++ b/paramecio/modules/admin/index.py
@@ -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+'/')
-@app.post('/'+config.admin_folder+'/')
-@app.get('/'+config.admin_folder+'//')
-@app.post('/'+config.admin_folder+'//')
+@app.get('/admin')
+@admin_app.get('/')
+@admin_app.post('/')
+@admin_app.get('//')
+@admin_app.post('//')
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)
diff --git a/setup.py b/setup.py
index 95f1847..299cf2f 100644
--- a/setup.py
+++ b/setup.py
@@ -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'