Skip to content

Commit

Permalink
Hotfix custom captcha config (#1584) (#1585)
Browse files Browse the repository at this point in the history
  • Loading branch information
forgetso authored Dec 21, 2024
2 parents 9f12acf + 2cfecf4 commit 75f9d6b
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 44 deletions.
6 changes: 3 additions & 3 deletions packages/database/src/databases/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ export class ProviderDatabase
const docs = await this.tables?.commitment
.find({
$or: [
{ $storedAtTimestamp: { $exists: false } },
{ storedAtTimestamp: { $exists: false } },
{
$expr: { $lt: ["$storedAtTimestamp", "$lastUpdatedTimestamp"] },
},
Expand Down Expand Up @@ -793,7 +793,7 @@ export class ProviderDatabase
const docs = await this.tables?.powcaptcha
.find<PoWCaptchaRecord[]>({
$or: [
{ $storedAtTimestamp: { $exists: false } },
{ storedAtTimestamp: { $exists: false } },
{
$expr: { $lt: ["$storedAtTimestamp", "$lastUpdatedTimestamp"] },
},
Expand Down Expand Up @@ -1285,7 +1285,7 @@ export class ProviderDatabase
): Promise<ScheduledTaskRecord | undefined> {
const cursor: ScheduledTaskRecord | undefined | null =
await this.tables?.scheduler
?.findOne({ taskId: taskId, status: status })
?.findOne({ _id: taskId, status: status })
.lean<ScheduledTaskRecord>();
return cursor ? cursor : undefined;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/provider/src/api/captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import type { ProviderEnvironment } from "@prosopo/types-env";
import { flatten } from "@prosopo/util";
import express, { type Router } from "express";
import { getBotScore } from "../tasks/detection/getBotScore.js";
import { getCaptchaConfig } from "../tasks/imgCaptcha/imgCaptchaTasksUtils.js";
import { Tasks } from "../tasks/tasks.js";
import { getIPAddress } from "../util.js";
import { handleErrors } from "./errorHandler.js";
Expand Down Expand Up @@ -108,7 +109,9 @@ export function prosopoRouter(env: ProviderEnvironment): Router {
);
}

const captchaConfig = await tasks.imgCaptchaManager.getCaptchaConfig(
const captchaConfig = await getCaptchaConfig(
tasks.db,
env.config,
ipAddress,
user,
dapp,
Expand Down
38 changes: 0 additions & 38 deletions packages/provider/src/tasks/imgCaptcha/imgCaptchaTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,44 +465,6 @@ export class ImgCaptchaManager {
};
}

async getCaptchaConfig(
ipAddress: Address4 | Address6,
user: string,
dapp: string,
): Promise<CaptchaConfig> {
const ipRule = await checkIpRules(this.db, ipAddress, dapp);
if (ipRule) {
return {
solved: {
count:
ipRule?.captchaConfig?.solved.count ||
this.config.captchas.solved.count,
},
unsolved: {
count:
ipRule?.captchaConfig?.unsolved.count ||
this.config.captchas.unsolved.count,
},
};
}
const userRule = await checkUserRules(this.db, user, dapp);
if (userRule) {
return {
solved: {
count:
userRule?.captchaConfig?.solved.count ||
this.config.captchas.solved.count,
},
unsolved: {
count:
userRule?.captchaConfig?.unsolved.count ||
this.config.captchas.unsolved.count,
},
};
}
return this.config.captchas;
}

checkLangRules(acceptLanguage: string): number {
return checkLangRules(this.config, acceptLanguage);
}
Expand Down
56 changes: 55 additions & 1 deletion packages/provider/src/tasks/imgCaptcha/imgCaptchaTasksUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ import {
CaptchaMerkleTree,
computeCaptchaSolutionHash,
} from "@prosopo/datasets";
import type { CaptchaSolution } from "@prosopo/types";
import type {
CaptchaConfig,
CaptchaSolution,
ProsopoConfigOutput,
} from "@prosopo/types";
import type { IProviderDatabase } from "@prosopo/types-database";
import type { Address4, Address6 } from "ip-address";
import { checkIpRules } from "../../rules/ip.js";
import { checkUserRules } from "../../rules/user.js";

/**
* Build merkle tree and get commitment from contract, returning the tree, commitment, and commitmentId
Expand Down Expand Up @@ -47,3 +55,49 @@ export const buildTreeAndGetCommitmentId = (

return { tree, commitmentId };
};

/**
* Get the captcha config for the user and ip address or return the default captcha config
* @param db
* @param config
* @param ipAddress
* @param user
* @param dapp
*/
export const getCaptchaConfig = async (
db: IProviderDatabase,
config: ProsopoConfigOutput,
ipAddress: Address4 | Address6,
user: string,
dapp: string,
): Promise<CaptchaConfig> => {
const ipRule = await checkIpRules(db, ipAddress, dapp);
if (ipRule) {
return {
solved: {
count:
ipRule?.captchaConfig?.solved.count || config.captchas.solved.count,
},
unsolved: {
count:
ipRule?.captchaConfig?.unsolved.count ||
config.captchas.unsolved.count,
},
};
}
const userRule = await checkUserRules(db, user, dapp);
if (userRule) {
return {
solved: {
count:
userRule?.captchaConfig?.solved.count || config.captchas.solved.count,
},
unsolved: {
count:
userRule?.captchaConfig?.unsolved.count ||
config.captchas.unsolved.count,
},
};
}
return config.captchas;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,21 @@ import {
computeCaptchaSolutionHash,
} from "@prosopo/datasets";
import type { CaptchaSolution } from "@prosopo/types";
import {
BlockRuleType,
type IPAddressBlockRule,
type IProviderDatabase,
type UserAccountBlockRule,
type UserAccountBlockRuleRecord,
} from "@prosopo/types-database";
import { Address4 } from "ip-address";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { buildTreeAndGetCommitmentId } from "../../../../tasks/imgCaptcha/imgCaptchaTasksUtils.js";
import { checkIpRules } from "../../../../rules/ip.js";
import { checkUserRules } from "../../../../rules/user.js";
import {
buildTreeAndGetCommitmentId,
getCaptchaConfig,
} from "../../../../tasks/imgCaptcha/imgCaptchaTasksUtils.js";

vi.mock("@prosopo/datasets", () => ({
CaptchaMerkleTree: vi.fn().mockImplementation(() => ({
Expand All @@ -28,6 +41,18 @@ vi.mock("@prosopo/datasets", () => ({
computeCaptchaSolutionHash: vi.fn(),
}));

vi.mock("../../../../rules/ip.js", () => {
return {
checkIpRules: vi.fn(),
};
});

vi.mock("../../../../rules/user.js", () => {
return {
checkUserRules: vi.fn(),
};
});

describe("buildTreeAndGetCommitmentId", () => {
const mockCaptchaSolutions = [
{ challenge: "challenge1", solution: "solution1", salt: "salt1" },
Expand Down Expand Up @@ -79,3 +104,98 @@ describe("buildTreeAndGetCommitmentId", () => {
);
});
});

describe("getCaptchaConfig", () => {
it("should return the default captcha config if no rules are found", async () => {
const db = {
getIPBlockRuleRecord: vi.fn().mockResolvedValue(null),
getUserBlockRuleRecord: vi.fn().mockResolvedValue(null),
} as unknown as IProviderDatabase;
const config = {
captchas: {
solved: { count: 1 },
unsolved: { count: 2 },
},
};
const ipAddress = new Address4("1.1.1.1");
const user = "mockedUser";
const dapp = "mockedDapp";

// @ts-ignore
const result = await getCaptchaConfig(db, config, ipAddress, user, dapp);

expect(result).toEqual({
solved: { count: 1 },
unsolved: { count: 2 },
});
});
it("should return the users config if there is one specified against the user's ip address", async () => {
const ipRule: IPAddressBlockRule = {
ip: 16843009, // 1.1.1.1
global: false,
hardBlock: false,
type: BlockRuleType.ipAddress,
captchaConfig: {
solved: { count: 3 },
unsolved: { count: 4 },
},
};
// biome-ignore lint/suspicious/noExplicitAny: tests
(checkIpRules as any).mockReturnValue(ipRule);
const db = {
getIPBlockRuleRecord: vi.fn().mockResolvedValue(ipRule),
getUserBlockRuleRecord: vi.fn().mockResolvedValue(null),
} as unknown as IProviderDatabase;
const config = {
captchas: {
solved: { count: 1 },
unsolved: { count: 2 },
},
};
const ipAddress = new Address4("1.1.1.1"); // 16843009
const user = "mockedUser";
const dapp = "mockedDapp";
// @ts-ignore
const result = await getCaptchaConfig(db, config, ipAddress, user, dapp);
expect(result).toEqual({
solved: { count: 3 },
unsolved: { count: 4 },
});
});
it("should return the user's config if there is one specified against the user's account", async () => {
const userRule: UserAccountBlockRule = {
userAccount: "mockedUser",
dappAccount: "mockedDapp",
global: false,
hardBlock: false,
type: BlockRuleType.userAccount,
captchaConfig: {
solved: { count: 5 },
unsolved: { count: 6 },
},
};
// biome-ignore lint/suspicious/noExplicitAny: tests
(checkIpRules as any).mockReturnValue(null);
const db = {
getIPBlockRuleRecord: vi.fn().mockResolvedValue(null),
getUserBlockRuleRecord: vi.fn().mockResolvedValue(userRule),
} as unknown as IProviderDatabase;
// biome-ignore lint/suspicious/noExplicitAny: tests
(checkUserRules as any).mockReturnValue(userRule);
const config = {
captchas: {
solved: { count: 1 },
unsolved: { count: 2 },
},
};
const ipAddress = new Address4("1.1.1.1");
const user = "mockedUser";
const dapp = "mockedDapp";
// @ts-ignore
const result = await getCaptchaConfig(db, config, ipAddress, user, dapp);
expect(result).toEqual({
solved: { count: 5 },
unsolved: { count: 6 },
});
});
});
13 changes: 13 additions & 0 deletions packages/types-database/src/types/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ export const PoWCaptchaRecordSchema = new Schema<PoWCaptchaRecord>({

// Set an index on the captchaId field, ascending
PoWCaptchaRecordSchema.index({ challenge: 1 });
PoWCaptchaRecordSchema.index({ storedAtTimestamp: 1 });
PoWCaptchaRecordSchema.index({ storedAtTimestamp: 1, lastUpdatedTimestamp: 1 });

export const UserCommitmentRecordSchema = new Schema<UserCommitmentRecord>({
userAccount: { type: String, required: true },
Expand Down Expand Up @@ -223,6 +225,12 @@ export const UserCommitmentRecordSchema = new Schema<UserCommitmentRecord>({
});
// Set an index on the commitment id field, descending
UserCommitmentRecordSchema.index({ id: -1 });
UserCommitmentRecordSchema.index({ storedAtTimestamp: 1 });
UserCommitmentRecordSchema.index({
storedAtTimestamp: 1,
lastUpdatedTimestamp: 1,
});
UserCommitmentRecordSchema.index({ userAccount: 1, dappAccount: 1 });

export const DatasetRecordSchema = new Schema<DatasetWithIds>({
contentTree: { type: [[String]], required: true },
Expand Down Expand Up @@ -332,6 +340,9 @@ export const ScheduledTaskRecordSchema = new Schema<ScheduledTaskMongoose>({
required: false,
},
});
ScheduledTaskRecordSchema.index({ processName: 1 });
ScheduledTaskRecordSchema.index({ processName: 1, status: 1 });
ScheduledTaskRecordSchema.index({ _id: 1, status: 1 });

export type FrictionlessToken = {
token: string;
Expand Down Expand Up @@ -371,6 +382,8 @@ export const SessionRecordSchema = new Schema<SessionRecord>({
},
});

SessionRecordSchema.index({ sessionId: 1 }, { unique: true });

export type BlockRule = {
global: boolean;
type: BlockRuleType;
Expand Down

0 comments on commit 75f9d6b

Please sign in to comment.