feat: let moderators require a login before attendees can post messages - #245
Open
junkerderprovinz wants to merge 3 commits into
Open
feat: let moderators require a login before attendees can post messages#245junkerderprovinz wants to merge 3 commits into
junkerderprovinz wants to merge 3 commits into
Conversation
Adds a "Require login to post" toggle to the moderator's Attendee Settings, next to "Allow anonymous messages" and "Enable messages". While it is on, only attendees with a Claper account (local password, OIDC, LTI, whichever the deployment allows) can post; everyone else sees the composer replaced by a short notice and a link to the login page. This is the missing option for a lecturer moderating alone: instead of deleting troll messages after the fact, every message carries an account behind it, so an SSO-authenticated audience is accountable by construction. Deliberately a per-event presentation state flag, not a deployment-wide env var: whether an audience needs to be identified is a per-lecture decision, the same kind of decision as "allow anonymous messages", and it belongs next to it in the same panel. New migration adds `authenticated_chat_only` (boolean, default false) to presentation_states, so every existing event keeps behaving exactly as before until a moderator turns it on. The gate itself sits in the "save" handle_event clause of the attendee LiveView, in front of the two existing save clauses, which is where chat_enabled and anonymous_chat_enabled are already enforced. That clause reads the live @State, so it also covers a message pushed straight over the websocket rather than through the form, and it follows a mid-session flip immediately (the moderator's update broadcasts :state_updated). PostController.create/2 has no authenticated user at all, so it answers 403 while the flag is on, via a new Presentations.authenticated_chat_only?/1 that always reads the current value from the database. Poll, form and quiz answering are untouched. Fixes ClaperCo#107
Five LiveView tests around the attendee room (anonymous attendee sees the login prompt instead of the composer, logged in attendee keeps it, a "save" pushed by an anonymous attendee is refused and stores nothing, the same push from a logged in attendee is stored, and the composer swaps over when the moderator turns the setting on mid-session) plus three context tests for Presentations.authenticated_chat_only?/1.
Clicks the new Attendee Settings toggle on the manage page and asserts the presentation state was persisted and the label flipped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Issue #107 asks for a way to hold an audience accountable when nobody is free to moderate: instead of deleting troll messages after the fact, let the moderator require that attendees are authenticated (e.g. through their university SSO) before they can post at all. This adds a per-event Require login to post toggle that does exactly that, leaving polls, forms and quizzes untouched.
What this adds
authenticated_chat_only(boolean, defaultfalse) onpresentation_states, cast inPresentationState.changeset/2. Every existing event keeps behaving exactly as before until a moderator turns it on.ManageAttendeesOptionsComponent), sitting right under "Allow anonymous messages", with the same disabled-when-messages-are-off behaviour and keyboard shortcut handling (F;A/S/Dwere taken). Label flips between "Require login to post" and "Allow messages without login" like the neighbouring toggles do. Handled by a new"checked"clause inEventLive.Manage, matching the existing ones one-for-one.handle_event("save", ...)clause inEventLive.Show, placed in front of the two existing save clauses, next to wherechat_enabledandanonymous_chat_enabledare already enforced. It refuses the post and flashes "You must be logged in to post a message". Because it matches on the live@state, it also covers asavepushed straight over the websocket rather than through the form.ifaround the composer became acondwith three branches (messages off / login required / normal composer).PostController.create/2builds a post with no user at all, so it answers403while the flag is on, via a newPresentations.authenticated_chat_only?/1that always reads the current value from the database rather than trusting a state struct loaded earlier.mix gettext.extract+mix gettext.merge priv/gettextasAGENTS.mdasks; 4 new msgids across the 10 locales ("Log in" already existed), the rest of that diff is reference-line churn.Deliberately a per-event presentation state flag rather than an env var: whether an audience must be identified is a per-lecture decision of the same kind as "allow anonymous messages", so it belongs in the same panel, changeable mid-event without a redeploy.
Verification
Run locally against a real PostgreSQL with Elixir 1.18.4 / OTP 28 (the versions in
.tool-versions) and the same environment the CI workflow sets:mix format --check-formatted— clean.mix credo diff --from-git-merge-base origin/main— added no issues.mix test— 345 tests, 3 failures. Those 3 are pre-existing: I ran the suite on untoucheddevbefore writing a line (336 tests, the identical 3 failures) and they areConverterTest's two ImageMagick-availability tests plusPresentationsTest'smissing_slide_thumbnails?/1case, all local-thumbnail path tests that misbehave on my Windows host. Nothing they touch is in this PR.New tests, all passing:
#post-formgone,#login-required-composerpresent).savepushed by an anonymous attendee is refused, the flash is rendered, and nothing is written (Posts.list_posts/1stays empty).user_id.{:state_updated, ...}the anonymous attendee's composer swaps to the login prompt without a reload.Presentations.authenticated_chat_only?/1(default, enabled, unknown event).Not verified, to be explicit about it:
PostFormJS hook path and how the new notice looks on a narrow phone viewport were not checked visually.403inPostControlleris defence in depth and has no test, becausePostControllercurrently has no route inrouter.ex— nothing reaches it today. I added the check so the endpoint cannot become a hole if it is ever wired up.Behavioural edge cases worth knowing:
savethey still push is refused, because the clause reads the live@state. Messages they already posted stay — the moderator's existing pin/delete/ban tools handle those, as before.Fixes #107