From 2a2af5fff2ad759d1e44d5d4528d816bbceff055 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 21 Jul 2025 21:09:05 -0600 Subject: [PATCH] fix: remove fief out of the source code (#502) * fix: remove fief out of the source code * fix: remove corresponding test about migration --- CLAUDE.md | 2 +- server/.env_template | 5 -- server/env.example | 9 ++-- server/pyproject.toml | 1 - server/reflector/auth/auth_fief.py | 25 ---------- server/reflector/db/migrate_user.py | 56 ---------------------- server/reflector/settings.py | 7 +-- server/reflector/views/transcripts.py | 5 -- server/tests/test_transcripts.py | 68 +-------------------------- server/uv.lock | 43 ----------------- 10 files changed, 7 insertions(+), 214 deletions(-) delete mode 100644 server/reflector/auth/auth_fief.py delete mode 100644 server/reflector/db/migrate_user.py diff --git a/CLAUDE.md b/CLAUDE.md index 2273f896..3f256c93 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -146,7 +146,7 @@ All endpoints prefixed `/v1/`: - `REDIS_URL` - Redis broker for Celery - `MODAL_TOKEN_ID`, `MODAL_TOKEN_SECRET` - Modal.com GPU processing - `WHEREBY_API_KEY` - Video platform integration -- `REFLECTOR_AUTH_BACKEND` - Authentication method (none, fief, jwt) +- `REFLECTOR_AUTH_BACKEND` - Authentication method (none, jwt) **Frontend** (`www/.env`): - `NEXTAUTH_URL`, `NEXTAUTH_SECRET` - Authentication configuration diff --git a/server/.env_template b/server/.env_template index 2c79d80d..579f80ca 100644 --- a/server/.env_template +++ b/server/.env_template @@ -6,11 +6,6 @@ LLM_BACKEND=modal LLM_URL=https://monadical-sas--reflector-llm-web.modal.run LLM_MODAL_API_KEY=***REMOVED*** -AUTH_BACKEND=fief -AUTH_FIEF_URL=https://auth.reflector.media/reflector-local -AUTH_FIEF_CLIENT_ID=***REMOVED*** -AUTH_FIEF_CLIENT_SECRET= <----------------------------------------------------------------------------------------- - TRANSLATE_URL=https://monadical-sas--reflector-translator-web.modal.run ZEPHYR_LLM_URL=https://monadical-sas--reflector-llm-zephyr-web.modal.run DIARIZATION_URL=https://monadical-sas--reflector-diarizer-web.modal.run diff --git a/server/env.example b/server/env.example index e36145d5..0b2f2fde 100644 --- a/server/env.example +++ b/server/env.example @@ -7,11 +7,9 @@ ## User authentication ## ======================================================= -## Using fief (fief.dev) -AUTH_BACKEND=fief -AUTH_FIEF_URL=https://auth.reflector.media/reflector-local -AUTH_FIEF_CLIENT_ID=***REMOVED*** -AUTH_FIEF_CLIENT_SECRET= +## Using jwt/authentik +AUTH_BACKEND=jwt +AUTH_JWT_AUDIENCE= ## ======================================================= ## Transcription backend @@ -88,4 +86,3 @@ DIARIZATION_URL=https://monadical-sas--reflector-diarizer-web.modal.run ## Sentry DSN configuration #SENTRY_DSN= - diff --git a/server/pyproject.toml b/server/pyproject.toml index 39a47eac..b3d94b16 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -22,7 +22,6 @@ dependencies = [ "fastapi-pagination>=0.12.6", "databases[aiosqlite, asyncpg]>=0.7.0", "sqlalchemy<1.5", - "fief-client[fastapi]>=0.17.0", "alembic>=1.11.3", "nltk>=3.8.1", "prometheus-fastapi-instrumentator>=6.1.0", diff --git a/server/reflector/auth/auth_fief.py b/server/reflector/auth/auth_fief.py deleted file mode 100644 index 0b363fc0..00000000 --- a/server/reflector/auth/auth_fief.py +++ /dev/null @@ -1,25 +0,0 @@ -from fastapi.security import OAuth2AuthorizationCodeBearer -from fief_client import FiefAccessTokenInfo, FiefAsync, FiefUserInfo -from fief_client.integrations.fastapi import FiefAuth -from reflector.settings import settings - -fief = FiefAsync( - settings.AUTH_FIEF_URL, - settings.AUTH_FIEF_CLIENT_ID, - settings.AUTH_FIEF_CLIENT_SECRET, -) - -scheme = OAuth2AuthorizationCodeBearer( - f"{settings.AUTH_FIEF_URL}/authorize", - f"{settings.AUTH_FIEF_URL}/api/token", - scopes={"openid": "openid", "offline_access": "offline_access"}, - auto_error=False, -) - -auth = FiefAuth(fief, scheme) - -UserInfo = FiefUserInfo -AccessTokenInfo = FiefAccessTokenInfo -authenticated = auth.authenticated() -current_user = auth.current_user() -current_user_optional = auth.current_user(optional=True) diff --git a/server/reflector/db/migrate_user.py b/server/reflector/db/migrate_user.py deleted file mode 100644 index f058bf21..00000000 --- a/server/reflector/db/migrate_user.py +++ /dev/null @@ -1,56 +0,0 @@ -from reflector.db import database -from reflector.db.meetings import meetings -from reflector.db.rooms import rooms -from reflector.db.transcripts import transcripts - -users_to_migrate = [ - ["123@lifex.pink", "63b727f5-485d-449f-b528-563d779b11ef", None], - ["ana@monadical.com", "1bae2e4d-5c04-49c2-932f-a86266a6ca13", None], - ["cspencer@sprocket.org", "614ed0be-392e-488c-bd19-6a9730fd0e9e", None], - ["daniel.f.lopez.j@gmail.com", "ca9561bd-c989-4a1e-8877-7081cf62ae7f", None], - ["jenalee@monadical.com", "c7c1e79e-b068-4b28-a9f4-29d98b1697ed", None], - ["jennifer@rootandseed.com", "f5321727-7546-4b2b-b69d-095a931ef0c4", None], - ["jose@monadical.com", "221f079c-7ce0-4677-90b7-0359b6315e27", None], - ["labenclayton@gmail.com", "40078cd0-543c-40e4-9c2e-5ce57a686428", None], - ["mathieu@monadical.com", "c7a36151-851e-4afa-9fab-aaca834bfd30", None], - ["michal.flak.96@gmail.com", "3096eb5e-b590-41fc-a0d1-d152c1895402", None], - ["sara@monadical.com", "31ab0cfe-5d2c-4c7a-84de-a29494714c99", None], - ["sara@monadical.com", "b871e5f0-754e-447f-9c3d-19f629f0082b", None], - ["sebastian@monadical.com", "f024f9d0-15d0-480f-8529-43959fc8b639", None], - ["sergey@monadical.com", "5c4798eb-b9ab-4721-a540-bd96fc434156", None], - ["sergey@monadical.com", "9dd8a6b4-247e-48fe-b1fb-4c84dd3c01bc", None], - ["transient.tran@gmail.com", "617ba2d3-09b6-4b1f-a435-a7f41c3ce060", None], -] - - -async def migrate_user(email, user_id): - # if the email match the email in the users_to_migrate list - # reassign all transcripts/rooms/meetings to the new user_id - - user_ids = [user[1] for user in users_to_migrate if user[0] == email] - if not user_ids: - return - - # do not migrate back - if user_id in user_ids: - return - - for old_user_id in user_ids: - query = ( - transcripts.update() - .where(transcripts.c.user_id == old_user_id) - .values(user_id=user_id) - ) - await database.execute(query) - - query = ( - rooms.update().where(rooms.c.user_id == old_user_id).values(user_id=user_id) - ) - await database.execute(query) - - query = ( - meetings.update() - .where(meetings.c.user_id == old_user_id) - .values(user_id=user_id) - ) - await database.execute(query) diff --git a/server/reflector/settings.py b/server/reflector/settings.py index 54a8f87e..5db27289 100644 --- a/server/reflector/settings.py +++ b/server/reflector/settings.py @@ -90,14 +90,9 @@ class Settings(BaseSettings): # Sentry SENTRY_DSN: str | None = None - # User authentication (none, fief) + # User authentication (none, jwt) AUTH_BACKEND: str = "none" - # User authentication using fief - AUTH_FIEF_URL: str | None = None - AUTH_FIEF_CLIENT_ID: str | None = None - AUTH_FIEF_CLIENT_SECRET: str | None = None - # User authentication using JWT AUTH_JWT_ALGORITHM: str = "RS256" AUTH_JWT_PUBLIC_KEY: str | None = "authentik.monadical.com_public.pem" diff --git a/server/reflector/views/transcripts.py b/server/reflector/views/transcripts.py index 51d59a1c..7a57d092 100644 --- a/server/reflector/views/transcripts.py +++ b/server/reflector/views/transcripts.py @@ -8,7 +8,6 @@ from fastapi_pagination.ext.databases import paginate from jose import jwt from pydantic import BaseModel, Field, field_serializer from reflector.db.meetings import meetings_controller -from reflector.db.migrate_user import migrate_user from reflector.db.rooms import rooms_controller from reflector.db.transcripts import ( SourceKind, @@ -114,10 +113,6 @@ async def transcripts_list( user_id = user["sub"] if user else None - # for fief to jwt migration, migrate user if needed - if user: - await migrate_user(email=user["email"], user_id=user["sub"]) - return await paginate( database, await transcripts_controller.get_all( diff --git a/server/tests/test_transcripts.py b/server/tests/test_transcripts.py index be0779ff..ce41923e 100644 --- a/server/tests/test_transcripts.py +++ b/server/tests/test_transcripts.py @@ -1,6 +1,6 @@ -import pytest -from unittest.mock import patch from contextlib import asynccontextmanager + +import pytest from httpx import AsyncClient @@ -261,67 +261,3 @@ async def test_transcript_mark_reviewed(): response = await ac.get(f"/transcripts/{tid}") assert response.status_code == 200 assert response.json()["reviewed"] is True - - -@asynccontextmanager -async def patch_migrate_user(): - with patch( - "reflector.db.migrate_user.users_to_migrate", - [["test@mail.com", "randomuserid", None]], - ): - yield - - -@pytest.mark.asyncio -async def test_transcripts_list_authenticated_migration(): - # XXX this test is a bit fragile, as it depends on the storage which - # is shared between tests - from reflector.app import app - - testx1 = "testmigration1" - testx2 = "testmigration2" - - async with patch_migrate_user(), AsyncClient( - app=app, base_url="http://test/v1" - ) as ac: - # first ensure client 2 does not have any transcripts related to this test - async with authenticated_client2_ctx(): - response = await ac.get("/transcripts") - assert response.status_code == 200 - # assert len(response.json()["items"]) == 0 - names = [t["name"] for t in response.json()["items"]] - assert testx1 not in names - assert testx2 not in names - - # create 2 transcripts with client 1 - async with authenticated_client_ctx(): - response = await ac.post("/transcripts", json={"name": testx1}) - assert response.status_code == 200 - assert response.json()["name"] == testx1 - - response = await ac.post("/transcripts", json={"name": testx2}) - assert response.status_code == 200 - assert response.json()["name"] == testx2 - - response = await ac.get("/transcripts") - assert response.status_code == 200 - assert len(response.json()["items"]) >= 2 - names = [t["name"] for t in response.json()["items"]] - assert testx1 in names - assert testx2 in names - - # now going back to client 2, migration should happen - async with authenticated_client2_ctx(): - response = await ac.get("/transcripts") - assert response.status_code == 200 - names = [t["name"] for t in response.json()["items"]] - assert testx1 in names - assert testx2 in names - - # and client 1 should have nothing now - async with authenticated_client_ctx(): - response = await ac.get("/transcripts") - assert response.status_code == 200 - names = [t["name"] for t in response.json()["items"]] - assert testx1 not in names - assert testx2 not in names diff --git a/server/uv.lock b/server/uv.lock index dbbe7997..f1f920eb 100644 --- a/server/uv.lock +++ b/server/uv.lock @@ -817,25 +817,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/69/28359d152f9e2ec1ff4dff3da47011b6346e9a472f89b409bb13017a7d1f/faster_whisper-1.1.1-py3-none-any.whl", hash = "sha256:5808dc334fb64fb4336921450abccfe5e313a859b31ba61def0ac7f639383d90", size = 1118368, upload-time = "2025-01-01T14:47:16.131Z" }, ] -[[package]] -name = "fief-client" -version = "0.20.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "jwcrypto" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/af/f6cc3ded8bdb901097b92a3ed444c48576a1b62f01352cb2fa069b0dd166/fief_client-0.20.0.tar.gz", hash = "sha256:dbfb906d03c4a5402ceac5c843aa4708535fb6f5d5c1c4e263ec06fbbbc434d7", size = 32465, upload-time = "2024-10-13T11:54:08.793Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/06/d33506317b4c9b71025eb010d96c4f7a8f89fa620ca30532c2e8e4390593/fief_client-0.20.0-py3-none-any.whl", hash = "sha256:425f40cc7c45c651daec63da402e033c53d91dcaa3f9bf208873fd8692fc16dc", size = 20219, upload-time = "2024-10-13T11:54:07.342Z" }, -] - -[package.optional-dependencies] -fastapi = [ - { name = "fastapi" }, - { name = "makefun" }, -] - [[package]] name = "filelock" version = "3.18.0" @@ -1211,19 +1192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, ] -[[package]] -name = "jwcrypto" -version = "1.5.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/db/870e5d5fb311b0bcf049630b5ba3abca2d339fd5e13ba175b4c13b456d08/jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039", size = 87168, upload-time = "2024-03-06T19:58:31.831Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/58/4a1880ea64032185e9ae9f63940c9327c6952d5584ea544a8f66972f2fda/jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789", size = 92520, upload-time = "2024-03-06T19:58:29.765Z" }, -] - [[package]] name = "kombu" version = "5.5.4" @@ -1299,15 +1267,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] -[[package]] -name = "makefun" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/cf/6780ab8bc3b84a1cce3e4400aed3d64b6db7d5e227a2f75b6ded5674701a/makefun-1.16.0.tar.gz", hash = "sha256:e14601831570bff1f6d7e68828bcd30d2f5856f24bad5de0ccb22921ceebc947", size = 73565, upload-time = "2025-05-09T15:00:42.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/c0/4bc973defd1270b89ccaae04cef0d5fa3ea85b59b108ad2c08aeea9afb76/makefun-1.16.0-py2.py3-none-any.whl", hash = "sha256:43baa4c3e7ae2b17de9ceac20b669e9a67ceeadff31581007cca20a07bbe42c4", size = 22923, upload-time = "2025-05-09T15:00:41.042Z" }, -] - [[package]] name = "mako" version = "1.3.10" @@ -2172,7 +2131,6 @@ dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "fastapi-pagination" }, { name = "faster-whisper" }, - { name = "fief-client", extra = ["fastapi"] }, { name = "httpx" }, { name = "jsonschema" }, { name = "loguru" }, @@ -2234,7 +2192,6 @@ requires-dist = [ { name = "fastapi", extras = ["standard"], specifier = ">=0.100.1" }, { name = "fastapi-pagination", specifier = ">=0.12.6" }, { name = "faster-whisper", specifier = ">=0.10.0" }, - { name = "fief-client", extras = ["fastapi"], specifier = ">=0.17.0" }, { name = "httpx", specifier = ">=0.24.1" }, { name = "jsonschema", specifier = ">=4.23.0" }, { name = "loguru", specifier = ">=0.7.0" },