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:
Rahul Shirsat 2021-07-05 12:55:40 +05:30 committed by Akshay Joshi
parent ef67409d61
commit 9fdda038a9
10 changed files with 266 additions and 19 deletions

View File

@ -388,7 +388,9 @@ SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE = \
# flask-security-too will validate email addresses and check deliverability
# by default. Disable the deliverability check by default, which was the old
# 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

View File

@ -465,6 +465,8 @@ def create_app(app_name=None):
# CSRF Token expiration till session expires
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
'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)

View 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()

View File

@ -13,6 +13,7 @@ import random
import os
import re
import getpass
from pgadmin.utils.constants import ENTER_EMAIL_ADDRESS
from pgadmin.utils.validation_utils import validate_email
@ -27,6 +28,10 @@ def user_info_desktop():
return email, p1
def pprompt():
return getpass.getpass(), getpass.getpass('Retype password:')
def user_info_server():
print("NOTE: Configuring authentication for SERVER mode.\n")
@ -45,13 +50,10 @@ def user_info_server():
"pgAdmin user account:\n"
)
email = input("Email address: ")
email = input(ENTER_EMAIL_ADDRESS)
while not validate_email(email):
print('Invalid email address. Please try again.')
email = input("Email address: ")
def pprompt():
return getpass.getpass(), getpass.getpass('Retype password:')
email = input(ENTER_EMAIL_ADDRESS)
p1, p2 = pprompt()
while p1 != p2 or len(p1) < 6:

View 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'])

View File

@ -98,3 +98,5 @@ BINARY_PATHS = {
}
UTILITIES_ARRAY = ['pg_dump', 'pg_dumpall', 'pg_restore', 'psql']
ENTER_EMAIL_ADDRESS = "Email address: "

View 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'])

View File

@ -7,20 +7,21 @@
#
##########################################################################
import re
from email_validator import validate_email as email_validate, \
EmailNotValidError
import config
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
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

View File

@ -17,6 +17,7 @@ import sqlite3
import shutil
from functools import partial
import random
import importlib
from selenium.webdriver.support.wait import WebDriverWait
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_details['user_id'] = user_id
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)