mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Resolve the log in issue for a user having a non-existing email id
1) Added CHECK_EMAIL_DELIVERABILITY & SECURITY_EMAIL_VALIDATOR_ARGS. 2) Added test cases for deliverability check. Fixes #6550
This commit is contained in:
parent
ef67409d61
commit
9fdda038a9
@ -388,7 +388,9 @@ SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE = \
|
|||||||
# flask-security-too will validate email addresses and check deliverability
|
# flask-security-too will validate email addresses and check deliverability
|
||||||
# by default. Disable the deliverability check by default, which was the old
|
# by default. Disable the deliverability check by default, which was the old
|
||||||
# behaviour in <= v5.3
|
# behaviour in <= v5.3
|
||||||
SECURITY_EMAIL_VALIDATOR_ARGS = {"check_deliverability": False}
|
CHECK_EMAIL_DELIVERABILITY = False
|
||||||
|
SECURITY_EMAIL_VALIDATOR_ARGS = \
|
||||||
|
{"check_deliverability": CHECK_EMAIL_DELIVERABILITY}
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Upgrade checks
|
# Upgrade checks
|
||||||
|
@ -465,6 +465,8 @@ def create_app(app_name=None):
|
|||||||
# CSRF Token expiration till session expires
|
# CSRF Token expiration till session expires
|
||||||
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
|
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
|
||||||
'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'],
|
'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'],
|
||||||
|
# Disable deliverable check for email addresss
|
||||||
|
'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS
|
||||||
}))
|
}))
|
||||||
|
|
||||||
security.init_app(app, user_datastore)
|
security.init_app(app, user_datastore)
|
||||||
|
62
web/pgadmin/setup/tests/test_no_email_deliverability.py
Normal file
62
web/pgadmin/setup/tests/test_no_email_deliverability.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
from pgadmin.setup import user_info
|
||||||
|
import config
|
||||||
|
from regression.python_test_utils.test_utils import module_patch
|
||||||
|
from unittest.mock import patch
|
||||||
|
from pgadmin.utils.constants import ENTER_EMAIL_ADDRESS
|
||||||
|
|
||||||
|
|
||||||
|
class EmailValidationOnSetup(BaseTestGenerator):
|
||||||
|
"""
|
||||||
|
This class tests the non-deliverability of email for invalid email id.
|
||||||
|
This test case is only responsible for testing non-deliverability emails
|
||||||
|
only.
|
||||||
|
"""
|
||||||
|
PPROMPT_RETURN_VALUE = '1234567'
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
# scenario for testing invalid email for non-deliverability only
|
||||||
|
('TestCase for email validation', dict(
|
||||||
|
data=['postgres@local.dev', 'pg@pgadminrocks.com',
|
||||||
|
'me.pg@demo.dev', 'pg@123.pgcom',
|
||||||
|
'pg@postgres.local', 'postgres@pg.blah'],
|
||||||
|
check_deliverability=False,
|
||||||
|
)),
|
||||||
|
]
|
||||||
|
|
||||||
|
@patch('builtins.input')
|
||||||
|
@patch('os.environ')
|
||||||
|
def runTest(self, os_environ_mock, input_mock):
|
||||||
|
|
||||||
|
if config.SERVER_MODE is False:
|
||||||
|
self.skipTest(
|
||||||
|
"Can not email validation test cases in the DESKTOP mode."
|
||||||
|
)
|
||||||
|
|
||||||
|
os_environ_mock.return_value = []
|
||||||
|
config.CHECK_EMAIL_DELIVERABILITY = self.check_deliverability
|
||||||
|
|
||||||
|
with module_patch('pgadmin.setup.user_info.pprompt') as pprompt_mock:
|
||||||
|
pprompt_mock.return_value \
|
||||||
|
= self.PPROMPT_RETURN_VALUE, self.PPROMPT_RETURN_VALUE
|
||||||
|
|
||||||
|
for e in self.data:
|
||||||
|
input_mock.return_value = e
|
||||||
|
# skipping some setup-db part as we are only testing the
|
||||||
|
# mail validation through setup.
|
||||||
|
email, password = user_info()
|
||||||
|
|
||||||
|
input_mock.assert_called_once_with(ENTER_EMAIL_ADDRESS)
|
||||||
|
# assert equal means deliverability is not done, and entered
|
||||||
|
# email id is returned as it is.
|
||||||
|
self.assertEqual(e, email)
|
||||||
|
input_mock.reset_mock()
|
@ -13,6 +13,7 @@ import random
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import getpass
|
import getpass
|
||||||
|
from pgadmin.utils.constants import ENTER_EMAIL_ADDRESS
|
||||||
|
|
||||||
from pgadmin.utils.validation_utils import validate_email
|
from pgadmin.utils.validation_utils import validate_email
|
||||||
|
|
||||||
@ -27,6 +28,10 @@ def user_info_desktop():
|
|||||||
return email, p1
|
return email, p1
|
||||||
|
|
||||||
|
|
||||||
|
def pprompt():
|
||||||
|
return getpass.getpass(), getpass.getpass('Retype password:')
|
||||||
|
|
||||||
|
|
||||||
def user_info_server():
|
def user_info_server():
|
||||||
print("NOTE: Configuring authentication for SERVER mode.\n")
|
print("NOTE: Configuring authentication for SERVER mode.\n")
|
||||||
|
|
||||||
@ -45,13 +50,10 @@ def user_info_server():
|
|||||||
"pgAdmin user account:\n"
|
"pgAdmin user account:\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
email = input("Email address: ")
|
email = input(ENTER_EMAIL_ADDRESS)
|
||||||
while not validate_email(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(ENTER_EMAIL_ADDRESS)
|
||||||
|
|
||||||
def pprompt():
|
|
||||||
return getpass.getpass(), getpass.getpass('Retype password:')
|
|
||||||
|
|
||||||
p1, p2 = pprompt()
|
p1, p2 = pprompt()
|
||||||
while p1 != p2 or len(p1) < 6:
|
while p1 != p2 or len(p1) < 6:
|
||||||
|
0
web/pgadmin/tools/user_management/tests/__init__.py
Normal file
0
web/pgadmin/tools/user_management/tests/__init__.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
from pgadmin.tools.user_management import validate_user
|
||||||
|
from unittest.mock import patch
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
class TestValidateUser(BaseTestGenerator):
|
||||||
|
""" This class will test the user email validation with/without email
|
||||||
|
deliverability while validating user. """
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
('User email validation (no deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email='postgres@local.dev',
|
||||||
|
check_deliverability=False,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result='postgres@local.dev'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
('User email validation (with deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email='postgres@local.dev',
|
||||||
|
check_deliverability=True,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result='Invalid email address.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
]
|
||||||
|
|
||||||
|
@patch('pgadmin.tools.user_management.validate_password')
|
||||||
|
def runTest(self, validate_password_mock):
|
||||||
|
|
||||||
|
if config.SERVER_MODE is False:
|
||||||
|
self.skipTest(
|
||||||
|
"Can not email validation test cases in the DESKTOP mode."
|
||||||
|
)
|
||||||
|
config.CHECK_EMAIL_DELIVERABILITY = self.data['check_deliverability']
|
||||||
|
ndata = {}
|
||||||
|
|
||||||
|
validate_password_mock.return_value.method.return_value = ''
|
||||||
|
try:
|
||||||
|
ndata = validate_user(self.data)
|
||||||
|
except Exception as e:
|
||||||
|
ndata['email'] = str(e.description)
|
||||||
|
|
||||||
|
# assert equal means deliverability is not done, and entered
|
||||||
|
# email id is returned as it is.
|
||||||
|
self.assertEqual(ndata['email'],
|
||||||
|
self.data['expected_data']['test_result'])
|
@ -98,3 +98,5 @@ BINARY_PATHS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILITIES_ARRAY = ['pg_dump', 'pg_dumpall', 'pg_restore', 'psql']
|
UTILITIES_ARRAY = ['pg_dump', 'pg_dumpall', 'pg_restore', 'psql']
|
||||||
|
|
||||||
|
ENTER_EMAIL_ADDRESS = "Email address: "
|
||||||
|
82
web/pgadmin/utils/tests/test_validate_email.py
Normal file
82
web/pgadmin/utils/tests/test_validate_email.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2021, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
from pgadmin.utils.validation_utils import validate_email
|
||||||
|
from unittest.mock import patch
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
class TestEmailValidate(BaseTestGenerator):
|
||||||
|
""" This class will test the email validation utility with or without email
|
||||||
|
deliverability. """
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
('Email validation (no deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email_list=['postgres@local.dev', 'pg@pgadminrocks.com',
|
||||||
|
'me.pg@demo.dev', 'pg@123.pgcom',
|
||||||
|
'pg@postgres.local', 'postgres@pg.blah',
|
||||||
|
'john@doe.com', 'punster@tr.co',
|
||||||
|
'admin@example.com'],
|
||||||
|
check_deliverability=False,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
('Email validation (with deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email_list=['postgres@local.dev', 'pg@pgadminrocks.com',
|
||||||
|
'pg@postgres.local'],
|
||||||
|
check_deliverability=True,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
('Empty email validation (no deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email_list=[''],
|
||||||
|
check_deliverability=False,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
('Empty email validation (with deliverability)',
|
||||||
|
dict(
|
||||||
|
data=dict(
|
||||||
|
email_list=[''],
|
||||||
|
check_deliverability=True,
|
||||||
|
expected_data=dict(
|
||||||
|
test_result=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
]
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
|
||||||
|
if config.SERVER_MODE is False:
|
||||||
|
self.skipTest(
|
||||||
|
"Can not run email validation test cases in the DESKTOP mode."
|
||||||
|
)
|
||||||
|
config.CHECK_EMAIL_DELIVERABILITY = self.data['check_deliverability']
|
||||||
|
|
||||||
|
for e in self.data['email_list']:
|
||||||
|
result = validate_email(e)
|
||||||
|
# validate_email returns True if email is valid,
|
||||||
|
# even if non-deliverable. False if email is not valid or
|
||||||
|
# deliverability is turned ON.
|
||||||
|
self.assertEqual(result,
|
||||||
|
self.data['expected_data']['test_result'])
|
@ -7,20 +7,21 @@
|
|||||||
#
|
#
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
import re
|
from email_validator import validate_email as email_validate, \
|
||||||
|
EmailNotValidError
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
def validate_email(email):
|
def validate_email(email):
|
||||||
if email == '' or email is None:
|
try:
|
||||||
|
# Validate.
|
||||||
|
valid = email_validate(
|
||||||
|
email, check_deliverability=config.CHECK_EMAIL_DELIVERABILITY)
|
||||||
|
|
||||||
|
# Update with the normalized form.
|
||||||
|
email = valid.email
|
||||||
|
return True
|
||||||
|
except EmailNotValidError as e:
|
||||||
|
# email is not valid, exception message is human-readable
|
||||||
|
print(str(e))
|
||||||
return False
|
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
|
|
||||||
|
@ -17,6 +17,7 @@ import sqlite3
|
|||||||
import shutil
|
import shutil
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import random
|
import random
|
||||||
|
import importlib
|
||||||
|
|
||||||
from selenium.webdriver.support.wait import WebDriverWait
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
from testtools.testcase import clone_test_with_new_id
|
from testtools.testcase import clone_test_with_new_id
|
||||||
@ -1747,3 +1748,34 @@ def create_users_for_parallel_tests(tester):
|
|||||||
user_id = json.loads(response.data.decode('utf-8'))['id']
|
user_id = json.loads(response.data.decode('utf-8'))['id']
|
||||||
user_details['user_id'] = user_id
|
user_details['user_id'] = user_id
|
||||||
return user_details
|
return user_details
|
||||||
|
|
||||||
|
|
||||||
|
def module_patch(*args):
|
||||||
|
"""
|
||||||
|
This is a helper function responsible to import a function inside
|
||||||
|
a module with the same name
|
||||||
|
|
||||||
|
e.g. user_info module has user_info function in it.
|
||||||
|
|
||||||
|
:param args:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
target = args[0]
|
||||||
|
components = target.split('.')
|
||||||
|
from unittest import mock
|
||||||
|
for i in range(len(components), 0, -1):
|
||||||
|
try:
|
||||||
|
# attempt to import the module
|
||||||
|
imported = importlib.import_module('.'.join(components[:i]))
|
||||||
|
|
||||||
|
# module was imported, let's use it in the patch
|
||||||
|
patch = mock.patch(*args)
|
||||||
|
patch.getter = lambda: imported
|
||||||
|
patch.attribute = '.'.join(components[i:])
|
||||||
|
return patch
|
||||||
|
except Exception as exc:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# did not find a module, just return the default mock
|
||||||
|
return mock.patch(*args)
|
||||||
|
Loading…
Reference in New Issue
Block a user