외부 계정 수동 등록 허용
This commit is contained in:
parent
bd389a97cc
commit
8ed185ce6c
9 changed files with 3450 additions and 1484 deletions
|
|
@ -12,9 +12,10 @@ from fastapi import Response
|
|||
from starlette.requests import Request
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from . import auth_sessions
|
||||
from . import auth_sessions, deps
|
||||
from .config import Settings, settings
|
||||
from .deps import Principal, Role
|
||||
from .routes import admin as admin_routes
|
||||
from .routes import auth as auth_routes
|
||||
from .saml import inflate_redirect_request
|
||||
|
||||
|
|
@ -31,12 +32,15 @@ def patched_settings(**values: Any):
|
|||
setattr(settings, key, value)
|
||||
|
||||
|
||||
def _request(headers: list[tuple[bytes, bytes]] | None = None) -> Request:
|
||||
def _request(
|
||||
headers: list[tuple[bytes, bytes]] | None = None,
|
||||
path: str = "/auth/login",
|
||||
) -> Request:
|
||||
return Request(
|
||||
{
|
||||
"type": "http",
|
||||
"method": "GET",
|
||||
"path": "/auth/login",
|
||||
"path": path,
|
||||
"headers": headers or [(b"host", b"localhost:8000")],
|
||||
}
|
||||
)
|
||||
|
|
@ -198,6 +202,228 @@ class AuthProviderScaffoldTest(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertEqual(teacher_error.exception.status_code, 403)
|
||||
self.assertEqual(learner_error.exception.status_code, 400)
|
||||
|
||||
async def test_new_provider_account_waits_for_admin_approval(self) -> None:
|
||||
with (
|
||||
patched_settings(
|
||||
environment="dev",
|
||||
auth_new_user_default_status="pending",
|
||||
auth_super_admin_emails=["yunchan@twentyoz.kr"],
|
||||
auth_admin_emails=[],
|
||||
auth_teacher_emails=[],
|
||||
auth_approved_emails=[],
|
||||
),
|
||||
patch.object(auth_sessions, "get_pool", side_effect=RuntimeError("no db")),
|
||||
):
|
||||
sid, user = await auth_sessions.create_session(
|
||||
email="pending@hs.ac.kr",
|
||||
display_name="Pending Learner",
|
||||
role="learner",
|
||||
external_id="google:pending-provider-sub",
|
||||
)
|
||||
|
||||
self.assertEqual(user.account_status, "pending")
|
||||
me = await auth_routes._me_response(user)
|
||||
self.assertEqual(me.account_status, "pending")
|
||||
self.assertTrue(me.approval_required)
|
||||
|
||||
principal = await deps.get_current_principal(
|
||||
_request(path="/auth/me"),
|
||||
dev_session_cookie=sid,
|
||||
)
|
||||
self.assertEqual(principal.account_status, "pending")
|
||||
|
||||
with self.assertRaises(auth_routes.HTTPException) as blocked:
|
||||
await deps.get_current_principal(
|
||||
_request(path="/personas"),
|
||||
dev_session_cookie=sid,
|
||||
)
|
||||
self.assertEqual(blocked.exception.status_code, 403)
|
||||
self.assertEqual(blocked.exception.detail, "account_pending")
|
||||
|
||||
async def test_dev_login_account_is_auto_approved_for_local_e2e(self) -> None:
|
||||
with (
|
||||
patched_settings(
|
||||
environment="dev",
|
||||
auth_new_user_default_status="pending",
|
||||
auth_super_admin_emails=["yunchan@twentyoz.kr"],
|
||||
auth_admin_emails=[],
|
||||
auth_teacher_emails=[],
|
||||
auth_approved_emails=[],
|
||||
),
|
||||
patch.object(auth_sessions, "get_pool", side_effect=RuntimeError("no db")),
|
||||
):
|
||||
sid, user = await auth_sessions.create_session(
|
||||
email="dev-learner@hs.ac.kr",
|
||||
display_name="Dev Learner",
|
||||
role="learner",
|
||||
external_id="dev:dev-learner@hs.ac.kr",
|
||||
)
|
||||
|
||||
self.assertEqual(user.account_status, "approved")
|
||||
principal = await deps.get_current_principal(
|
||||
_request(path="/personas"),
|
||||
dev_session_cookie=sid,
|
||||
)
|
||||
self.assertEqual(principal.account_status, "approved")
|
||||
|
||||
async def test_admin_created_external_domain_user_can_login(self) -> None:
|
||||
with (
|
||||
patched_settings(
|
||||
environment="dev",
|
||||
auth_dev_login_enabled=True,
|
||||
auth_allowed_email_domains=["hs.ac.kr"],
|
||||
auth_new_user_default_status="pending",
|
||||
auth_super_admin_emails=["yunchan@twentyoz.kr"],
|
||||
auth_admin_emails=[],
|
||||
auth_teacher_emails=[],
|
||||
auth_approved_emails=[],
|
||||
),
|
||||
patch.object(auth_sessions, "get_pool", side_effect=RuntimeError("no db")),
|
||||
):
|
||||
super_admin = Principal(
|
||||
user_id="00000000-0000-0000-0000-000000000604",
|
||||
role=Role.ADMIN,
|
||||
email="yunchan@twentyoz.kr",
|
||||
display_name="Yun Chan",
|
||||
admin_access=True,
|
||||
super_admin=True,
|
||||
)
|
||||
created = await admin_routes.create_user(
|
||||
admin_routes.AdminUserCreate(
|
||||
email="forced.gmail@gmail.com",
|
||||
display_name="Forced Gmail",
|
||||
role="teacher",
|
||||
account_status="approved",
|
||||
cohort_ids=["manual-cohort"],
|
||||
),
|
||||
super_admin,
|
||||
)
|
||||
|
||||
me = await auth_routes.dev_login(
|
||||
_request(path="/auth/dev-login"),
|
||||
auth_routes.DevLoginRequest(
|
||||
email="forced.gmail@gmail.com",
|
||||
role="learner",
|
||||
display_name="Provider Name",
|
||||
),
|
||||
Response(),
|
||||
)
|
||||
|
||||
self.assertEqual(me.user_id, created.user_id)
|
||||
self.assertEqual(me.email, "forced.gmail@gmail.com")
|
||||
self.assertEqual(me.role, "teacher")
|
||||
self.assertEqual(me.account_status, "approved")
|
||||
self.assertEqual(me.cohort_ids, ["manual-cohort"])
|
||||
|
||||
with self.assertRaises(auth_routes.HTTPException) as denied:
|
||||
await auth_routes.dev_login(
|
||||
_request(path="/auth/dev-login"),
|
||||
auth_routes.DevLoginRequest(
|
||||
email="unmanaged@gmail.com",
|
||||
role="learner",
|
||||
display_name="Unmanaged Gmail",
|
||||
),
|
||||
Response(),
|
||||
)
|
||||
self.assertEqual(denied.exception.status_code, 403)
|
||||
|
||||
async def test_super_admin_email_is_admin_and_auto_approved(self) -> None:
|
||||
with (
|
||||
patched_settings(
|
||||
environment="dev",
|
||||
auth_new_user_default_status="pending",
|
||||
auth_super_admin_emails=["yunchan@twentyoz.kr", "hoonjungkoo@hs.ac.kr"],
|
||||
auth_admin_emails=[],
|
||||
auth_teacher_emails=[],
|
||||
auth_approved_emails=[],
|
||||
),
|
||||
patch.object(auth_sessions, "get_pool", side_effect=RuntimeError("no db")),
|
||||
):
|
||||
role = auth_routes._role_for_email("yunchan@twentyoz.kr")
|
||||
_, user = await auth_sessions.create_session(
|
||||
email="yunchan@twentyoz.kr",
|
||||
display_name="Yun Chan",
|
||||
role=role.value,
|
||||
external_id="google:yunchan",
|
||||
)
|
||||
|
||||
self.assertEqual(role, Role.ADMIN)
|
||||
self.assertEqual(user.role, "admin")
|
||||
self.assertTrue(user.admin_access)
|
||||
self.assertEqual(user.account_status, "approved")
|
||||
|
||||
hoonjung_role = auth_routes._role_for_email("hoonjungkoo@hs.ac.kr")
|
||||
self.assertEqual(hoonjung_role, Role.ADMIN)
|
||||
|
||||
async def test_super_admin_can_enter_teacher_and_learner_role_guards(self) -> None:
|
||||
principal = Principal(
|
||||
user_id="00000000-0000-0000-0000-000000000601",
|
||||
role=Role.LEARNER,
|
||||
email="hoonjungkoo@hs.ac.kr",
|
||||
display_name="Super Admin",
|
||||
admin_access=True,
|
||||
super_admin=True,
|
||||
)
|
||||
|
||||
teacher_checker = deps.require_role(Role.TEACHER, Role.ADMIN)
|
||||
teacher_view = await teacher_checker(principal)
|
||||
|
||||
self.assertEqual(teacher_view.role, Role.ADMIN)
|
||||
self.assertTrue(principal.can_access_role(Role.LEARNER))
|
||||
self.assertTrue(principal.can_access_role(Role.TEACHER))
|
||||
self.assertTrue(principal.can_access_role(Role.ADMIN))
|
||||
|
||||
async def test_only_super_admin_can_grant_admin_access(self) -> None:
|
||||
with (
|
||||
patched_settings(
|
||||
environment="dev",
|
||||
auth_super_admin_emails=["yunchan@twentyoz.kr"],
|
||||
auth_admin_emails=[],
|
||||
auth_teacher_emails=[],
|
||||
auth_approved_emails=[],
|
||||
),
|
||||
patch.object(auth_sessions, "get_pool", side_effect=RuntimeError("no db")),
|
||||
):
|
||||
target = await auth_sessions.upsert_managed_user(
|
||||
email="admin-grant-target@hs.ac.kr",
|
||||
display_name="Grant Target",
|
||||
role="learner",
|
||||
external_id="dev:admin-grant-target@hs.ac.kr",
|
||||
)
|
||||
operator = Principal(
|
||||
user_id="00000000-0000-0000-0000-000000000602",
|
||||
role=Role.LEARNER,
|
||||
email="operator@hs.ac.kr",
|
||||
display_name="Operator",
|
||||
admin_access=True,
|
||||
super_admin=False,
|
||||
)
|
||||
|
||||
with self.assertRaises(auth_routes.HTTPException) as denied:
|
||||
await admin_routes.patch_user(
|
||||
target.user_id,
|
||||
admin_routes.AdminUserPatch(admin_access=True),
|
||||
operator,
|
||||
)
|
||||
self.assertEqual(denied.exception.status_code, 403)
|
||||
|
||||
super_admin = Principal(
|
||||
user_id="00000000-0000-0000-0000-000000000603",
|
||||
role=Role.ADMIN,
|
||||
email="yunchan@twentyoz.kr",
|
||||
display_name="Yun Chan",
|
||||
admin_access=True,
|
||||
super_admin=True,
|
||||
)
|
||||
updated = await admin_routes.patch_user(
|
||||
target.user_id,
|
||||
admin_routes.AdminUserPatch(admin_access=True),
|
||||
super_admin,
|
||||
)
|
||||
|
||||
self.assertTrue(updated.admin_access)
|
||||
self.assertEqual(updated.role, "learner")
|
||||
|
||||
async def test_auth_config_allows_dev_login_from_configured_tailnet_forwarded_host(self) -> None:
|
||||
request = _request(
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue