-
Notifications
You must be signed in to change notification settings - Fork 3
Redirect to dashboard home after login, usually #2600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e37ac0d
87c910f
a4c610e
7ef859c
5c373f2
4ff8416
f1cbaa1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import json | ||
from base64 import b64encode | ||
from typing import NamedTuple | ||
from unittest.mock import MagicMock | ||
from urllib.parse import urljoin | ||
|
||
|
@@ -29,7 +30,7 @@ | |
(["allowed-2"], "https://good.com/url-2"), | ||
], | ||
) | ||
def test_get_redirect_url(mocker, param_names, expected_redirect): | ||
def test_get_redirect_url(mocker, param_names, expected_redirect, settings): | ||
"""Next url should be respected if host is allowed""" | ||
GET = { | ||
"exists-a": "/url-a", | ||
|
@@ -38,10 +39,7 @@ def test_get_redirect_url(mocker, param_names, expected_redirect): | |
"disallowed-a": "https://malicious.com/url-1", | ||
"allowed-2": "https://good.com/url-2", | ||
} | ||
mocker.patch( | ||
"authentication.views.settings.ALLOWED_REDIRECT_HOSTS", | ||
["good.com"], | ||
) | ||
settings.ALLOWED_REDIRECT_HOSTS = ["good.com"] | ||
|
||
mock_request = mocker.MagicMock(GET=GET) | ||
assert get_redirect_url(mock_request, param_names) == expected_redirect | ||
|
@@ -50,6 +48,7 @@ def test_get_redirect_url(mocker, param_names, expected_redirect): | |
@pytest.mark.parametrize( | ||
"test_params", | ||
[ | ||
# has_apisix_header, next_url | ||
(True, "/search"), | ||
(True, None), | ||
(False, "/search"), | ||
|
@@ -129,8 +128,9 @@ def test_next_logout(mocker, client, user, test_params, settings): | |
|
||
@pytest.mark.parametrize("is_authenticated", [True, False]) | ||
@pytest.mark.parametrize("has_next", [True, False]) | ||
def test_custom_logout_view(mocker, client, user, is_authenticated, has_next): | ||
def test_custom_logout_view(mocker, client, user, is_authenticated, has_next, settings): # noqa: PLR0913 | ||
"""Test logout redirect""" | ||
settings.ALLOWED_REDIRECT_HOSTS = ["ocw.mit.edu"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test was broken for me locally because I guess my local env file overrides this value. |
||
next_url = "https://ocw.mit.edu" if has_next else "" | ||
mock_request = mocker.MagicMock(user=user, META={}) | ||
if is_authenticated: | ||
|
@@ -245,23 +245,43 @@ def test_custom_login_view_first_time_login_sets_has_logged_in(mocker): | |
mock_profile.save.assert_called_once() | ||
|
||
|
||
class LoginOrgUserRedirectParams(NamedTuple): | ||
"""Parameters for testing org user login redirect behavior""" | ||
|
||
has_logged_in: bool | ||
login_url: str | ||
expected_redirect: str | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"test_case", | ||
"params", | ||
[ | ||
( | ||
False, | ||
"/dashboard/organization/test-organization", | ||
), # First-time login → org dashboard | ||
( | ||
True, | ||
"/app", | ||
), # Subsequent login → normal app | ||
LoginOrgUserRedirectParams( | ||
has_logged_in=False, | ||
login_url="/login/?next=/dashboard", | ||
expected_redirect="/dashboard", | ||
), | ||
LoginOrgUserRedirectParams( | ||
has_logged_in=False, | ||
login_url="/login/?next=/dashboard&signup_next=/somewhere-else", | ||
expected_redirect="/somewhere-else", | ||
), | ||
LoginOrgUserRedirectParams( | ||
has_logged_in=True, | ||
login_url="/login/?next=/dashboard&signup_next=/somewhere-else", | ||
expected_redirect="/dashboard", | ||
), | ||
], | ||
) | ||
def test_login_org_user_redirect(mocker, client, user, test_case, settings): | ||
def test_login_org_user_redirect( | ||
mocker, | ||
client, | ||
user, | ||
params, | ||
settings, | ||
): | ||
"""Test organization user redirect behavior - org users skip onboarding regardless of login history""" | ||
# Unpack test case | ||
has_logged_in, expected_url = test_case | ||
has_logged_in, login_url, expected_redirect = params | ||
|
||
# Set up user profile based on test scenario | ||
user.profile.has_logged_in = has_logged_in | ||
|
@@ -284,15 +304,18 @@ def test_login_org_user_redirect(mocker, client, user, test_case, settings): | |
) | ||
client.force_login(user) | ||
response = client.get( | ||
"/login/", | ||
login_url, | ||
follow=False, | ||
HTTP_X_USERINFO=header_str, | ||
) | ||
assert response.status_code == 302 | ||
# Handle environment differences - in some envs it returns full URL, in others just path | ||
expected_full_url = urljoin(settings.APP_BASE_URL, expected_url) | ||
assert response.url in [expected_url, expected_full_url] | ||
expected_full_redirect = urljoin(settings.APP_BASE_URL, expected_redirect) | ||
assert response.url in [expected_redirect, expected_full_redirect] | ||
|
||
# Verify that org users are never sent to onboarding | ||
# (onboarding URL would contain settings.MITOL_NEW_USER_LOGIN_URL) | ||
assert settings.MITOL_NEW_USER_LOGIN_URL not in response.url | ||
|
||
user.profile.refresh_from_db() | ||
assert user.profile.has_logged_in is True |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ import React from "react" | |
import { styled, Breadcrumbs, Container, Typography } from "ol-components" | ||
import * as urls from "@/common/urls" | ||
import { useB2BAttachMutation } from "api/mitxonline-hooks/organizations" | ||
import { useMitxOnlineUserMe } from "api/mitxonline-hooks/user" | ||
import { userQueries } from "api/hooks/user" | ||
import { useQuery } from "@tanstack/react-query" | ||
import { useRouter } from "next-nprogress-bar" | ||
|
@@ -18,11 +17,7 @@ const InterstitialMessage = styled(Typography)(({ theme }) => ({ | |
})) | ||
|
||
const EnrollmentCodePage: React.FC<EnrollmentCodePage> = ({ code }) => { | ||
const { | ||
mutate: attach, | ||
isSuccess, | ||
isPending, | ||
} = useB2BAttachMutation({ | ||
const enrollment = useB2BAttachMutation({ | ||
enrollment_code: code, | ||
}) | ||
const router = useRouter() | ||
|
@@ -31,24 +26,21 @@ const EnrollmentCodePage: React.FC<EnrollmentCodePage> = ({ code }) => { | |
...userQueries.me(), | ||
staleTime: 0, | ||
}) | ||
const { data: mitxOnlineUser } = useMitxOnlineUserMe() | ||
|
||
const enrollAsync = enrollment.mutateAsync | ||
React.useEffect(() => { | ||
attach?.() | ||
}, [attach]) | ||
if (user?.is_authenticated) { | ||
enrollAsync().then(() => router.push(urls.DASHBOARD_HOME)) | ||
} | ||
}, [user?.is_authenticated, enrollAsync, router]) | ||
|
||
React.useEffect(() => { | ||
if (userLoading) { | ||
return | ||
} | ||
if (!user?.is_authenticated) { | ||
const loginUrlString = urls.auth({ | ||
loginNext: { | ||
pathname: urls.b2bAttachView(code), | ||
searchParams: null, | ||
}, | ||
// On signup, redirect to the attach page so attachment can occur. | ||
signupNext: { | ||
next: { | ||
pathname: urls.b2bAttachView(code), | ||
searchParams: null, | ||
}, | ||
|
@@ -57,13 +49,7 @@ const EnrollmentCodePage: React.FC<EnrollmentCodePage> = ({ code }) => { | |
loginUrl.searchParams.set("skip_onboarding", "1") | ||
router.push(loginUrl.toString()) | ||
} | ||
if (isSuccess) { | ||
const org = mitxOnlineUser?.b2b_organizations?.[0] | ||
if (org) { | ||
router.push(urls.organizationView(org.slug.replace("org-", ""))) | ||
} | ||
} | ||
}, [isSuccess, userLoading, user, mitxOnlineUser, code, router]) | ||
}, [userLoading, user, code, router]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably no race condition if the attach |
||
|
||
return ( | ||
<Container> | ||
|
@@ -72,7 +58,7 @@ const EnrollmentCodePage: React.FC<EnrollmentCodePage> = ({ code }) => { | |
ancestors={[{ href: urls.HOME, label: "Home" }]} | ||
current="Use Enrollment Code" | ||
/> | ||
{isPending && ( | ||
{enrollment.isPending && ( | ||
<InterstitialMessage>Validating code "{code}"...</InterstitialMessage> | ||
)} | ||
</Container> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing from
main
instead ofdjango.conf
breaks the pytest django fixtures.