Skip to content

Commit ba37cf5

Browse files
committed
[server] restrict allowed phone numbers
1 parent 689b7f8 commit ba37cf5

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { MigrationInterface, QueryRunner } from "typeorm";
8+
import { columnExists } from "./helper/helper";
9+
10+
const D_B_USER = "d_b_user";
11+
const COL_PHONE_NUMBER = "verificationPhoneNumber";
12+
13+
export class IndexPhoneNumber1663784254956 implements MigrationInterface {
14+
public async up(queryRunner: QueryRunner): Promise<void> {
15+
if (!(await columnExists(queryRunner, D_B_USER, COL_PHONE_NUMBER))) {
16+
await queryRunner.query(
17+
`ALTER TABLE ${D_B_USER} ADD INDEX (${COL_PHONE_NUMBER}), ALGORITHM=INPLACE, LOCK=NONE `,
18+
);
19+
}
20+
}
21+
22+
public async down(queryRunner: QueryRunner): Promise<void> {}
23+
}

components/gitpod-db/src/typeorm/user-db-impl.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,22 @@ export class TypeORMUserDBImpl implements UserDB {
570570
async getByRefreshToken(refreshTokenToken: string): Promise<OAuthToken> {
571571
throw new Error("Not implemented");
572572
}
573+
574+
async countUsagesOfPhoneNumber(phoneNumber: string): Promise<number> {
575+
return (await this.getUserRepo())
576+
.createQueryBuilder()
577+
.where("verificationPhoneNumber = :phoneNumber", { phoneNumber })
578+
.getCount();
579+
}
580+
581+
async isBlockedPhoneNumber(phoneNumber: string): Promise<boolean> {
582+
const blockedUsers = await (await this.getUserRepo())
583+
.createQueryBuilder()
584+
.where("verificationPhoneNumber = :phoneNumber", { phoneNumber })
585+
.andWhere("blocked = true")
586+
.getCount();
587+
return blockedUsers > 0;
588+
}
573589
}
574590

575591
export class TransactionalUserDBImpl extends TypeORMUserDBImpl {

components/gitpod-db/src/user-db.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository {
146146
storeGitpodToken(token: GitpodToken & { user: DBUser }): Promise<void>;
147147
deleteGitpodToken(tokenHash: string): Promise<void>;
148148
deleteGitpodTokensNamedLike(userId: string, namePattern: string): Promise<void>;
149+
countUsagesOfPhoneNumber(phoneNumber: string): Promise<number>;
150+
isBlockedPhoneNumber(phoneNumber: string): Promise<boolean>;
149151
}
150152
export type PartialUserUpdate = Partial<Omit<User, "identities">> & Pick<User, "id">;
151153

components/server/src/auth/verification-service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import { inject, injectable, postConstruct } from "inversify";
1010
import { Config } from "../config";
1111
import { Twilio } from "twilio";
1212
import { ServiceContext } from "twilio/lib/rest/verify/v2/service";
13-
import { WorkspaceDB } from "@gitpod/gitpod-db/lib";
13+
import { UserDB, WorkspaceDB } from "@gitpod/gitpod-db/lib";
1414
import { ConfigCatClientFactory } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
1515

1616
@injectable()
1717
export class VerificationService {
1818
@inject(Config) protected config: Config;
1919
@inject(WorkspaceDB) protected workspaceDB: WorkspaceDB;
20+
@inject(UserDB) protected userDB: UserDB;
2021
@inject(ConfigCatClientFactory) protected readonly configCatClientFactory: ConfigCatClientFactory;
2122

2223
protected verifyService: ServiceContext;
@@ -59,6 +60,14 @@ export class VerificationService {
5960
if (!this.verifyService) {
6061
throw new Error("No verification service configured.");
6162
}
63+
const isBlockedNumber = this.userDB.isBlockedPhoneNumber(phoneNumber);
64+
const usages = await this.userDB.countUsagesOfPhoneNumber(phoneNumber);
65+
if (usages > 3) {
66+
throw new Error("The given phone number has been used more than three times.");
67+
}
68+
if (await isBlockedNumber) {
69+
throw new Error("The given phone number is blocked due to abuse.");
70+
}
6271
const verification = await this.verifyService.verifications.create({ to: phoneNumber, channel: "sms" });
6372
log.info("Verification code sent", { phoneNumber, status: verification.status });
6473
}

0 commit comments

Comments
 (0)