mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(auth): fix approval signups and auto-join invites (#3467)
This commit is contained in:
1 parent
e403fb6cce
commit
d5ca5d8293
64 files changed
+622
-72
No files matched your search
@@ -91,4 +91,67 @@ describe('Auth registration policy', () => {
|
||||
.expect(403, APIErrorCodes.REGISTRATION_PENDING_APPROVAL)
|
||||
.execute();
|
||||
});
|
||||
|
||||
it('refuses an approval registration without sign-in credentials', async () => {
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/register')
|
||||
.body({global_name: 'Nobody Signs In', date_of_birth: '2000-01-01', consent: true})
|
||||
.expect(400, APIErrorCodes.REGISTRATION_CREDENTIALS_REQUIRED)
|
||||
.execute();
|
||||
expect(await getInstanceConfigRepository().getPendingRegistrations()).toEqual([]);
|
||||
});
|
||||
|
||||
it('refuses an approval registration with an email but no password', async () => {
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
const {password: _password, ...body} = registrationBody('nopassword');
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/register')
|
||||
.body(body)
|
||||
.expect(400, APIErrorCodes.REGISTRATION_CREDENTIALS_REQUIRED)
|
||||
.execute();
|
||||
expect(await getInstanceConfigRepository().getPendingRegistrations()).toEqual([]);
|
||||
});
|
||||
|
||||
it('refuses an approval registration with a password but no email', async () => {
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
const {email: _email, ...body} = registrationBody('noemail');
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/register')
|
||||
.body(body)
|
||||
.expect(400, APIErrorCodes.REGISTRATION_CREDENTIALS_REQUIRED)
|
||||
.execute();
|
||||
expect(await getInstanceConfigRepository().getPendingRegistrations()).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps the registration URL seat when an approval registration is refused', async () => {
|
||||
const repository = getInstanceConfigRepository();
|
||||
await repository.setRegistrationConfig({mode: 'open', admin_registration_urls_enabled: true});
|
||||
const {code} = await repository.createRegistrationUrl({
|
||||
label: 'Reviewed link',
|
||||
createdByUserId: '1',
|
||||
expiresAt: null,
|
||||
maxUses: 1,
|
||||
approvalRequired: true,
|
||||
});
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/register')
|
||||
.body({global_name: 'Seat Keeper', date_of_birth: '2000-01-01', consent: true, registration_url_code: code})
|
||||
.expect(400, APIErrorCodes.REGISTRATION_CREDENTIALS_REQUIRED)
|
||||
.execute();
|
||||
const registration = await createBuilderWithoutAuth<PendingRegistrationResponse>(harness)
|
||||
.post('/auth/register')
|
||||
.body({...registrationBody('seatkeeper'), registration_url_code: code})
|
||||
.execute();
|
||||
expect(registration.registration_pending_approval).toBe(true);
|
||||
});
|
||||
|
||||
it('still accepts an unclaimed registration in open mode', async () => {
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'open'});
|
||||
const registration = await createBuilderWithoutAuth<RegistrationTokenResponse>(harness)
|
||||
.post('/auth/register')
|
||||
.body({global_name: 'Open Unclaimed', date_of_birth: '2000-01-01', consent: true})
|
||||
.execute();
|
||||
expect(registration.token.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
@@ -428,8 +428,11 @@ describe('signups racing on registration URLs and pending approvals', () => {
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
const fresh = await register('pendingafter');
|
||||
|
||||
const listed = (await readPendingRegistrations()).map((entry) => entry.user_id);
|
||||
expect(listed.toSorted()).toEqual([account.userId, fresh.json.user_id].toSorted());
|
||||
const pendingRegistrations = await readPendingRegistrations();
|
||||
expect(pendingRegistrations.map((entry) => entry.user_id).toSorted()).toEqual(
|
||||
[account.userId, fresh.json.user_id].toSorted(),
|
||||
);
|
||||
expect(pendingRegistrations.find((entry) => entry.user_id === account.userId)?.invite_code).toBeNull();
|
||||
|
||||
const decided = await createBuilder<PendingRegistrationListResponse>(harness, admin.token)
|
||||
.patch(`/admin/instance/pending-registrations/${account.userId}`)
|
||||
|
||||
@@ -9,10 +9,13 @@ import {
|
||||
setUserACLs,
|
||||
type TestAccount,
|
||||
} from '@app/api/auth/tests/AuthTestUtils';
|
||||
import {setupTestGuildWithMembers} from '@app/api/guild/tests/GuildTestUtils';
|
||||
import {Config} from '@app/api/Config';
|
||||
import {createChannelInvite, setupTestGuildWithMembers} from '@app/api/guild/tests/GuildTestUtils';
|
||||
import {getInstanceConfigRepository} from '@app/api/middleware/ServiceSingletons';
|
||||
import type {ApiTestHarness} from '@app/api/test/ApiTestHarness';
|
||||
import {createBuilder, createBuilderWithoutAuth} from '@app/api/test/TestRequestBuilder';
|
||||
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
|
||||
import type {PendingRegistrationListResponse} from '@fluxer/schema/src/domains/admin/AdminSchemas';
|
||||
import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it} from 'vitest';
|
||||
|
||||
interface SsoStartResponse {
|
||||
@@ -503,6 +506,79 @@ describe('Auth SSO flow', () => {
|
||||
.expect(200)
|
||||
.execute();
|
||||
});
|
||||
it('joins a provisioned user to the auto-join invite community', async () => {
|
||||
const {owner, guild} = await setupTestGuildWithMembers(harness, 0);
|
||||
const invite = await createChannelInvite(harness, owner.token, guild.system_channel_id!);
|
||||
await enableSso(harness, admin.token);
|
||||
const originalAutoJoinInviteCode = Config.instance.autoJoinInviteCode;
|
||||
Config.instance.autoJoinInviteCode = invite.code;
|
||||
try {
|
||||
const startData = await createBuilderWithoutAuth<SsoStartResponse>(harness)
|
||||
.post('/auth/sso/start')
|
||||
.body({})
|
||||
.execute();
|
||||
const completeData = await createBuilderWithoutAuth<SsoCompleteResponse>(harness)
|
||||
.post('/auth/sso/complete')
|
||||
.body({
|
||||
code: `sso-auto-join-${Date.now()}@example.com`,
|
||||
state: startData.state,
|
||||
})
|
||||
.execute();
|
||||
await createBuilder(harness, owner.token)
|
||||
.get(`/guilds/${guild.id}/members/${completeData.user_id}`)
|
||||
.expect(200)
|
||||
.execute();
|
||||
} finally {
|
||||
Config.instance.autoJoinInviteCode = originalAutoJoinInviteCode;
|
||||
}
|
||||
});
|
||||
it('replays the auto-join invite when an SSO registration is approved', async () => {
|
||||
const reviewer = await setUserACLs(harness, await createTestAccount(harness), [
|
||||
AdminACLs.AUTHENTICATE,
|
||||
AdminACLs.REGISTRATION_REVIEW,
|
||||
]);
|
||||
const {owner, guild} = await setupTestGuildWithMembers(harness, 0);
|
||||
const invite = await createChannelInvite(harness, owner.token, guild.system_channel_id!);
|
||||
await enableSso(harness, admin.token);
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
const originalAutoJoinInviteCode = Config.instance.autoJoinInviteCode;
|
||||
Config.instance.autoJoinInviteCode = invite.code;
|
||||
try {
|
||||
const startData = await createBuilderWithoutAuth<SsoStartResponse>(harness)
|
||||
.post('/auth/sso/start')
|
||||
.body({})
|
||||
.execute();
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/sso/complete')
|
||||
.body({
|
||||
code: `sso-auto-join-approval-${Date.now()}@example.com`,
|
||||
state: startData.state,
|
||||
})
|
||||
.expect(403, 'REGISTRATION_PENDING_APPROVAL')
|
||||
.execute();
|
||||
const listed = await createBuilder<PendingRegistrationListResponse>(harness, reviewer.token)
|
||||
.get('/admin/instance/pending-registrations')
|
||||
.expect(200)
|
||||
.execute();
|
||||
expect(listed.pending_registrations).toEqual([expect.objectContaining({invite_code: invite.code})]);
|
||||
const pendingUserId = listed.pending_registrations[0]!.user_id;
|
||||
await createBuilder(harness, owner.token)
|
||||
.get(`/guilds/${guild.id}/members/${pendingUserId}`)
|
||||
.expect(404)
|
||||
.execute();
|
||||
await createBuilder(harness, reviewer.token)
|
||||
.patch(`/admin/instance/pending-registrations/${pendingUserId}`)
|
||||
.body({status: 'approved'})
|
||||
.expect(200)
|
||||
.execute();
|
||||
await createBuilder(harness, owner.token)
|
||||
.get(`/guilds/${guild.id}/members/${pendingUserId}`)
|
||||
.expect(200)
|
||||
.execute();
|
||||
} finally {
|
||||
Config.instance.autoJoinInviteCode = originalAutoJoinInviteCode;
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('existing user login', () => {
|
||||
let admin: TestAccount;
|
||||
|
||||
@@ -404,6 +404,29 @@ describe('Username sign-in', () => {
|
||||
expect(user.username.toLowerCase().startsWith('sharedname')).toBe(true);
|
||||
expect(user.isUnclaimedAccount()).toBe(true);
|
||||
});
|
||||
|
||||
it('refuses a credential-less approval registration on a username instance', async () => {
|
||||
await useUsernameMode();
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
await createBuilderWithoutAuth(harness)
|
||||
.post('/auth/register')
|
||||
.body({global_name: 'No Credentials', date_of_birth: '2000-01-01'})
|
||||
.expect(400, APIErrorCodes.REGISTRATION_CREDENTIALS_REQUIRED)
|
||||
.execute();
|
||||
expect(await getInstanceConfigRepository().getPendingRegistrations()).toEqual([]);
|
||||
});
|
||||
|
||||
it('accepts an approval registration with a username and password', async () => {
|
||||
await useUsernameMode();
|
||||
await getInstanceConfigRepository().setRegistrationConfig({mode: 'approval'});
|
||||
const registration = await createBuilderWithoutAuth<{registration_pending_approval: true; user_id: string}>(
|
||||
harness,
|
||||
)
|
||||
.post('/auth/register')
|
||||
.body({username: 'reviewedperson', password: PASSWORD, date_of_birth: '2000-01-01'})
|
||||
.execute();
|
||||
expect(registration).toEqual({registration_pending_approval: true, user_id: expect.any(String)});
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', () => {
|
||||
|
||||
Reference in new issue
Block a user