Skip to content

Commit

Permalink
Purge non-verified user (#3417)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sjanssen2 authored Jun 20, 2024
1 parent 8b3f717 commit fd2d9b7
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 10 deletions.
5 changes: 4 additions & 1 deletion qiita_db/handlers/tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ def test_get(self):
{'email': 'shared@foo.bar', 'name': 'Shared'},
{'email': 'admin@foo.bar', 'name': 'Admin'},
{'email': 'demo@microbio.me', 'name': 'Demo'},
{'email': 'test@foo.bar', 'name': 'Dude'}
{'email': 'test@foo.bar', 'name': 'Dude'},
{'email': 'justnow@nonvalidat.ed', 'name': 'JustNow'},
{'email': 'ayearago@nonvalidat.ed', 'name': 'Oldie'},
{'email': '3Xdays@nonvalidat.ed', 'name': 'TooLate'}
]}
self.assertEqual(obs, exp)

Expand Down
4 changes: 0 additions & 4 deletions qiita_db/support_files/patches/92.sql
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,3 @@ ALTER TABLE qiita.qiita_user
ADD creation_timestamp timestamp without time zone DEFAULT NOW();

COMMENT ON COLUMN qiita.qiita_user.creation_timestamp IS 'The date the user account was created';

-- for testing: provide creation date for one of the existing users

UPDATE qiita.qiita_user SET creation_timestamp = '2015-12-03 13:52:42.751331-07' WHERE email = 'test@foo.bar';
13 changes: 12 additions & 1 deletion qiita_db/support_files/patches/test_db_sql/92.sql
Original file line number Diff line number Diff line change
Expand Up @@ -925,4 +925,15 @@ INSERT INTO qiita.slurm_resource_allocations(processing_job_id, samples, columns
('61da73ff-b4ff-49a1-b775-c6215cfbd291', 231, 107, 2, 'nan', 333544000, 200, '2023-02-17T15:05:17', NULL, NULL),
('6c84dcf1-c5ea-4e69-b17f-d2d5b8d48bdf', 123, 50, 2, 'nan', 327520000, 82, '2023-02-18T15:13:15', NULL, NULL),
('dcb12603-4142-44d1-9a52-3ca3511e380e', 320, 44, 2, 'nan', 329448000, 475, '2023-02-19T06:29:32', NULL, NULL),
('a0dd0a4d-b73f-4e9d-87dd-d29efba25336', 41, 50, 2, 'nan', 301108000, 144, '2023-02-19T09:14:27', NULL, NULL);
('a0dd0a4d-b73f-4e9d-87dd-d29efba25336', 41, 50, 2, 'nan', 301108000, 144, '2023-02-19T09:14:27', NULL, NULL);

-- for testing: provide creation date for one of the existing users

UPDATE qiita.qiita_user SET creation_timestamp = '2015-12-03 13:52:42.751331-07' WHERE email = 'test@foo.bar';

-- Jun 20, 2024
-- Add some non-verified users to the test DB to test new admin page: /admin/purge_users/

INSERT INTO qiita.qiita_user VALUES ('justnow@nonvalidat.ed', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'JustNow', 'NonVeriUser', '1634 Edgemont Avenue', '303-492-1984', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW());
INSERT INTO qiita.qiita_user VALUES ('ayearago@nonvalidat.ed', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Oldie', 'NonVeriUser', '172 New Lane', '102-111-1984', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW() - INTERVAL '1 YEAR');
INSERT INTO qiita.qiita_user VALUES ('3Xdays@nonvalidat.ed', 5, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'TooLate', 'NonVeriUser', '564 C Street', '508-492-222', NULL, NULL, NULL, false, NULL, NULL, NULL, NOW() - INTERVAL '30 DAY');
2 changes: 1 addition & 1 deletion qiita_db/test/test_meta_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def _get_daily_stats():
self.assertDictEqual(f(redis_key), exp)
# then the unique values
vals = [
('num_users', b'4', r_client.get),
('num_users', b'7', r_client.get),
('lat_longs', b'[]', r_client.get),
('num_studies_ebi', b'1', r_client.get),
('num_samples_ebi', b'27', r_client.get),
Expand Down
3 changes: 2 additions & 1 deletion qiita_db/test/test_portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ def test_add_portal(self):
qdb.sql_connection.TRN.add("SELECT * FROM qiita.analysis_portal")
obs = qdb.sql_connection.TRN.execute_fetchindex()
exp = [[1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 2], [8, 2],
[9, 2], [10, 2], [11, 4], [12, 4], [13, 4], [14, 4]]
[9, 2], [10, 2], [11, 4], [12, 4], [13, 4], [14, 4],
[15, 4], [16, 4], [17, 4]]
self.assertCountEqual(obs, exp)

with self.assertRaises(qdb.exceptions.QiitaDBDuplicateError):
Expand Down
2 changes: 1 addition & 1 deletion qiita_db/test/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SetupTest(TestCase):
"""Tests that the test database have been successfully populated"""

def test_qiita_user(self):
self.assertEqual(get_count("qiita.qiita_user"), 4)
self.assertEqual(get_count("qiita.qiita_user"), 7)

def test_study_person(self):
self.assertEqual(get_count("qiita.study_person"), 3)
Expand Down
74 changes: 74 additions & 0 deletions qiita_pet/handlers/user_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
# -----------------------------------------------------------------------------

import re
from json import dumps
import warnings

from tornado.web import authenticated, HTTPError
from wtforms import Form, StringField, BooleanField, validators
from wtforms.validators import ValidationError

from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_pet.handlers.api_proxy import user_jobs_get_req
from qiita_pet.handlers.portal import PortalEditBase
import qiita_db as qdb
from qiita_db.util import send_email
from qiita_db.user import User
from qiita_db.logger import LogEntry
Expand Down Expand Up @@ -375,3 +379,73 @@ class UserJobs(BaseHandler):
def get(self):
response = user_jobs_get_req(self.current_user)
self.write(response)


class PurgeUsersAJAXHandler(PortalEditBase):
# define columns besides email that will be displayed on website
FIELDS = ['name', 'affiliation', 'address', 'phone',
'creation_timestamp']

@authenticated
@execute_as_transaction
def get(self):
# retrieving users not yet verified
self.check_admin()
with qdb.sql_connection.TRN:
sql = """SELECT email,{0}
FROM qiita.qiita_user
WHERE (user_level_id=5) AND
(creation_timestamp < (NOW() - INTERVAL '30 DAY'))
""".format(','.join(self.FIELDS))
qdb.sql_connection.TRN.add(sql)
users = qdb.sql_connection.TRN.execute()[1:]

# fetching information for each user
result = []
for list in users:
for user in list:
usermail = user[0]
user_unit = {'email': usermail}
user_infos = User(usermail).info
for col in self.FIELDS:
user_unit[col] = str(user_infos[col])
result.append(user_unit)
# returning information as JSON
self.write(dumps(result, separators=(',', ':')))


class PurgeUsersHandler(PortalEditBase):
@authenticated
@execute_as_transaction
def get(self):
# render page and transfer headers to be included for the table
self.check_admin()
self.render('admin_purge_users.html',
headers=['email'] + PurgeUsersAJAXHandler.FIELDS,
submit_url="/admin/purge_users/")

def post(self):
# check if logged in user is admin and fetch all checked boxes as well
# as the action
self.check_admin()
users = map(str, self.get_arguments('selected'))
action = self.get_argument('action')

# depending on the action delete user from db (remove)
num_deleted_user = 0
for user in users:
try:
with warnings.catch_warnings(record=True) as warns:
if action == "Remove":
user_to_delete = User(user)
user_to_delete.delete(user)
num_deleted_user += 1
else:
raise HTTPError(
400, reason="Unknown action: %s" % action)
except QiitaDBError as e:
self.write(action.upper() + " ERROR:<br/>" + str(e))
return
msg = '; '.join([str(w.message) for w in warns])
self.write(("%i non-validated user(s) successfully removed from "
"database<br/>%s") % (num_deleted_user, msg))
1 change: 1 addition & 0 deletions qiita_pet/templates/sitebase.html
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@
<li><a href="{% raw qiita_config.portal_dir %}/admin/error/">View Errors</a></li>
<li><a href="{% raw qiita_config.portal_dir %}/admin/approval/">View Studies awaiting approval</a></li>
<li><a href="{% raw qiita_config.portal_dir %}/admin/portals/studies/">Edit study portal connections</a></li>
<li><a href="{% raw qiita_config.portal_dir %}/admin/purge_users/">Purge non-validated users</a></li>
{% end %}
<li><a href="{% raw qiita_config.portal_dir %}/admin/sample_validation/">Sample Validation</a></li>
<li><a href="{% raw qiita_config.portal_dir %}/admin/processing_jobs/">Processing Jobs</a></li>
Expand Down
31 changes: 31 additions & 0 deletions qiita_pet/test/test_user_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
from unittest import main
from wtforms.validators import ValidationError
from wtforms import StringField
from mock import Mock
from json import loads

from qiita_pet.test.tornado_test_base import TestHandlerBase
from qiita_pet.handlers.user_handlers import UserProfile
from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_db.user import User


class TestUserProfile(TestHandlerBase):
Expand Down Expand Up @@ -124,5 +128,32 @@ def test_get(self):
self.assertEqual(response.code, 200)


class TestPurgeUsersAJAXHandler(TestHandlerBase):
def setUp(self):
super().setUp()
BaseHandler.get_current_user = Mock(return_value=User("admin@foo.bar"))

def test_get(self):
response = self.get('/admin/purge_usersAjax/?_=1718805487494')
obs_users_table = loads(response.body.decode('ascii'))
obs_users = {user['email'] for user in obs_users_table}
self.assertIn('ayearago@nonvalidat.ed', obs_users)
self.assertIn('3Xdays@nonvalidat.ed', obs_users)
self.assertNotIn('justnow@nonvalidat.ed', obs_users)

def test_post_removeBoth(self):
# remove both users
response = self.post('/admin/purge_users/',
{'action': 'Remove',
'selected': ['ayearago@nonvalidat.ed',
'3Xdays@nonvalidat.ed']})
self.assertEqual(response.code, 200)

# test that zero users are listed now
response = self.get('/admin/purge_usersAjax/?_=1718805487495')
obs_users_table = loads(response.body.decode('ascii'))
self.assertEqual(obs_users_table, [])


if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
AuthCreateHandler, AuthLoginHandler, AuthLogoutHandler, AuthVerifyHandler)
from qiita_pet.handlers.user_handlers import (
ChangeForgotPasswordHandler, ForgotPasswordHandler, UserProfileHandler,
UserMessagesHander, UserJobs)
UserMessagesHander, UserJobs, PurgeUsersAJAXHandler, PurgeUsersHandler)
from qiita_pet.handlers.admin_processing_job import (
AdminProcessingJob, AJAXAdminProcessingJobListing, SampleValidation)
from qiita_pet.handlers.analysis_handlers import (
Expand Down Expand Up @@ -134,6 +134,8 @@ def __init__(self):
(r"/admin/processing_jobs/", AdminProcessingJob),
(r"/admin/processing_jobs/list", AJAXAdminProcessingJobListing),
(r"/admin/sample_validation/", SampleValidation),
(r"/admin/purge_users/", PurgeUsersHandler),
(r"/admin/purge_usersAjax/", PurgeUsersAJAXHandler),
(r"/ebi_submission/(.*)", EBISubmitHandler),
# Study handlers
(r"/study/create/", StudyEditHandler),
Expand Down

0 comments on commit fd2d9b7

Please sign in to comment.