1) Added email id validation on the login page.

2) Added validation for the file manager.
This commit is contained in:
Aditya Toshniwal 2020-09-11 19:55:19 +05:30 committed by Akshay Joshi
parent 6ded547a0d
commit b82e6dbdb8
6 changed files with 86 additions and 43 deletions

View File

@ -17,16 +17,18 @@ from flask_babelex import gettext
from .registry import AuthSourceRegistry from .registry import AuthSourceRegistry
from pgadmin.model import User from pgadmin.model import User
from pgadmin.utils.validation_utils import validate_email
@six.add_metaclass(AuthSourceRegistry) @six.add_metaclass(AuthSourceRegistry)
class BaseAuthentication(object): class BaseAuthentication(object):
DEFAULT_MSG = { DEFAULT_MSG = {
'USER_DOES_NOT_EXIST': 'Incorrect username or password.', 'USER_DOES_NOT_EXIST': gettext('Incorrect username or password.'),
'LOGIN_FAILED': 'Login failed', 'LOGIN_FAILED': gettext('Login failed'),
'EMAIL_NOT_PROVIDED': 'Email/Username not provided', 'EMAIL_NOT_PROVIDED': gettext('Email/Username not provided'),
'PASSWORD_NOT_PROVIDED': 'Password not provided' 'PASSWORD_NOT_PROVIDED': gettext('Password not provided'),
'INVALID_EMAIL': gettext('Email/Username is not valid')
} }
@abstractproperty @abstractproperty
@ -85,7 +87,10 @@ class InternalAuthentication(BaseAuthentication):
def validate(self, form): def validate(self, form):
"""User validation""" """User validation"""
# validate the email id first
if not validate_email(form.data['email']):
form.errors['email'] = [self.messages('INVALID_EMAIL')]
return False
# Flask security validation # Flask security validation
return form.validate_on_submit() return form.validate_on_submit()

View File

@ -18,6 +18,7 @@ from urllib.parse import unquote
from sys import platform as _platform from sys import platform as _platform
import config import config
import codecs import codecs
import pathlib
from werkzeug.exceptions import InternalServerError from werkzeug.exceptions import InternalServerError
import simplejson as json import simplejson as json
@ -317,6 +318,11 @@ class Filemanager(object):
# Stores list of dict for filename & its encoding # Stores list of dict for filename & its encoding
loaded_file_encoding_list = [] loaded_file_encoding_list = []
ERROR_NOT_ALLOWED = {
'Error': gettext('Not allowed'),
'Code': 0
}
def __init__(self, trans_id): def __init__(self, trans_id):
self.trans_id = trans_id self.trans_id = trans_id
self.patherror = encode_json( self.patherror = encode_json(
@ -822,10 +828,7 @@ class Filemanager(object):
Rename file or folder Rename file or folder
""" """
if not self.validate_request('rename'): if not self.validate_request('rename'):
return { return self.ERROR_NOT_ALLOWED
'Error': gettext('Not allowed'),
'Code': 0
}
the_dir = self.dir if self.dir is not None else '' the_dir = self.dir if self.dir is not None else ''
@ -883,10 +886,7 @@ class Filemanager(object):
Delete file or folder Delete file or folder
""" """
if not self.validate_request('delete'): if not self.validate_request('delete'):
return { return self.ERROR_NOT_ALLOWED
'Error': gettext('Not allowed'),
'Code': 0
}
the_dir = self.dir if self.dir is not None else '' the_dir = self.dir if self.dir is not None else ''
orig_path = "{0}{1}".format(the_dir, path) orig_path = "{0}{1}".format(the_dir, path)
@ -924,10 +924,7 @@ class Filemanager(object):
File upload functionality File upload functionality
""" """
if not self.validate_request('upload'): if not self.validate_request('upload'):
return { return self.ERROR_NOT_ALLOWED
'Error': gettext('Not allowed'),
'Code': 0
}
the_dir = self.dir if self.dir is not None else '' the_dir = self.dir if self.dir is not None else ''
err_msg = '' err_msg = ''
@ -940,6 +937,12 @@ class Filemanager(object):
orig_path = "{0}{1}".format(the_dir, path) orig_path = "{0}{1}".format(the_dir, path)
new_name = "{0}{1}".format(orig_path, file_name) new_name = "{0}{1}".format(orig_path, file_name)
try:
# Check if the new file is inside the users directory
pathlib.Path(new_name).relative_to(the_dir)
except ValueError as _:
return self.ERROR_NOT_ALLOWED
with open(new_name, 'wb') as f: with open(new_name, 'wb') as f:
while True: while True:
# 4MB chunk (4 * 1024 * 1024 Bytes) # 4MB chunk (4 * 1024 * 1024 Bytes)
@ -1103,10 +1106,7 @@ class Filemanager(object):
Functionality to create new folder Functionality to create new folder
""" """
if not self.validate_request('create'): if not self.validate_request('create'):
return { return self.ERROR_NOT_ALLOWED
'Error': gettext('Not allowed'),
'Code': 0
}
the_dir = self.dir if self.dir is not None else '' the_dir = self.dir if self.dir is not None else ''
@ -1156,10 +1156,7 @@ class Filemanager(object):
Functionality to download file Functionality to download file
""" """
if not self.validate_request('download'): if not self.validate_request('download'):
return { return self.ERROR_NOT_ALLOWED
'Error': gettext('Not allowed'),
'Code': 0
}
the_dir = self.dir if self.dir is not None else '' the_dir = self.dir if self.dir is not None else ''
orig_path = "{0}{1}".format(the_dir, path) orig_path = "{0}{1}".format(the_dir, path)

View File

@ -14,6 +14,8 @@ import os
import re import re
import getpass import getpass
from pgadmin.utils.validation_utils import validate_email
def user_info_desktop(): def user_info_desktop():
print("NOTE: Configuring authentication for DESKTOP mode.") print("NOTE: Configuring authentication for DESKTOP mode.")
@ -43,14 +45,8 @@ def user_info_server():
"pgAdmin user account:\n" "pgAdmin user account:\n"
) )
email_filter = re.compile(
"^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
)
email = input("Email address: ") email = input("Email address: ")
while email == '' or not email_filter.match(email): while not validate_email(email):
print('Invalid email address. Please try again.') print('Invalid email address. Please try again.')
email = input("Email address: ") email = input("Email address: ")

View File

@ -22,9 +22,10 @@ from werkzeug.exceptions import InternalServerError
import config import config
from pgadmin.utils import PgAdminModule from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_response as ajax_response, \ from pgadmin.utils.ajax import make_response as ajax_response, \
make_json_response, bad_request, internal_server_error make_json_response, bad_request, internal_server_error, forbidden
from pgadmin.utils.csrf import pgCSRFProtect from pgadmin.utils.csrf import pgCSRFProtect
from pgadmin.utils.constants import MIMETYPE_APP_JS from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.validation_utils import validate_email
from pgadmin.model import db, Role, User, UserPreference, Server, \ from pgadmin.model import db, Role, User, UserPreference, Server, \
ServerGroup, Process, Setting ServerGroup, Process, Setting
@ -104,16 +105,11 @@ def validate_password(data, new_data):
def validate_user(data): def validate_user(data):
new_data = dict() new_data = dict()
email_filter = re.compile(
"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
)
validate_password(data, new_data) validate_password(data, new_data)
if 'email' in data and data['email'] and data['email'] != "": if 'email' in data and data['email'] and data['email'] != "":
if email_filter.match(data['email']): if validate_email(data['email']):
new_data['email'] = data['email'] new_data['email'] = data['email']
else: else:
raise InternalServerError(_("Invalid email address.")) raise InternalServerError(_("Invalid email address."))
@ -383,6 +379,18 @@ def update(uid):
request.data, encoding='utf-8' request.data, encoding='utf-8'
) )
# Username and email can not be changed for internal users
if usr.auth_source == current_app.PGADMIN_DEFAULT_AUTH_SOURCE:
non_editable_params = ('username', 'email')
for f in non_editable_params:
if f in data:
return forbidden(
errmsg=_(
"'{0}' is not allowed to modify."
).format(f)
)
try: try:
new_data = validate_user(data) new_data = validate_user(data)

View File

@ -34,9 +34,18 @@ def get_storage_directory():
if storage_dir is None: if storage_dir is None:
return None return None
username = current_user.username.split('@')[0] def _preprocess_username(un):
if len(username) == 0 or username[0].isdigit(): ret_un = un
username = 'pga_user_' + username if len(ret_un) == 0 or ret_un[0].isdigit():
ret_un = 'pga_user_' + username
ret_un = ret_un.replace('@', '_')\
.replace('/', 'slash')\
.replace('\\', 'slash')
return ret_un
username = _preprocess_username(current_user.username.split('@')[0])
# Figure out the old-style storage directory name # Figure out the old-style storage directory name
old_storage_dir = os.path.join( old_storage_dir = os.path.join(
@ -45,11 +54,13 @@ def get_storage_directory():
username username
) )
username = _preprocess_username(current_user.username)
# Figure out the new style storage directory name # Figure out the new style storage directory name
storage_dir = os.path.join( storage_dir = os.path.join(
storage_dir.decode('utf-8') if hasattr(storage_dir, 'decode') storage_dir.decode('utf-8') if hasattr(storage_dir, 'decode')
else storage_dir, else storage_dir,
current_user.username.replace('@', '_') username
) )
# Rename an old-style storage directory, if the new style doesn't exist # Rename an old-style storage directory, if the new style doesn't exist

View File

@ -0,0 +1,26 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2020, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import re
def validate_email(email):
if email == '' or email is None:
return False
email_filter = re.compile(
"^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]"
"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
)
if not email_filter.match(email):
return False
return True