pdf-quiz-generator/backend/app/services
Daniel 7f36e07af0
Some checks failed
Tests / backend (push) Failing after 4s
Tests / frontend (push) Failing after 33s
Tests / e2e (push) Failing after 34s
feat: SSO reads roles from the provider's groups, and needs a verified address
Everybody arriving through SSO landed as a learner and was promoted by
hand. That is fine for three people and wrong for three hundred: the
list of who may edit the bank then lives in two places and only one of
them is maintained. OIDC_ROLE_CLAIM names the claim that carries somebody's
groups ("groups" for most providers, "roles" for Entra app roles), and
OIDC_ADMIN_GROUPS / OIDC_MODERATOR_GROUPS say which values mean what.
All three shapes a provider might send are read — a list, a
space-separated string, a comma-separated one — and matched case-blind.

Applied on every sign-in, not only at creation, because a group somebody
can be added to and never removed from is not a list anybody can rely
on. The one demotion it refuses is the last administrator: a mistyped
group name should not lock everybody out of the settings page,
including the person who could fix the group name. It is logged when it
happens.

Blank OIDC_ROLE_CLAIM is the old behaviour exactly: role "user", set
once, never touched.

And an address the provider will not vouch for is not an identity. This
matches on email, so an explicit email_verified:false would hand an
existing account to whoever typed its address at a careless provider.
Refused now — only on an explicit false, since a provider that omits the
claim is not asserting anything either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 05:20:52 +02:00
..
__init__.py Initial commit: PDF Quiz Generator app 2026-03-30 20:04:53 +00:00
ai_mode_service.py fix: drawers that close, sources that are sources, and a model that does not haggle 2026-09-12 23:34:24 +02:00
ai_service.py fix: a speech model is added with its voices, and Orpheus is sent where it works 2026-09-13 05:04:37 +02:00
article_service.py feat: section links in prose, a picker that writes them, and cards that render 2026-09-13 02:05:02 +02:00
article_writer.py feat: search that looks at what an article says 2026-09-12 15:57:42 +02:00
attempt_expiry.py feat: right after a tip is its own slice 2026-09-12 01:51:03 +02:00
captcha.py feat: Cap replaces hCaptcha, self-hosted beside the app 2026-09-12 06:14:14 +02:00
card_review.py feat: cards remember, render as prose, and are reachable from a question 2026-09-12 21:50:31 +02:00
clinical_corpus.py feat: ground AI drafts in the library and PubMed, and mend the card system 2026-09-13 02:44:42 +02:00
clinical_library.py feat: short / long / clinical, and stop broad topics retrieving index lines 2026-09-10 18:21:21 +02:00
draft_questions.py refactor: remove per-question sharing 2026-09-12 08:42:51 +02:00
email_service.py feat: ground AI drafts in the library and PubMed, and mend the card system 2026-09-13 02:44:42 +02:00
embedding_service.py feat: a session prepared for you, and a model that can see when the one on the job cannot 2026-09-12 15:46:04 +02:00
exam_blueprint.py feat: study plans built to the board's content outline 2026-09-11 21:40:17 +02:00
extraction_modes.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
file_intake.py fix: an upload is what its bytes say, not what its name claims 2026-09-12 19:05:12 +02:00
invites.py feat: the player is a box; sharing and sign-up are the administrator's 2026-09-11 19:17:35 +02:00
knowledge_groups.py feat: an organ system is a fact about a topic, not about a keyword 2026-09-12 04:32:25 +02:00
login_codes.py feat: sign in with a code sent by email 2026-09-12 17:29:28 +02:00
pdf_service.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
prepared_session.py refactor: remove the LMS 2026-09-12 23:27:51 +02:00
pubmed.py fix: a question's figure says what the question says, not what the library says 2026-09-13 04:26:59 +02:00
question_figures.py feat: an opened explanation figure shows what the library knows 2026-09-13 05:05:59 +02:00
quiz_builder.py refactor: remove the LMS 2026-09-12 23:27:51 +02:00
quiz_service.py Junction table for shared questions; verification blocking; semantic bank search; bug fixes 2026-04-01 00:55:13 +02:00
refresh_tokens.py feat: a versioned API, refresh tokens, and an end-to-end stack that found four bugs 2026-09-13 01:23:38 +02:00
rerank_service.py feat: rerank what a learner is shown, with Cohere through the proxy 2026-09-12 18:23:06 +02:00
search_service.py feat: rerank what a learner is shown, with Cohere through the proxy 2026-09-12 18:23:06 +02:00
site_settings.py feat: ground AI drafts in the library and PubMed, and mend the card system 2026-09-13 02:44:42 +02:00
sso_roles.py feat: SSO reads roles from the provider's groups, and needs a verified address 2026-09-13 05:20:52 +02:00
storage_service.py feat: serve uploads through storage, and move all 3,852 files to MinIO 2026-09-10 10:23:34 +02:00
study_plan_context.py feat: an exam that runs out is a result; delete and reset go all the way down 2026-09-11 04:47:11 +02:00
thumbnails.py fix: figures a browser will actually draw, and two requests that could hang 2026-09-12 16:29:45 +02:00
topic_claims.py feat: an article follows a topic, rather than copying it once 2026-09-12 19:57:06 +02:00
tts_voices.py fix: a speech model is added with its voices, and Orpheus is sent where it works 2026-09-13 05:04:37 +02:00
vector_service.py fix: gateway-agnostic URL handling for TTS and embeddings, docs cleanup 2026-04-19 02:17:35 +02:00
vision_service.py fix: WebP figures, the openai SDK removed, and a voice a site can add to 2026-09-12 17:13:05 +02:00