Skip to content

Commit fd2d9b7

Browse files
authored
Purge non-verified user (#3417)
* use SQL's NOW() fct to log creation of a new user account (validated or not). * at -> on * add new col * adding a new page for admins to easily list all users not yet validated and offer means to delete those * flake8 * fix tests * move DB changes to patch * revert file * no change here * execute merge manually * moving data insertion to test_db_sql patch * account for additional test users
1 parent 8b3f717 commit fd2d9b7

File tree

10 files changed

+129
-10
lines changed

10 files changed

+129
-10
lines changed

qiita_db/handlers/tests/test_user.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ def test_get(self):
5454
{'email': '[email protected]', 'name': 'Shared'},
5555
{'email': '[email protected]', 'name': 'Admin'},
5656
{'email': '[email protected]', 'name': 'Demo'},
57-
{'email': '[email protected]', 'name': 'Dude'}
57+
{'email': '[email protected]', 'name': 'Dude'},
58+
{'email': '[email protected]', 'name': 'JustNow'},
59+
{'email': '[email protected]', 'name': 'Oldie'},
60+
{'email': '[email protected]', 'name': 'TooLate'}
5861
]}
5962
self.assertEqual(obs, exp)
6063

qiita_db/support_files/patches/92.sql

-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,3 @@ ALTER TABLE qiita.qiita_user
3939
ADD creation_timestamp timestamp without time zone DEFAULT NOW();
4040

4141
COMMENT ON COLUMN qiita.qiita_user.creation_timestamp IS 'The date the user account was created';
42-
43-
-- for testing: provide creation date for one of the existing users
44-
45-
UPDATE qiita.qiita_user SET creation_timestamp = '2015-12-03 13:52:42.751331-07' WHERE email = '[email protected]';

qiita_db/support_files/patches/test_db_sql/92.sql

+12-1
Original file line numberDiff line numberDiff line change
@@ -925,4 +925,15 @@ INSERT INTO qiita.slurm_resource_allocations(processing_job_id, samples, columns
925925
('61da73ff-b4ff-49a1-b775-c6215cfbd291', 231, 107, 2, 'nan', 333544000, 200, '2023-02-17T15:05:17', NULL, NULL),
926926
('6c84dcf1-c5ea-4e69-b17f-d2d5b8d48bdf', 123, 50, 2, 'nan', 327520000, 82, '2023-02-18T15:13:15', NULL, NULL),
927927
('dcb12603-4142-44d1-9a52-3ca3511e380e', 320, 44, 2, 'nan', 329448000, 475, '2023-02-19T06:29:32', NULL, NULL),
928-
('a0dd0a4d-b73f-4e9d-87dd-d29efba25336', 41, 50, 2, 'nan', 301108000, 144, '2023-02-19T09:14:27', NULL, NULL);
928+
('a0dd0a4d-b73f-4e9d-87dd-d29efba25336', 41, 50, 2, 'nan', 301108000, 144, '2023-02-19T09:14:27', NULL, NULL);
929+
930+
-- for testing: provide creation date for one of the existing users
931+
932+
UPDATE qiita.qiita_user SET creation_timestamp = '2015-12-03 13:52:42.751331-07' WHERE email = '[email protected]';
933+
934+
-- Jun 20, 2024
935+
-- Add some non-verified users to the test DB to test new admin page: /admin/purge_users/
936+
937+
INSERT INTO qiita.qiita_user VALUES ('[email protected]', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'JustNow', 'NonVeriUser', '1634 Edgemont Avenue', '303-492-1984', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW());
938+
INSERT INTO qiita.qiita_user VALUES ('[email protected]', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Oldie', 'NonVeriUser', '172 New Lane', '102-111-1984', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW() - INTERVAL '1 YEAR');
939+
INSERT INTO qiita.qiita_user VALUES ('[email protected]', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'TooLate', 'NonVeriUser', '564 C Street', '508-492-222', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW() - INTERVAL '30 DAY');

qiita_db/test/test_meta_util.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def _get_daily_stats():
281281
self.assertDictEqual(f(redis_key), exp)
282282
# then the unique values
283283
vals = [
284-
('num_users', b'4', r_client.get),
284+
('num_users', b'7', r_client.get),
285285
('lat_longs', b'[]', r_client.get),
286286
('num_studies_ebi', b'1', r_client.get),
287287
('num_samples_ebi', b'27', r_client.get),

qiita_db/test/test_portal.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ def test_add_portal(self):
4747
qdb.sql_connection.TRN.add("SELECT * FROM qiita.analysis_portal")
4848
obs = qdb.sql_connection.TRN.execute_fetchindex()
4949
exp = [[1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 2], [8, 2],
50-
[9, 2], [10, 2], [11, 4], [12, 4], [13, 4], [14, 4]]
50+
[9, 2], [10, 2], [11, 4], [12, 4], [13, 4], [14, 4],
51+
[15, 4], [16, 4], [17, 4]]
5152
self.assertCountEqual(obs, exp)
5253

5354
with self.assertRaises(qdb.exceptions.QiitaDBDuplicateError):

qiita_db/test/test_setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class SetupTest(TestCase):
1515
"""Tests that the test database have been successfully populated"""
1616

1717
def test_qiita_user(self):
18-
self.assertEqual(get_count("qiita.qiita_user"), 4)
18+
self.assertEqual(get_count("qiita.qiita_user"), 7)
1919

2020
def test_study_person(self):
2121
self.assertEqual(get_count("qiita.study_person"), 3)

qiita_pet/handlers/user_handlers.py

+74
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
# -----------------------------------------------------------------------------
88

99
import re
10+
from json import dumps
11+
import warnings
1012

1113
from tornado.web import authenticated, HTTPError
1214
from wtforms import Form, StringField, BooleanField, validators
1315
from wtforms.validators import ValidationError
1416

1517
from qiita_pet.handlers.base_handlers import BaseHandler
1618
from qiita_pet.handlers.api_proxy import user_jobs_get_req
19+
from qiita_pet.handlers.portal import PortalEditBase
20+
import qiita_db as qdb
1721
from qiita_db.util import send_email
1822
from qiita_db.user import User
1923
from qiita_db.logger import LogEntry
@@ -375,3 +379,73 @@ class UserJobs(BaseHandler):
375379
def get(self):
376380
response = user_jobs_get_req(self.current_user)
377381
self.write(response)
382+
383+
384+
class PurgeUsersAJAXHandler(PortalEditBase):
385+
# define columns besides email that will be displayed on website
386+
FIELDS = ['name', 'affiliation', 'address', 'phone',
387+
'creation_timestamp']
388+
389+
@authenticated
390+
@execute_as_transaction
391+
def get(self):
392+
# retrieving users not yet verified
393+
self.check_admin()
394+
with qdb.sql_connection.TRN:
395+
sql = """SELECT email,{0}
396+
FROM qiita.qiita_user
397+
WHERE (user_level_id=5) AND
398+
(creation_timestamp < (NOW() - INTERVAL '30 DAY'))
399+
""".format(','.join(self.FIELDS))
400+
qdb.sql_connection.TRN.add(sql)
401+
users = qdb.sql_connection.TRN.execute()[1:]
402+
403+
# fetching information for each user
404+
result = []
405+
for list in users:
406+
for user in list:
407+
usermail = user[0]
408+
user_unit = {'email': usermail}
409+
user_infos = User(usermail).info
410+
for col in self.FIELDS:
411+
user_unit[col] = str(user_infos[col])
412+
result.append(user_unit)
413+
# returning information as JSON
414+
self.write(dumps(result, separators=(',', ':')))
415+
416+
417+
class PurgeUsersHandler(PortalEditBase):
418+
@authenticated
419+
@execute_as_transaction
420+
def get(self):
421+
# render page and transfer headers to be included for the table
422+
self.check_admin()
423+
self.render('admin_purge_users.html',
424+
headers=['email'] + PurgeUsersAJAXHandler.FIELDS,
425+
submit_url="/admin/purge_users/")
426+
427+
def post(self):
428+
# check if logged in user is admin and fetch all checked boxes as well
429+
# as the action
430+
self.check_admin()
431+
users = map(str, self.get_arguments('selected'))
432+
action = self.get_argument('action')
433+
434+
# depending on the action delete user from db (remove)
435+
num_deleted_user = 0
436+
for user in users:
437+
try:
438+
with warnings.catch_warnings(record=True) as warns:
439+
if action == "Remove":
440+
user_to_delete = User(user)
441+
user_to_delete.delete(user)
442+
num_deleted_user += 1
443+
else:
444+
raise HTTPError(
445+
400, reason="Unknown action: %s" % action)
446+
except QiitaDBError as e:
447+
self.write(action.upper() + " ERROR:<br/>" + str(e))
448+
return
449+
msg = '; '.join([str(w.message) for w in warns])
450+
self.write(("%i non-validated user(s) successfully removed from "
451+
"database<br/>%s") % (num_deleted_user, msg))

qiita_pet/templates/sitebase.html

+1
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@
383383
<li><a href="{% raw qiita_config.portal_dir %}/admin/error/">View Errors</a></li>
384384
<li><a href="{% raw qiita_config.portal_dir %}/admin/approval/">View Studies awaiting approval</a></li>
385385
<li><a href="{% raw qiita_config.portal_dir %}/admin/portals/studies/">Edit study portal connections</a></li>
386+
<li><a href="{% raw qiita_config.portal_dir %}/admin/purge_users/">Purge non-validated users</a></li>
386387
{% end %}
387388
<li><a href="{% raw qiita_config.portal_dir %}/admin/sample_validation/">Sample Validation</a></li>
388389
<li><a href="{% raw qiita_config.portal_dir %}/admin/processing_jobs/">Processing Jobs</a></li>

qiita_pet/test/test_user_handlers.py

+31
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
from unittest import main
1010
from wtforms.validators import ValidationError
1111
from wtforms import StringField
12+
from mock import Mock
13+
from json import loads
1214

1315
from qiita_pet.test.tornado_test_base import TestHandlerBase
1416
from qiita_pet.handlers.user_handlers import UserProfile
17+
from qiita_pet.handlers.base_handlers import BaseHandler
18+
from qiita_db.user import User
1519

1620

1721
class TestUserProfile(TestHandlerBase):
@@ -124,5 +128,32 @@ def test_get(self):
124128
self.assertEqual(response.code, 200)
125129

126130

131+
class TestPurgeUsersAJAXHandler(TestHandlerBase):
132+
def setUp(self):
133+
super().setUp()
134+
BaseHandler.get_current_user = Mock(return_value=User("[email protected]"))
135+
136+
def test_get(self):
137+
response = self.get('/admin/purge_usersAjax/?_=1718805487494')
138+
obs_users_table = loads(response.body.decode('ascii'))
139+
obs_users = {user['email'] for user in obs_users_table}
140+
self.assertIn('[email protected]', obs_users)
141+
self.assertIn('[email protected]', obs_users)
142+
self.assertNotIn('[email protected]', obs_users)
143+
144+
def test_post_removeBoth(self):
145+
# remove both users
146+
response = self.post('/admin/purge_users/',
147+
{'action': 'Remove',
148+
'selected': ['[email protected]',
149+
150+
self.assertEqual(response.code, 200)
151+
152+
# test that zero users are listed now
153+
response = self.get('/admin/purge_usersAjax/?_=1718805487495')
154+
obs_users_table = loads(response.body.decode('ascii'))
155+
self.assertEqual(obs_users_table, [])
156+
157+
127158
if __name__ == "__main__":
128159
main()

qiita_pet/webserver.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
AuthCreateHandler, AuthLoginHandler, AuthLogoutHandler, AuthVerifyHandler)
2424
from qiita_pet.handlers.user_handlers import (
2525
ChangeForgotPasswordHandler, ForgotPasswordHandler, UserProfileHandler,
26-
UserMessagesHander, UserJobs)
26+
UserMessagesHander, UserJobs, PurgeUsersAJAXHandler, PurgeUsersHandler)
2727
from qiita_pet.handlers.admin_processing_job import (
2828
AdminProcessingJob, AJAXAdminProcessingJobListing, SampleValidation)
2929
from qiita_pet.handlers.analysis_handlers import (
@@ -134,6 +134,8 @@ def __init__(self):
134134
(r"/admin/processing_jobs/", AdminProcessingJob),
135135
(r"/admin/processing_jobs/list", AJAXAdminProcessingJobListing),
136136
(r"/admin/sample_validation/", SampleValidation),
137+
(r"/admin/purge_users/", PurgeUsersHandler),
138+
(r"/admin/purge_usersAjax/", PurgeUsersAJAXHandler),
137139
(r"/ebi_submission/(.*)", EBISubmitHandler),
138140
# Study handlers
139141
(r"/study/create/", StudyEditHandler),

0 commit comments

Comments
 (0)