Skip to content

Commit

Permalink
[server] restrict allowed phone numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
svenefftinge committed Sep 22, 2022
1 parent c5c5be9 commit e504643
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/

import { MigrationInterface, QueryRunner } from "typeorm";
import { columnExists } from "./helper/helper";

const D_B_USER = "d_b_user";
const COL_PHONE_NUMBER = "verificationPhoneNumber";

export class IndexPhoneNumber1663784254956 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!(await columnExists(queryRunner, D_B_USER, COL_PHONE_NUMBER))) {
await queryRunner.query(
`ALTER TABLE ${D_B_USER} ADD INDEX (${COL_PHONE_NUMBER}), ALGORITHM=INPLACE, LOCK=NONE `,
);
}
}

public async down(queryRunner: QueryRunner): Promise<void> {}
}
16 changes: 16 additions & 0 deletions components/gitpod-db/src/typeorm/user-db-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,22 @@ export class TypeORMUserDBImpl implements UserDB {
async getByRefreshToken(refreshTokenToken: string): Promise<OAuthToken> {
throw new Error("Not implemented");
}

async countUsagesOfPhoneNumber(phoneNumber: string): Promise<number> {
return (await this.getUserRepo())
.createQueryBuilder()
.where("verificationPhoneNumber = :phoneNumber", { phoneNumber })
.getCount();
}

async isBlockedPhoneNumber(phoneNumber: string): Promise<boolean> {
const blockedUsers = await (await this.getUserRepo())
.createQueryBuilder()
.where("verificationPhoneNumber = :phoneNumber", { phoneNumber })
.andWhere("blocked = true")
.getCount();
return blockedUsers > 0;
}
}

export class TransactionalUserDBImpl extends TypeORMUserDBImpl {
Expand Down
2 changes: 2 additions & 0 deletions components/gitpod-db/src/user-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository {
storeGitpodToken(token: GitpodToken & { user: DBUser }): Promise<void>;
deleteGitpodToken(tokenHash: string): Promise<void>;
deleteGitpodTokensNamedLike(userId: string, namePattern: string): Promise<void>;
countUsagesOfPhoneNumber(phoneNumber: string): Promise<number>;
isBlockedPhoneNumber(phoneNumber: string): Promise<boolean>;
}
export type PartialUserUpdate = Partial<Omit<User, "identities">> & Pick<User, "id">;

Expand Down
11 changes: 10 additions & 1 deletion components/server/src/auth/verification-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { inject, injectable, postConstruct } from "inversify";
import { Config } from "../config";
import { Twilio } from "twilio";
import { ServiceContext } from "twilio/lib/rest/verify/v2/service";
import { WorkspaceDB } from "@gitpod/gitpod-db/lib";
import { UserDB, WorkspaceDB } from "@gitpod/gitpod-db/lib";
import { ConfigCatClientFactory } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";

@injectable()
export class VerificationService {
@inject(Config) protected config: Config;
@inject(WorkspaceDB) protected workspaceDB: WorkspaceDB;
@inject(UserDB) protected userDB: UserDB;
@inject(ConfigCatClientFactory) protected readonly configCatClientFactory: ConfigCatClientFactory;

protected verifyService: ServiceContext;
Expand Down Expand Up @@ -59,6 +60,14 @@ export class VerificationService {
if (!this.verifyService) {
throw new Error("No verification service configured.");
}
const isBlockedNumber = this.userDB.isBlockedPhoneNumber(phoneNumber);
const usages = await this.userDB.countUsagesOfPhoneNumber(phoneNumber);
if (usages > 3) {
throw new Error("The given phone number has been used more than three times.");
}
if (await isBlockedNumber) {
throw new Error("The given phone number is blocked due to abuse.");
}
const verification = await this.verifyService.verifications.create({ to: phoneNumber, channel: "sms" });
log.info("Verification code sent", { phoneNumber, status: verification.status });
}
Expand Down
3 changes: 0 additions & 3 deletions components/server/src/user/user-deletion-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@ export class UserDeletionService {
user.avatarUrl = "deleted-avatarUrl";
user.fullName = "deleted-fullName";
user.name = "deleted-Name";
if (user.verificationPhoneNumber) {
user.verificationPhoneNumber = "deleted-phoneNumber";
}
}

protected deleteIdentities(user: User) {
Expand Down

0 comments on commit e504643

Please sign in to comment.