Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions auth/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,44 @@ async def rest_logout(request: web.Request, _) -> web.Response:
return web.Response(status=200)


async def check_inactive_user_from_login_id_or_hail_identity_id(
db: Database, login_id_or_hail_identity_uid: str
) -> Optional[UserData]:
users = [
x
async for x in db.select_and_fetchall(
"SELECT * FROM users WHERE (login_id = %s OR hail_identity_uid = %s) AND state = 'inactive';",
(login_id_or_hail_identity_uid, login_id_or_hail_identity_uid),
)
]
if len(users) != 1:
return None
userdata = dict(users[0])
userdata['system_permissions'] = {}
return typing.cast(UserData, userdata)


async def check_inactive_user_from_hail_session_id(db: Database, session_id: str) -> Optional[UserData]:
users = [
x
async for x in db.select_and_fetchall(
"""
SELECT users.*
FROM users
INNER JOIN sessions ON users.id = sessions.user_id
WHERE 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,
)
]
if len(users) != 1:
return None
userdata = dict(users[0])
userdata['system_permissions'] = {}
return typing.cast(UserData, userdata)


async def get_userinfo(request: web.Request, auth_token: str) -> UserData:
flow_client = request.app[AppKeys.FLOW_CLIENT]
client_session = request.app[AppKeys.CLIENT_SESSION]
Expand Down Expand Up @@ -976,10 +1014,19 @@ async def get_userinfo(request: web.Request, auth_token: str) -> UserData:


async def get_userinfo_from_login_id_or_hail_identity_id(
request: web.Request, login_id_or_hail_idenity_uid: str
request: web.Request, login_id_or_hail_identity_uid: str
) -> UserData:
db = request.app[AppKeys.DB]

inactive_user = await check_inactive_user_from_login_id_or_hail_identity_id(db, login_id_or_hail_identity_uid)
if inactive_user is not None:
log.info(
f'Inactive user {inactive_user["username"]} found for login id or Hail identity id {login_id_or_hail_identity_uid}.'
)
raise web.HTTPUnauthorized(
text='Your Hail account is inactive. Please contact a Hail administrator to reactivate.'
)

users = [
x
async for x in db.select_and_fetchall(
Expand All @@ -988,12 +1035,12 @@ async def get_userinfo_from_login_id_or_hail_identity_id(
FROM users
WHERE (users.login_id = %s OR users.hail_identity_uid = %s) AND users.state = 'active'
""",
(login_id_or_hail_idenity_uid, login_id_or_hail_idenity_uid),
(login_id_or_hail_identity_uid, login_id_or_hail_identity_uid),
)
]

if len(users) != 1:
log.info('Unknown login id')
log.info('Unknown login id or hail identity')
raise web.HTTPUnauthorized()

user = users[0]
Expand Down Expand Up @@ -1027,6 +1074,13 @@ async def get_userinfo_from_hail_session_id(request: web.Request, session_id: st
return None

db = request.app[AppKeys.DB]
inactive_user = await check_inactive_user_from_hail_session_id(db, session_id)
if inactive_user is not None:
log.info(f'Inactive user {inactive_user["username"]} found for Hail session.')
raise web.HTTPUnauthorized(
text='Your Hail account is inactive. Please contact a Hail administrator to reactivate.'
)

users = [
x
async for x in db.select_and_fetchall(
Expand Down
5 changes: 5 additions & 0 deletions gear/gear/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ async def _fetch_userdata_from_auth_service(session_id_and_session: Tuple[str, h
return await impersonate_user_and_get_info(session_id=session_id, client_session=client_session)
except asyncio.CancelledError:
raise
except web.HTTPException:
raise
except aiohttp.ClientResponseError as e:
log.exception('unknown exception getting userinfo')
raise web.HTTPInternalServerError() from e
Expand Down Expand Up @@ -229,6 +231,9 @@ async def impersonate_user(session_id: str, client_session: httpx.ClientSession,
return await retry_transient_errors(client_session.get_read_json, url, headers=headers)
except aiohttp.ClientResponseError as err:
if err.status == 401:
body = getattr(err, 'body', None)
if body:
raise web.HTTPUnauthorized(text=body)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: make this inactive-specific through some means (rather than just showing every 401)

return None
raise

Expand Down
Loading