Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/backend/src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ConflictException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
import { BadRequestException, ConflictException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
import { AuthController, NEUTRAL_PASSWORD_RESET, NEUTRAL_REGISTRATION } from './auth.controller';
import { ProductEventService } from '../audit/product-event.service';

Expand Down Expand Up @@ -280,6 +280,17 @@ describe('AuthController — answers that do not reveal accounts', () => {
expect(sent).toEqual(['verify new@example.com']);
});

it('refuses a throwaway address before looking it up, and creates nothing', async () => {
const { controller, users, usersService, sent } = makeController({ mode: 'cloud' });
await expect(controller.register({}, signup('lala@yopmail.com'))).rejects.toBeInstanceOf(
BadRequestException,
);
await flush();
expect(usersService.findByEmail).not.toHaveBeenCalled();
expect(users).toEqual([]);
expect(sent).toEqual([]);
});

it('does not sign anyone in to an existing account with the sign-up password', async () => {
const { controller } = makeController({ mode: 'cloud', accounts: [EXISTING] });
await controller.register({}, signup('taken@example.com'));
Expand All @@ -302,6 +313,13 @@ describe('AuthController — answers that do not reveal accounts', () => {
);
});

it('accepts a throwaway address: the operator decides who signs up', async () => {
const { controller } = makeController({ mode: 'self-hosted' });
await expect(controller.register({}, signup('lala@yopmail.com'))).resolves.toMatchObject({
accessToken: 'jwt',
});
});

it('makes the first user an admin of a new workspace', async () => {
const { controller } = makeController({ mode: 'self-hosted', openRegistration: false });
await expect(controller.register({}, signup('first@example.com'))).resolves.toMatchObject({
Expand Down
8 changes: 8 additions & 0 deletions packages/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { Roles, RolesGuard } from './roles.guard';
import { SelfHostedOnlyGuard } from '../common/self-hosted-only.guard';
import { EditionService } from '../ee/licensing/edition.service';
import { SignupAttributionDto } from './signup-attribution.dto';
import { DISPOSABLE_EMAIL_MESSAGE, isDisposableEmail } from './disposable-email.util';

/**
* Registration and password reset answer the same whether or not the address
Expand Down Expand Up @@ -532,6 +533,13 @@ export class AuthController {
* which works only for the account it has just created.
*/
private async registerCloud(req: any, dto: RegisterDto) {
// A throwaway inbox buys a fresh trial every week (the licence site grants
// one per address). Refused on the domain alone, before the address is
// looked up, so the answer says nothing about existing accounts.
if (isDisposableEmail(dto.email)) {
throw new BadRequestException(DISPOSABLE_EMAIL_MESSAGE);
}

const startedAt = Date.now();
const existing = await this.usersService.findByEmail(dto.email);

Expand Down
Loading
Loading