From 8089cf828664dd88df899809bb00cd031328ac2f Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:40:01 -0500 Subject: [PATCH 01/16] Initial additional SQL for tracking user activity. --- auth/sql/estimated-current.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index e0b6fa52f45..de5301d6875 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -7,6 +7,7 @@ CREATE TABLE `users` ( `display_name` varchar(255) DEFAULT NULL, `is_developer` tinyint(1) NOT NULL DEFAULT 0, `is_service_account` tinyint(1) NOT NULL DEFAULT 0, + `last_active` DATETIME NOT NULL, -- session `tokens_secret_name` varchar(255) DEFAULT NULL, -- identity @@ -21,6 +22,16 @@ CREATE TABLE `users` ( UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB; +CREATE EVENT `disable_inactive` + ON SCHEDULE EVERY 5 MINUTE + ON COMPLETION PRESERVE + DO + DELETE FROM users + WHERE (users.last_active IS NOT NULL) AND (DATEDIFF(day, NOW(), users.last_active) > 180); + -- placeholder: 180 days of inactivity + -- TBD: this is going to be more involved than just deleting users. + -- Updating last_active will be addressed elsewhere (e.g. when one submits a job). + CREATE TABLE `sessions` ( `session_id` VARCHAR(255) NOT NULL, `user_id` INT(11) NOT NULL, From 49f6456ae8369d96332d918a1445f4dad5a0760d Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:21:17 -0500 Subject: [PATCH 02/16] Changed 'deleted' to new 'inactive' state; made placeholder for 'inactive' table in case we want to avoid changing the users table schema. --- auth/sql/estimated-current.sql | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index de5301d6875..1c6ecb7ccff 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -1,13 +1,12 @@ CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `state` VARCHAR(100) NOT NULL, - -- creating, active, deleting, deleted + -- creating, active, deleting, deleted, inactive `username` varchar(255) NOT NULL COLLATE utf8mb4_0900_as_cs, `login_id` varchar(255) DEFAULT NULL COLLATE utf8mb4_0900_as_cs, `display_name` varchar(255) DEFAULT NULL, `is_developer` tinyint(1) NOT NULL DEFAULT 0, `is_service_account` tinyint(1) NOT NULL DEFAULT 0, - `last_active` DATETIME NOT NULL, -- session `tokens_secret_name` varchar(255) DEFAULT NULL, -- identity @@ -17,20 +16,24 @@ CREATE TABLE `users` ( -- namespace, for developers `namespace_name` varchar(255) DEFAULT NULL, `trial_bp_name` varchar(300) DEFAULT NULL, + -- "last active" tracking + `last_active` TIMESTAMP NOT NULL DEFAULT NOW(), PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB; +CREATE TABLE `inactive` ( + +) + CREATE EVENT `disable_inactive` - ON SCHEDULE EVERY 5 MINUTE + ON SCHEDULE EVERY 60 MINUTE ON COMPLETION PRESERVE DO - DELETE FROM users - WHERE (users.last_active IS NOT NULL) AND (DATEDIFF(day, NOW(), users.last_active) > 180); - -- placeholder: 180 days of inactivity - -- TBD: this is going to be more involved than just deleting users. - -- Updating last_active will be addressed elsewhere (e.g. when one submits a job). + UPDATE users + SET users.state = 'inactive' + WHERE (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 180); CREATE TABLE `sessions` ( `session_id` VARCHAR(255) NOT NULL, From 74faa27ede70b33cea419913a3df8b4c49b32f65 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:25:40 -0500 Subject: [PATCH 03/16] rename placeholder table --- auth/sql/estimated-current.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index 1c6ecb7ccff..c7f9217c3ac 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -23,12 +23,12 @@ CREATE TABLE `users` ( UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB; -CREATE TABLE `inactive` ( +CREATE TABLE `users_activity` ( ) CREATE EVENT `disable_inactive` - ON SCHEDULE EVERY 60 MINUTE + ON SCHEDULE EVERY 1 DAY ON COMPLETION PRESERVE DO UPDATE users From f1bfbbf149f5585f1dd2cd9db52c384780a31b9d Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:05:09 -0500 Subject: [PATCH 04/16] Added update for active users; added HTTP errror for inactive users. --- auth/auth/auth.py | 14 +++++++++++++- auth/sql/estimated-current.sql | 4 ---- gear/gear/auth.py | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index c50572db3d5..40ee984940a 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -769,7 +769,7 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st SELECT users.* FROM users INNER JOIN sessions ON users.id = sessions.user_id -WHERE users.state = 'active' AND sessions.session_id = %s AND (ISNULL(sessions.max_age_secs) OR (NOW() < TIMESTAMPADD(SECOND, sessions.max_age_secs, sessions.created))); +WHERE (users.state = 'active' OR users.state = 'inactive') AND sessions.session_id = %s AND (ISNULL(sessions.max_age_secs) OR (NOW() < TIMESTAMPADD(SECOND, sessions.max_age_secs, sessions.created))); """, session_id, 'get_userinfo', @@ -778,6 +778,18 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st if len(users) != 1: return None + + if users[0]['state'] == 'active' and len(users) == 1: + current_uid = users[0]['id'] + await db.execute_update( + """ +UPDATE users +SET last_active = NOW() +WHERE id = %s; +""", + current_uid, + ) + return typing.cast(UserData, users[0]) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index c7f9217c3ac..3e6121fa620 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -23,10 +23,6 @@ CREATE TABLE `users` ( UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB; -CREATE TABLE `users_activity` ( - -) - CREATE EVENT `disable_inactive` ON SCHEDULE EVERY 1 DAY ON COMPLETION PRESERVE diff --git a/gear/gear/auth.py b/gear/gear/auth.py index bfc2498be87..3c86e893f41 100644 --- a/gear/gear/auth.py +++ b/gear/gear/auth.py @@ -65,6 +65,8 @@ async def wrapped(request: web.Request) -> web.StreamResponse: if redirect or (redirect is None and '/api/' not in request.path): raise login_redirect(request) raise web.HTTPUnauthorized() + elif userdata['state'] == 'inactive': + raise web.HTTPUnauthorized() return await fun(request, userdata) return wrapped From 29ca3c3a2748bb1d8a8d0807319a25316235a238 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:07:19 -0500 Subject: [PATCH 05/16] Fixed SQL even to actually check for active users in WHERE --- auth/sql/estimated-current.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index 3e6121fa620..8725a4265e9 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -29,7 +29,7 @@ CREATE EVENT `disable_inactive` DO UPDATE users SET users.state = 'inactive' - WHERE (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 180); + WHERE (users.state = 'active') AND (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 180); CREATE TABLE `sessions` ( `session_id` VARCHAR(255) NOT NULL, From 7d26f4dffdc2b6e1a1b3b3ace01fe28797d9b27c Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:42:12 -0500 Subject: [PATCH 06/16] Changed inactivity timeout period to be 60 days --- auth/sql/estimated-current.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index 8725a4265e9..de766c9317f 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -29,7 +29,7 @@ CREATE EVENT `disable_inactive` DO UPDATE users SET users.state = 'inactive' - WHERE (users.state = 'active') AND (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 180); + WHERE (users.state = 'active') AND (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 60); CREATE TABLE `sessions` ( `session_id` VARCHAR(255) NOT NULL, From 5688e270dd74db2d32bd81f14ddd310138b7dd61 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:26:51 -0500 Subject: [PATCH 07/16] Removed redundant if statement check --- auth/auth/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index 40ee984940a..1664c54dff3 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -779,7 +779,7 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st if len(users) != 1: return None - if users[0]['state'] == 'active' and len(users) == 1: + if users[0]['state'] == 'active': current_uid = users[0]['id'] await db.execute_update( """ From 5d6f3e42ddbc45b8f6c09f562d0eb160aab54fa3 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:43:55 -0500 Subject: [PATCH 08/16] Added SQL, build.yaml modification for database migration --- batch/sql/add-users-last-active.sql | 1 + build.yaml | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 batch/sql/add-users-last-active.sql diff --git a/batch/sql/add-users-last-active.sql b/batch/sql/add-users-last-active.sql new file mode 100644 index 00000000000..99baa9de7d5 --- /dev/null +++ b/batch/sql/add-users-last-active.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN last_active TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; diff --git a/build.yaml b/build.yaml index b3b3eb68d1c..5c146a4f7cf 100644 --- a/build.yaml +++ b/build.yaml @@ -2345,6 +2345,9 @@ steps: - name: fix-mark-job-complete-deadlocks script: /io/sql/fix-mark-job-complete-deadlocks.sql online: true + - name: add-users-last-active + script: /io/sql/add-users-last-active.sql + online: true inputs: - from: /repo/batch/sql to: /io/sql From 727828017c6964a71f3dafaf136834199ca0b731 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:49:34 -0500 Subject: [PATCH 09/16] Change last_active data type to DATETIME --- auth/sql/estimated-current.sql | 2 +- batch/sql/add-users-last-active.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index de766c9317f..285a3d1d3fe 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -17,7 +17,7 @@ CREATE TABLE `users` ( `namespace_name` varchar(255) DEFAULT NULL, `trial_bp_name` varchar(300) DEFAULT NULL, -- "last active" tracking - `last_active` TIMESTAMP NOT NULL DEFAULT NOW(), + `last_active` DATETIME NOT NULL DEFAULT NOW(), PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) diff --git a/batch/sql/add-users-last-active.sql b/batch/sql/add-users-last-active.sql index 99baa9de7d5..79e41210f65 100644 --- a/batch/sql/add-users-last-active.sql +++ b/batch/sql/add-users-last-active.sql @@ -1 +1 @@ -ALTER TABLE users ADD COLUMN last_active TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; +ALTER TABLE users ADD COLUMN last_active DATETIME NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; From 14711cbb527649f69589c4eab5b92cea5f83479e Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:00:39 -0500 Subject: [PATCH 10/16] Changed loop to update inactive users to be Python-side rather than in SQL; renamed last_active to last_activated; updated associated DB migration SQL. --- auth/sql/estimated-current.sql | 10 +--------- batch/batch/driver/main.py | 9 +++++++++ batch/sql/add-users-last-active.sql | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index 285a3d1d3fe..b89ed7487ec 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -17,20 +17,12 @@ CREATE TABLE `users` ( `namespace_name` varchar(255) DEFAULT NULL, `trial_bp_name` varchar(300) DEFAULT NULL, -- "last active" tracking - `last_active` DATETIME NOT NULL DEFAULT NOW(), + `last_activated` DATETIME NOT NULL DEFAULT NOW(), PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB; -CREATE EVENT `disable_inactive` - ON SCHEDULE EVERY 1 DAY - ON COMPLETION PRESERVE - DO - UPDATE users - SET users.state = 'inactive' - WHERE (users.state = 'active') AND (users.last_active IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_active) > 60); - CREATE TABLE `sessions` ( `session_id` VARCHAR(255) NOT NULL, `user_id` INT(11) NOT NULL, diff --git a/batch/batch/driver/main.py b/batch/batch/driver/main.py index ac6f99d756e..3027cc8dffa 100644 --- a/batch/batch/driver/main.py +++ b/batch/batch/driver/main.py @@ -1643,6 +1643,14 @@ async def scheduling_cancelling_bump(app): app['cancel_running_state_changed'].set() +async def update_inactive_users(db: Database): + await db.execute_update(""" +UPDATE users +SET users.state = 'inactive' +WHERE (users.state = 'active') AND (users.last_activated IS NOT NULL) AND (DATEDIFF(CURRENT_DATE(), users.last_activated) > 60); +""") + + Resource = namedtuple('Resource', ['resource_id', 'deduped_resource_id']) @@ -1754,6 +1762,7 @@ async def close_and_wait(): task_manager.ensure_future(periodically_call(60, compact_agg_billing_project_users_by_date_table, app, db)) task_manager.ensure_future(periodically_call(60, delete_committed_job_groups_inst_coll_staging_records, db)) task_manager.ensure_future(periodically_call(60, delete_prev_cancelled_job_group_cancellable_resources_records, db)) + task_manager.ensure_future(periodically_call(86400, update_inactive_users, db)) # 86400 seconds = 1 day async def on_cleanup(app): diff --git a/batch/sql/add-users-last-active.sql b/batch/sql/add-users-last-active.sql index 79e41210f65..9d2d9943ae2 100644 --- a/batch/sql/add-users-last-active.sql +++ b/batch/sql/add-users-last-active.sql @@ -1 +1 @@ -ALTER TABLE users ADD COLUMN last_active DATETIME NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; +ALTER TABLE users ADD COLUMN last_activated DATETIME NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; From f353d771badb51fed3e8508944221ce41e131106 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:12:06 -0500 Subject: [PATCH 11/16] Refactor in auth/auth.py --- auth/auth/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index 1664c54dff3..f799d97204f 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -784,7 +784,7 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st await db.execute_update( """ UPDATE users -SET last_active = NOW() +SET last_activated = NOW() WHERE id = %s; """, current_uid, From 33378a6ff920b6efcba253a7a00be8d3ffed93e0 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:41:29 -0500 Subject: [PATCH 12/16] changed NOW() to UTC_TIMESTAMP(3) for precision and region agnosticism. --- auth/auth/auth.py | 2 +- auth/sql/estimated-current.sql | 4 ++-- batch/sql/add-users-last-active.sql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index f799d97204f..8cdd22aa273 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -784,7 +784,7 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st await db.execute_update( """ UPDATE users -SET last_activated = NOW() +SET last_activated = UTC_TIMESTAMP(3) WHERE id = %s; """, current_uid, diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index b89ed7487ec..2cee6fd8421 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -16,8 +16,8 @@ CREATE TABLE `users` ( -- namespace, for developers `namespace_name` varchar(255) DEFAULT NULL, `trial_bp_name` varchar(300) DEFAULT NULL, - -- "last active" tracking - `last_activated` DATETIME NOT NULL DEFAULT NOW(), + -- "last activated" tracking + `last_activated` DATETIME NOT NULL DEFAULT UTC_TIMESTAMP(3), PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) diff --git a/batch/sql/add-users-last-active.sql b/batch/sql/add-users-last-active.sql index 9d2d9943ae2..23f9234fe28 100644 --- a/batch/sql/add-users-last-active.sql +++ b/batch/sql/add-users-last-active.sql @@ -1 +1 @@ -ALTER TABLE users ADD COLUMN last_activated DATETIME NOT NULL DEFAULT NOW(), ALGORITHM=INSTANT; +ALTER TABLE users ADD COLUMN last_activated DATETIME NOT NULL DEFAULT UTC_TIMESTAMP(3), ALGORITHM=INSTANT; From 23b9b23472126fab826bb2e713550a5653c1bc10 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:09:18 -0500 Subject: [PATCH 13/16] Type change for default last_activated field --- auth/sql/estimated-current.sql | 2 +- batch/sql/add-users-last-active.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/sql/estimated-current.sql b/auth/sql/estimated-current.sql index 2cee6fd8421..bcbea3047df 100644 --- a/auth/sql/estimated-current.sql +++ b/auth/sql/estimated-current.sql @@ -17,7 +17,7 @@ CREATE TABLE `users` ( `namespace_name` varchar(255) DEFAULT NULL, `trial_bp_name` varchar(300) DEFAULT NULL, -- "last activated" tracking - `last_activated` DATETIME NOT NULL DEFAULT UTC_TIMESTAMP(3), + `last_activated` DATETIME NOT NULL DEFAULT NOW(3), PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), UNIQUE KEY `username` (`username`) diff --git a/batch/sql/add-users-last-active.sql b/batch/sql/add-users-last-active.sql index 23f9234fe28..cf14cc23acb 100644 --- a/batch/sql/add-users-last-active.sql +++ b/batch/sql/add-users-last-active.sql @@ -1 +1 @@ -ALTER TABLE users ADD COLUMN last_activated DATETIME NOT NULL DEFAULT UTC_TIMESTAMP(3), ALGORITHM=INSTANT; +ALTER TABLE users ADD COLUMN last_activated DATETIME NOT NULL DEFAULT NOW(3), ALGORITHM=INSTANT; From af186e45c2b367f38d5e2b66a919716ce78459b7 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:56:58 -0500 Subject: [PATCH 14/16] Added descriptive error message for inactive user case in auth.py --- gear/gear/auth.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gear/gear/auth.py b/gear/gear/auth.py index 3c86e893f41..d3a239ac69e 100644 --- a/gear/gear/auth.py +++ b/gear/gear/auth.py @@ -66,7 +66,9 @@ async def wrapped(request: web.Request) -> web.StreamResponse: raise login_redirect(request) raise web.HTTPUnauthorized() elif userdata['state'] == 'inactive': - raise web.HTTPUnauthorized() + raise web.HTTPUnauthorized( + text="Account is inactive. Please contact a Hail administrator to reactivate." + ) return await fun(request, userdata) return wrapped From 9f4399f2b7deb09e130b195aa7f4bec240c85a66 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:18:51 -0500 Subject: [PATCH 15/16] added developer reactivation functionality --- auth/auth/auth.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index 8cdd22aa273..52d2818bc7f 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -641,6 +641,59 @@ async def rest_delete_user(request: web.Request, _) -> web.Response: return web.json_response() +async def _activate_user(db: Database, username: str, id: Optional[str]): + where_conditions = ['state = "inactive"', 'username = %s'] + where_args = [username] + + if id is not None: + where_conditions.append('id = %s') + where_args.append(id) + + n_rows = await db.execute_update( + f""" +UPDATE users +SET state = 'active', last_activated = NOW() +WHERE {' AND '.join(where_conditions)}; +""", + where_args, + ) + + if n_rows == 0: + raise UnknownUser(username) + + +@routes.post('/users/activate') +@auth.authenticated_developers_only() +async def activate_user(request: web.Request, _) -> NoReturn: + session = await aiohttp_session.get_session(request) + db = request.app[AppKeys.DB] + post = await request.post() + id = str(post['id']) + username = str(post['username']) + + try: + await _activate_user(db, username, id) + set_message(session, f'Reactivated user {id} {username}.', 'info') + except UnknownUser: + set_message(session, f'Reactivation failed, no such user {id} {username}.', 'error') + + raise web.HTTPFound(deploy_config.external_url('auth', '/users')) + + +@routes.delete('/api/v1alpha/users/{user}') +@auth.authenticated_developers_only() +async def rest_activate_user(request: web.Request, _) -> web.Response: + db = request.app[AppKeys.DB] + username = request.match_info['user'] + + try: + await _activate_user(db, username, None) + except UnknownUser as e: + raise e.http_response() + + return web.json_response() + + @routes.get('/api/v1alpha/oauth2callback') async def rest_callback(request): flow_json = request.query.get('flow') From d9f24043e4160d71fbe72fa30623a40daf5ecea8 Mon Sep 17 00:00:00 2001 From: grohli <22306963+grohli@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:55:36 -0500 Subject: [PATCH 16/16] added appropriate security headers --- auth/auth/auth.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/auth/auth/auth.py b/auth/auth/auth.py index 07938331be1..d4173d6b288 100644 --- a/auth/auth/auth.py +++ b/auth/auth/auth.py @@ -712,6 +712,7 @@ async def _activate_user(db: Database, username: str, id: Optional[str]): @routes.post('/users/activate') +@web_security_headers @auth.authenticated_developers_only() async def activate_user(request: web.Request, _) -> NoReturn: session = await aiohttp_session.get_session(request) @@ -730,6 +731,7 @@ async def activate_user(request: web.Request, _) -> NoReturn: @routes.delete('/api/v1alpha/users/{user}') +@api_security_headers @auth.authenticated_developers_only() async def rest_activate_user(request: web.Request, _) -> web.Response: db = request.app[AppKeys.DB]