v4: a deployment chooses which ways federated sign-in goes — every instance took users of the others and let its own sign in to them, with no way to turn either off. GERBEUR_FEDERATION_INCOMING=0 keeps users of other instances out: the login and register pages offer no other instance, /manage no list of instances, their sign-ins are refused, and those already in are signed out until it is on again, their homes' events still applied meanwhile. GERBEUR_FEDERATION_OUTGOING=0 keeps the instance's users from signing in elsewhere: its /authorize page lets no instance sign them in, its metadata says so (gerbeur_outgoing), which other instances check before sending anyone there, the settings page lists no instances, and at startup every grant is withdrawn, which signs them out there. Both are on by default and reach the client through index.html. The dev instance gerbeur (:3000) takes no users of the others, while its own still sign in to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,12 +8,18 @@ import { makeSession, makeToken } from "../fixtures.ts";
|
||||
import { renderApp } from "../render.tsx";
|
||||
import { AUTH_STORAGE_KEY } from "../../utils/auth/storage.ts";
|
||||
|
||||
const instance = vi.hoisted(() => ({ emailEnabled: false }));
|
||||
const instance = vi.hoisted(() => ({
|
||||
emailEnabled: false,
|
||||
federationOutgoing: true,
|
||||
}));
|
||||
vi.mock("../../config/instance.ts", () => ({
|
||||
DEFAULT_STYLE: "smooth",
|
||||
get EMAIL_ENABLED() {
|
||||
return instance.emailEnabled;
|
||||
},
|
||||
get FEDERATION_OUTGOING() {
|
||||
return instance.federationOutgoing;
|
||||
},
|
||||
}));
|
||||
|
||||
const annSession = makeSession({
|
||||
@@ -35,6 +41,7 @@ const row = (label: string) =>
|
||||
describe("Settings", () => {
|
||||
afterEach(() => {
|
||||
instance.emailEnabled = false;
|
||||
instance.federationOutgoing = true;
|
||||
document.documentElement.removeAttribute("data-style");
|
||||
document.documentElement.removeAttribute("data-color-scheme");
|
||||
});
|
||||
@@ -197,6 +204,14 @@ describe("Settings", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("no instances are listed where accounts don't sign in elsewhere", async () => {
|
||||
instance.federationOutgoing = false;
|
||||
open();
|
||||
expect(await screen.findByText("ann")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Other instances")).not.toBeInTheDocument();
|
||||
expect(calls("GET /api/me/grants")).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("an account from another instance changes its password there", async () => {
|
||||
open(makeSession({ id: "al", username: "alice@home.example" }));
|
||||
const link = await screen.findByRole("link", {
|
||||
|
||||
@@ -8,6 +8,14 @@ import { calls, deferred, fail, route } from "../fetch.ts";
|
||||
import { makeSession, wire } from "../fixtures.ts";
|
||||
import { currentPath, renderWithContexts } from "../render.tsx";
|
||||
|
||||
const instance = vi.hoisted(() => ({ federationIncoming: true }));
|
||||
vi.mock("../../config/instance.ts", async (importOriginal) => ({
|
||||
...await importOriginal<typeof import("../../config/instance.ts")>(),
|
||||
get FEDERATION_INCOMING() {
|
||||
return instance.federationIncoming;
|
||||
},
|
||||
}));
|
||||
|
||||
const home = <Route path="/" element={<p>home page</p>} />;
|
||||
|
||||
describe("UserLogin", () => {
|
||||
@@ -153,6 +161,7 @@ describe("UserLogin", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
assign.mockReset();
|
||||
instance.federationIncoming = true;
|
||||
});
|
||||
|
||||
test("a trusted instance's button, or any address, starts the sign-in there", async () => {
|
||||
@@ -198,6 +207,14 @@ describe("UserLogin", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("isn't offered where accounts of other instances don't sign in", () => {
|
||||
instance.federationIncoming = false;
|
||||
setup();
|
||||
expect(screen.getByPlaceholderText("Username")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/another gerbeur/)).not.toBeInTheDocument();
|
||||
expect(calls("GET /api/federation/peers")).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("a refusal is shown under the field", async () => {
|
||||
route("GET /api/federation/peers", () => []);
|
||||
route(
|
||||
|
||||
Reference in New Issue
Block a user