@prosopo/provider 3.2.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/api/admin/apiAdminRoutesProvider.js +26 -21
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +21 -20
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +27 -27
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +33 -33
- package/dist/api/admin/createApiAdminRoutesProvider.js +8 -5
- package/dist/api/blacklistRequestInspector.js +115 -98
- package/dist/api/block.js +13 -8
- package/dist/api/captcha.js +519 -356
- package/dist/api/domainMiddleware.js +75 -68
- package/dist/api/headerCheckMiddleware.js +26 -25
- package/dist/api/ignoreMiddleware.js +12 -10
- package/dist/api/ja4Middleware.js +74 -82
- package/dist/api/public.js +26 -23
- package/dist/api/robotsMiddleware.js +11 -9
- package/dist/api/validateAddress.js +20 -18
- package/dist/api/verify.js +133 -95
- package/dist/cjs/api/captcha.cjs +45 -36
- package/dist/cjs/tasks/captchaManager.cjs +24 -10
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +0 -3
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +1 -1
- package/dist/index.js +38 -15
- package/dist/rules/lang.js +14 -14
- package/dist/schedulers/captchaScheduler.js +28 -23
- package/dist/schedulers/getClientList.js +29 -24
- package/dist/tasks/captchaManager.js +109 -85
- package/dist/tasks/client/clientTasks.js +265 -204
- package/dist/tasks/dataset/datasetTasks.js +27 -16
- package/dist/tasks/dataset/datasetTasksUtils.js +31 -31
- package/dist/tasks/detection/decodePayload.js +378 -208
- package/dist/tasks/detection/getBotScore.js +12 -10
- package/dist/tasks/frictionless/frictionlessTasks.js +117 -119
- package/dist/tasks/frictionless/frictionlessTasksUtils.js +10 -5
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +360 -271
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +23 -16
- package/dist/tasks/index.js +4 -2
- package/dist/tasks/powCaptcha/powTasks.js +157 -97
- package/dist/tasks/powCaptcha/powTasksUtils.js +24 -20
- package/dist/tasks/tasks.js +53 -29
- package/dist/util.js +98 -88
- package/package.json +31 -27
- package/vite.cjs.config.ts +4 -1
- package/vite.esm.config.ts +20 -0
- package/vite.test.config.ts +15 -3
- package/dist/api/admin/apiAdminRoutesProvider.d.ts +0 -9
- package/dist/api/admin/apiAdminRoutesProvider.d.ts.map +0 -1
- package/dist/api/admin/apiAdminRoutesProvider.js.map +0 -1
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts +0 -14
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts.map +0 -1
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js.map +0 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts +0 -14
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +0 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js.map +0 -1
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts +0 -14
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts.map +0 -1
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js.map +0 -1
- package/dist/api/admin/createApiAdminRoutesProvider.d.ts +0 -4
- package/dist/api/admin/createApiAdminRoutesProvider.d.ts.map +0 -1
- package/dist/api/admin/createApiAdminRoutesProvider.js.map +0 -1
- package/dist/api/blacklistRequestInspector.d.ts +0 -39
- package/dist/api/blacklistRequestInspector.d.ts.map +0 -1
- package/dist/api/blacklistRequestInspector.js.map +0 -1
- package/dist/api/block.d.ts +0 -3
- package/dist/api/block.d.ts.map +0 -1
- package/dist/api/block.js.map +0 -1
- package/dist/api/captcha.d.ts +0 -4
- package/dist/api/captcha.d.ts.map +0 -1
- package/dist/api/captcha.js.map +0 -1
- package/dist/api/domainMiddleware.d.ts +0 -4
- package/dist/api/domainMiddleware.d.ts.map +0 -1
- package/dist/api/domainMiddleware.js.map +0 -1
- package/dist/api/headerCheckMiddleware.d.ts +0 -4
- package/dist/api/headerCheckMiddleware.d.ts.map +0 -1
- package/dist/api/headerCheckMiddleware.js.map +0 -1
- package/dist/api/ignoreMiddleware.d.ts +0 -3
- package/dist/api/ignoreMiddleware.d.ts.map +0 -1
- package/dist/api/ignoreMiddleware.js.map +0 -1
- package/dist/api/ja4Middleware.d.ts +0 -10
- package/dist/api/ja4Middleware.d.ts.map +0 -1
- package/dist/api/ja4Middleware.js.map +0 -1
- package/dist/api/public.d.ts +0 -3
- package/dist/api/public.d.ts.map +0 -1
- package/dist/api/public.js.map +0 -1
- package/dist/api/robotsMiddleware.d.ts +0 -3
- package/dist/api/robotsMiddleware.d.ts.map +0 -1
- package/dist/api/robotsMiddleware.js.map +0 -1
- package/dist/api/validateAddress.d.ts +0 -5
- package/dist/api/validateAddress.d.ts.map +0 -1
- package/dist/api/validateAddress.js.map +0 -1
- package/dist/api/verify.d.ts +0 -4
- package/dist/api/verify.d.ts.map +0 -1
- package/dist/api/verify.js.map +0 -1
- package/dist/index.d.ts +0 -15
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/rules/lang.d.ts +0 -3
- package/dist/rules/lang.d.ts.map +0 -1
- package/dist/rules/lang.js.map +0 -1
- package/dist/schedulers/captchaScheduler.d.ts +0 -4
- package/dist/schedulers/captchaScheduler.d.ts.map +0 -1
- package/dist/schedulers/captchaScheduler.js.map +0 -1
- package/dist/schedulers/getClientList.d.ts +0 -4
- package/dist/schedulers/getClientList.d.ts.map +0 -1
- package/dist/schedulers/getClientList.js.map +0 -1
- package/dist/tasks/captchaManager.d.ts +0 -48
- package/dist/tasks/captchaManager.d.ts.map +0 -1
- package/dist/tasks/captchaManager.js.map +0 -1
- package/dist/tasks/client/clientTasks.d.ts +0 -21
- package/dist/tasks/client/clientTasks.d.ts.map +0 -1
- package/dist/tasks/client/clientTasks.js.map +0 -1
- package/dist/tasks/dataset/datasetTasks.d.ts +0 -13
- package/dist/tasks/dataset/datasetTasks.d.ts.map +0 -1
- package/dist/tasks/dataset/datasetTasks.js.map +0 -1
- package/dist/tasks/dataset/datasetTasksUtils.d.ts +0 -3
- package/dist/tasks/dataset/datasetTasksUtils.d.ts.map +0 -1
- package/dist/tasks/dataset/datasetTasksUtils.js.map +0 -1
- package/dist/tasks/detection/decodePayload.d.ts +0 -5
- package/dist/tasks/detection/decodePayload.d.ts.map +0 -1
- package/dist/tasks/detection/decodePayload.js.map +0 -1
- package/dist/tasks/detection/getBotScore.d.ts +0 -5
- package/dist/tasks/detection/getBotScore.d.ts.map +0 -1
- package/dist/tasks/detection/getBotScore.js.map +0 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +0 -23
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +0 -1
- package/dist/tasks/frictionless/frictionlessTasks.js.map +0 -1
- package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts +0 -5
- package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts.map +0 -1
- package/dist/tasks/frictionless/frictionlessTasksUtils.js.map +0 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +0 -29
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +0 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +0 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts +0 -7
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +0 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +0 -1
- package/dist/tasks/index.d.ts +0 -2
- package/dist/tasks/index.d.ts.map +0 -1
- package/dist/tasks/index.js.map +0 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts +0 -16
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +0 -1
- package/dist/tasks/powCaptcha/powTasks.js.map +0 -1
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts +0 -3
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +0 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js.map +0 -1
- package/dist/tasks/tasks.d.ts +0 -25
- package/dist/tasks/tasks.d.ts.map +0 -1
- package/dist/tasks/tasks.js.map +0 -1
- package/dist/tests/index.d.ts +0 -2
- package/dist/tests/index.d.ts.map +0 -1
- package/dist/tests/index.js +0 -2
- package/dist/tests/index.js.map +0 -1
- package/dist/tests/integration/accessRules.integration.test.d.ts +0 -2
- package/dist/tests/integration/accessRules.integration.test.d.ts.map +0 -1
- package/dist/tests/integration/accessRules.integration.test.js +0 -164
- package/dist/tests/integration/accessRules.integration.test.js.map +0 -1
- package/dist/tests/integration/imgCaptcha.integration.test.d.ts +0 -2
- package/dist/tests/integration/imgCaptcha.integration.test.d.ts.map +0 -1
- package/dist/tests/integration/imgCaptcha.integration.test.js +0 -261
- package/dist/tests/integration/imgCaptcha.integration.test.js.map +0 -1
- package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts +0 -32
- package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts.map +0 -1
- package/dist/tests/integration/mocks/solvedTestCaptchas.js +0 -1046
- package/dist/tests/integration/mocks/solvedTestCaptchas.js.map +0 -1
- package/dist/tests/integration/powCaptcha.integration.test.d.ts +0 -2
- package/dist/tests/integration/powCaptcha.integration.test.d.ts.map +0 -1
- package/dist/tests/integration/powCaptcha.integration.test.js +0 -306
- package/dist/tests/integration/powCaptcha.integration.test.js.map +0 -1
- package/dist/tests/integration/registerSitekey.d.ts +0 -3
- package/dist/tests/integration/registerSitekey.d.ts.map +0 -1
- package/dist/tests/integration/registerSitekey.js +0 -39
- package/dist/tests/integration/registerSitekey.js.map +0 -1
- package/dist/tests/integration/userAccessPolicy.d.ts +0 -16
- package/dist/tests/integration/userAccessPolicy.d.ts.map +0 -1
- package/dist/tests/integration/userAccessPolicy.js +0 -55
- package/dist/tests/integration/userAccessPolicy.js.map +0 -1
- package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts +0 -2
- package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/api/ignoreMiddleware.unit.test.js +0 -43
- package/dist/tests/unit/api/ignoreMiddleware.unit.test.js.map +0 -1
- package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts +0 -2
- package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/api/ja4Middleware.unit.test.js +0 -71
- package/dist/tests/unit/api/ja4Middleware.unit.test.js.map +0 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts +0 -2
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +0 -75
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +0 -236
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +0 -277
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js +0 -93
- package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js +0 -75
- package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +0 -68
- package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js +0 -37
- package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +0 -402
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +0 -46
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +0 -228
- package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +0 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts +0 -2
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +0 -68
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +0 -1
- package/dist/tests/unit/util.unit.test.d.ts +0 -2
- package/dist/tests/unit/util.unit.test.d.ts.map +0 -1
- package/dist/tests/unit/util.unit.test.js +0 -148
- package/dist/tests/unit/util.unit.test.js.map +0 -1
- package/dist/util.d.ts +0 -13
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { u8aToHex, stringToHex } from "@polkadot/util";
|
|
2
2
|
import { ProsopoEnvError } from "@prosopo/common";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { computePendingRequestHash, compareCaptchaSolutions, parseAndSortCaptchaSolutions } from "@prosopo/datasets";
|
|
4
|
+
import { DEFAULT_IMAGE_CAPTCHA_TIMEOUT, CaptchaStatus, ApiParams } from "@prosopo/types";
|
|
5
5
|
import { at } from "@prosopo/util";
|
|
6
6
|
import { randomAsHex, signatureVerify } from "@prosopo/util-crypto";
|
|
7
7
|
import { checkLangRules } from "../../rules/lang.js";
|
|
@@ -9,293 +9,382 @@ import { shuffleArray, validateIpAddress } from "../../util.js";
|
|
|
9
9
|
import { CaptchaManager } from "../captchaManager.js";
|
|
10
10
|
import { computeFrictionlessScore } from "../frictionless/frictionlessTasksUtils.js";
|
|
11
11
|
import { buildTreeAndGetCommitmentId } from "./imgCaptchaTasksUtils.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
});
|
|
12
|
+
class ImgCaptchaManager extends CaptchaManager {
|
|
13
|
+
constructor(db, pair, config, logger) {
|
|
14
|
+
super(db, pair, logger);
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
async getCaptchaWithProof(datasetId, solved, size) {
|
|
18
|
+
const captchaDocs = await this.db.getRandomCaptcha(solved, datasetId, size);
|
|
19
|
+
if (!captchaDocs) {
|
|
20
|
+
throw new ProsopoEnvError("DATABASE.CAPTCHA_GET_FAILED", {
|
|
21
|
+
context: {
|
|
22
|
+
failedFuncName: this.getCaptchaWithProof.name,
|
|
23
|
+
datasetId,
|
|
24
|
+
solved,
|
|
25
|
+
size
|
|
28
26
|
}
|
|
29
|
-
|
|
27
|
+
});
|
|
30
28
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const unsolvedCount = Math.abs(Math.trunc(captchaConfig.unsolved.count));
|
|
43
|
-
const solvedCount = Math.abs(Math.trunc(captchaConfig.solved.count));
|
|
44
|
-
if (!solvedCount) {
|
|
45
|
-
throw new ProsopoEnvError("CONFIG.INVALID_CAPTCHA_NUMBER");
|
|
46
|
-
}
|
|
47
|
-
const solved = await this.getCaptchaWithProof(datasetId, true, solvedCount);
|
|
48
|
-
let unsolved = [];
|
|
49
|
-
if (unsolvedCount) {
|
|
50
|
-
unsolved = await this.getCaptchaWithProof(datasetId, false, unsolvedCount);
|
|
29
|
+
return captchaDocs;
|
|
30
|
+
}
|
|
31
|
+
async getRandomCaptchasAndRequestHash(datasetId, userAccount, ipAddress, captchaConfig, threshold, frictionlessTokenId) {
|
|
32
|
+
const dataset = await this.db.getDatasetDetails(datasetId);
|
|
33
|
+
if (!dataset) {
|
|
34
|
+
throw new ProsopoEnvError("DATABASE.DATASET_GET_FAILED", {
|
|
35
|
+
context: {
|
|
36
|
+
failedFuncName: this.getRandomCaptchasAndRequestHash.name,
|
|
37
|
+
dataset,
|
|
38
|
+
datasetId
|
|
51
39
|
}
|
|
52
|
-
|
|
53
|
-
const salt = randomAsHex();
|
|
54
|
-
const requestHash = computePendingRequestHash(captchas.map((c) => c.captchaId), userAccount, salt);
|
|
55
|
-
const currentTime = Date.now();
|
|
56
|
-
const signedRequestHash = u8aToHex(this.pair.sign(stringToHex(requestHash)));
|
|
57
|
-
const timeLimit = captchas
|
|
58
|
-
.map((captcha) => captcha.timeLimitMs || DEFAULT_IMAGE_CAPTCHA_TIMEOUT)
|
|
59
|
-
.reduce((a, b) => a + b, 0);
|
|
60
|
-
const deadlineTs = timeLimit + currentTime;
|
|
61
|
-
await this.db.storePendingImageCommitment(userAccount, requestHash, salt, deadlineTs, currentTime, ipAddress.bigInt(), threshold, frictionlessTokenId);
|
|
62
|
-
return {
|
|
63
|
-
captchas,
|
|
64
|
-
requestHash,
|
|
65
|
-
timestamp: currentTime,
|
|
66
|
-
signedRequestHash,
|
|
67
|
-
};
|
|
40
|
+
});
|
|
68
41
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
42
|
+
const unsolvedCount = Math.abs(
|
|
43
|
+
Math.trunc(captchaConfig.unsolved.count)
|
|
44
|
+
);
|
|
45
|
+
const solvedCount = Math.abs(
|
|
46
|
+
Math.trunc(captchaConfig.solved.count)
|
|
47
|
+
);
|
|
48
|
+
if (!solvedCount) {
|
|
49
|
+
throw new ProsopoEnvError("CONFIG.INVALID_CAPTCHA_NUMBER");
|
|
50
|
+
}
|
|
51
|
+
const solved = await this.getCaptchaWithProof(datasetId, true, solvedCount);
|
|
52
|
+
let unsolved = [];
|
|
53
|
+
if (unsolvedCount) {
|
|
54
|
+
unsolved = await this.getCaptchaWithProof(
|
|
55
|
+
datasetId,
|
|
56
|
+
false,
|
|
57
|
+
unsolvedCount
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const captchas = shuffleArray([...solved, ...unsolved]);
|
|
61
|
+
const salt = randomAsHex();
|
|
62
|
+
const requestHash = computePendingRequestHash(
|
|
63
|
+
captchas.map((c) => c.captchaId),
|
|
64
|
+
userAccount,
|
|
65
|
+
salt
|
|
66
|
+
);
|
|
67
|
+
const currentTime = Date.now();
|
|
68
|
+
const signedRequestHash = u8aToHex(
|
|
69
|
+
this.pair.sign(stringToHex(requestHash))
|
|
70
|
+
);
|
|
71
|
+
const timeLimit = captchas.map((captcha) => captcha.timeLimitMs || DEFAULT_IMAGE_CAPTCHA_TIMEOUT).reduce((a, b) => a + b, 0);
|
|
72
|
+
const deadlineTs = timeLimit + currentTime;
|
|
73
|
+
await this.db.storePendingImageCommitment(
|
|
74
|
+
userAccount,
|
|
75
|
+
requestHash,
|
|
76
|
+
salt,
|
|
77
|
+
deadlineTs,
|
|
78
|
+
currentTime,
|
|
79
|
+
ipAddress.bigInt(),
|
|
80
|
+
threshold,
|
|
81
|
+
frictionlessTokenId
|
|
82
|
+
);
|
|
83
|
+
return {
|
|
84
|
+
captchas,
|
|
85
|
+
requestHash,
|
|
86
|
+
timestamp: currentTime,
|
|
87
|
+
signedRequestHash
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Validate and store the text captcha solution(s) from the Dapp User in a web2 environment
|
|
92
|
+
* @param {string} userAccount
|
|
93
|
+
* @param {string} dappAccount
|
|
94
|
+
* @param {string} requestHash
|
|
95
|
+
* @param {JSON} captchas
|
|
96
|
+
* @param userTimestampSignature
|
|
97
|
+
* @param timestamp
|
|
98
|
+
* @param providerRequestHashSignature
|
|
99
|
+
* @param ipAddress
|
|
100
|
+
* @param headers
|
|
101
|
+
* @param threshold the percentage of captchas that must be correct to return true
|
|
102
|
+
* @return {Promise<DappUserSolutionResult>} result containing the contract event
|
|
103
|
+
*/
|
|
104
|
+
async dappUserSolution(userAccount, dappAccount, requestHash, captchas, userTimestampSignature, timestamp, providerRequestHashSignature, ipAddress, headers, ja4) {
|
|
105
|
+
const verification = signatureVerify(
|
|
106
|
+
stringToHex(timestamp.toString()),
|
|
107
|
+
userTimestampSignature,
|
|
108
|
+
userAccount
|
|
109
|
+
);
|
|
110
|
+
if (!verification.isValid) {
|
|
111
|
+
const err = new ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
112
|
+
context: { failedFuncName: this.dappUserSolution.name, userAccount }
|
|
113
|
+
});
|
|
114
|
+
this.logger.info(() => ({
|
|
115
|
+
err,
|
|
116
|
+
msg: "Invalid user timestamp signature"
|
|
117
|
+
}));
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
const providerRequestHashSignatureVerify = signatureVerify(
|
|
121
|
+
stringToHex(requestHash.toString()),
|
|
122
|
+
providerRequestHashSignature,
|
|
123
|
+
this.pair.address
|
|
124
|
+
);
|
|
125
|
+
if (!providerRequestHashSignatureVerify.isValid) {
|
|
126
|
+
const err = new ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
127
|
+
context: {
|
|
128
|
+
failedFuncName: this.dappUserSolution.name,
|
|
129
|
+
userAccount,
|
|
130
|
+
error: "requestHash signature is invalid"
|
|
80
131
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
});
|
|
133
|
+
this.logger.info(() => ({
|
|
134
|
+
err,
|
|
135
|
+
msg: "Invalid provider requestHash signature"
|
|
136
|
+
}));
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
let response = {
|
|
140
|
+
captchas: [],
|
|
141
|
+
verified: false
|
|
142
|
+
};
|
|
143
|
+
const pendingRecord = await this.db.getPendingImageCommitment(requestHash);
|
|
144
|
+
const unverifiedCaptchaIds = captchas.map((captcha) => captcha.captchaId);
|
|
145
|
+
const pendingRequest = await this.validateDappUserSolutionRequestIsPending(
|
|
146
|
+
requestHash,
|
|
147
|
+
pendingRecord,
|
|
148
|
+
userAccount,
|
|
149
|
+
unverifiedCaptchaIds
|
|
150
|
+
);
|
|
151
|
+
if (pendingRequest) {
|
|
152
|
+
const { storedCaptchas, receivedCaptchas, captchaIds } = await this.validateReceivedCaptchasAgainstStoredCaptchas(captchas);
|
|
153
|
+
const { tree, commitmentId } = buildTreeAndGetCommitmentId(receivedCaptchas);
|
|
154
|
+
const datasetId = at(storedCaptchas, 0).datasetId;
|
|
155
|
+
if (!datasetId) {
|
|
156
|
+
throw new ProsopoEnvError("CAPTCHA.ID_MISMATCH", {
|
|
157
|
+
context: { failedFuncName: this.dappUserSolution.name }
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
await this.db.updatePendingImageCommitmentStatus(requestHash);
|
|
161
|
+
const commit = {
|
|
162
|
+
id: commitmentId,
|
|
163
|
+
userAccount,
|
|
164
|
+
dappAccount,
|
|
165
|
+
providerAccount: this.pair.address,
|
|
166
|
+
datasetId,
|
|
167
|
+
result: { status: CaptchaStatus.pending },
|
|
168
|
+
userSignature: userTimestampSignature,
|
|
169
|
+
userSubmitted: true,
|
|
170
|
+
serverChecked: false,
|
|
171
|
+
requestedAtTimestamp: timestamp,
|
|
172
|
+
ipAddress,
|
|
173
|
+
headers,
|
|
174
|
+
frictionlessTokenId: pendingRecord.frictionlessTokenId,
|
|
175
|
+
ja4
|
|
176
|
+
};
|
|
177
|
+
await this.db.storeUserImageCaptchaSolution(receivedCaptchas, commit);
|
|
178
|
+
const solutionRecords = await Promise.all(
|
|
179
|
+
storedCaptchas.map(async (captcha) => {
|
|
180
|
+
const solutionRecord = await this.db.getSolutionByCaptchaId(
|
|
181
|
+
captcha.captchaId
|
|
182
|
+
);
|
|
183
|
+
if (!solutionRecord) {
|
|
184
|
+
throw new ProsopoEnvError("CAPTCHA.SOLUTION_NOT_FOUND", {
|
|
185
|
+
context: { failedFuncName: this.dappUserSolution.name }
|
|
89
186
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
187
|
+
}
|
|
188
|
+
return solutionRecord;
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
const totalImages = storedCaptchas[0]?.items.length || 0;
|
|
192
|
+
if (compareCaptchaSolutions(
|
|
193
|
+
receivedCaptchas,
|
|
194
|
+
solutionRecords,
|
|
195
|
+
totalImages,
|
|
196
|
+
pendingRecord.threshold
|
|
197
|
+
)) {
|
|
198
|
+
response = {
|
|
199
|
+
captchas: captchaIds.map((id) => ({
|
|
200
|
+
captchaId: id,
|
|
201
|
+
proof: tree.proof(id)
|
|
202
|
+
})),
|
|
203
|
+
verified: true
|
|
99
204
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
datasetId,
|
|
119
|
-
result: { status: CaptchaStatus.pending },
|
|
120
|
-
userSignature: userTimestampSignature,
|
|
121
|
-
userSubmitted: true,
|
|
122
|
-
serverChecked: false,
|
|
123
|
-
requestedAtTimestamp: timestamp,
|
|
124
|
-
ipAddress,
|
|
125
|
-
headers,
|
|
126
|
-
frictionlessTokenId: pendingRecord.frictionlessTokenId,
|
|
127
|
-
ja4,
|
|
128
|
-
};
|
|
129
|
-
await this.db.storeUserImageCaptchaSolution(receivedCaptchas, commit);
|
|
130
|
-
const solutionRecords = await Promise.all(storedCaptchas.map(async (captcha) => {
|
|
131
|
-
const solutionRecord = await this.db.getSolutionByCaptchaId(captcha.captchaId);
|
|
132
|
-
if (!solutionRecord) {
|
|
133
|
-
throw new ProsopoEnvError("CAPTCHA.SOLUTION_NOT_FOUND", {
|
|
134
|
-
context: { failedFuncName: this.dappUserSolution.name },
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
return solutionRecord;
|
|
138
|
-
}));
|
|
139
|
-
const totalImages = storedCaptchas[0]?.items.length || 0;
|
|
140
|
-
if (compareCaptchaSolutions(receivedCaptchas, solutionRecords, totalImages, pendingRecord.threshold)) {
|
|
141
|
-
response = {
|
|
142
|
-
captchas: captchaIds.map((id) => ({
|
|
143
|
-
captchaId: id,
|
|
144
|
-
proof: tree.proof(id),
|
|
145
|
-
})),
|
|
146
|
-
verified: true,
|
|
147
|
-
};
|
|
148
|
-
await this.db.approveDappUserCommitment(commitmentId);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
await this.db.disapproveDappUserCommitment(commitmentId, "CAPTCHA.INVALID_SOLUTION");
|
|
152
|
-
response = {
|
|
153
|
-
captchas: captchaIds.map((id) => ({
|
|
154
|
-
captchaId: id,
|
|
155
|
-
proof: [[]],
|
|
156
|
-
})),
|
|
157
|
-
verified: false,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
this.logger.info(() => ({
|
|
163
|
-
msg: "Request hash not found",
|
|
164
|
-
}));
|
|
165
|
-
}
|
|
166
|
-
return response;
|
|
205
|
+
await this.db.approveDappUserCommitment(commitmentId);
|
|
206
|
+
} else {
|
|
207
|
+
await this.db.disapproveDappUserCommitment(
|
|
208
|
+
commitmentId,
|
|
209
|
+
"CAPTCHA.INVALID_SOLUTION"
|
|
210
|
+
);
|
|
211
|
+
response = {
|
|
212
|
+
captchas: captchaIds.map((id) => ({
|
|
213
|
+
captchaId: id,
|
|
214
|
+
proof: [[]]
|
|
215
|
+
})),
|
|
216
|
+
verified: false
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
this.logger.info(() => ({
|
|
221
|
+
msg: "Request hash not found"
|
|
222
|
+
}));
|
|
167
223
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
|
|
184
|
-
captchas,
|
|
185
|
-
},
|
|
186
|
-
});
|
|
224
|
+
return response;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Validate length of received captchas array matches length of captchas found in database
|
|
228
|
+
* Validate that the datasetId is the same for all captchas and is equal to the datasetId on the stored captchas
|
|
229
|
+
*/
|
|
230
|
+
async validateReceivedCaptchasAgainstStoredCaptchas(captchas) {
|
|
231
|
+
const receivedCaptchas = parseAndSortCaptchaSolutions(captchas);
|
|
232
|
+
const captchaIds = receivedCaptchas.map((captcha) => captcha.captchaId);
|
|
233
|
+
const storedCaptchas = await this.db.getCaptchaById(captchaIds);
|
|
234
|
+
if (!storedCaptchas || receivedCaptchas.length !== storedCaptchas.length) {
|
|
235
|
+
throw new ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_ID", {
|
|
236
|
+
context: {
|
|
237
|
+
failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
|
|
238
|
+
captchas
|
|
187
239
|
}
|
|
188
|
-
|
|
240
|
+
});
|
|
189
241
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
if (pendingRecord.deadlineTimestamp < currentTime) {
|
|
199
|
-
this.logger.info(() => ({
|
|
200
|
-
msg: "Deadline for responding to captcha has expired",
|
|
201
|
-
}));
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
if (pendingRecord) {
|
|
205
|
-
const pendingHashComputed = computePendingRequestHash(captchaIds, userAccount, pendingRecord.salt);
|
|
206
|
-
return requestHash === pendingHashComputed;
|
|
242
|
+
if (!storedCaptchas.every(
|
|
243
|
+
(captcha) => captcha.datasetId === at(storedCaptchas, 0).datasetId
|
|
244
|
+
)) {
|
|
245
|
+
throw new ProsopoEnvError("CAPTCHA.DIFFERENT_DATASET_IDS", {
|
|
246
|
+
context: {
|
|
247
|
+
failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
|
|
248
|
+
captchas
|
|
207
249
|
}
|
|
208
|
-
|
|
250
|
+
});
|
|
209
251
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
252
|
+
return { storedCaptchas, receivedCaptchas, captchaIds };
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Validate that a Dapp User is responding to their own pending captcha request
|
|
256
|
+
* @param {string} requestHash
|
|
257
|
+
* @param {PendingCaptchaRequest} pendingRecord
|
|
258
|
+
* @param {string} userAccount
|
|
259
|
+
* @param {string[]} captchaIds
|
|
260
|
+
*/
|
|
261
|
+
async validateDappUserSolutionRequestIsPending(requestHash, pendingRecord, userAccount, captchaIds) {
|
|
262
|
+
const currentTime = Date.now();
|
|
263
|
+
if (!pendingRecord) {
|
|
264
|
+
this.logger.info(() => ({
|
|
265
|
+
msg: "No pending record found"
|
|
266
|
+
}));
|
|
267
|
+
return false;
|
|
221
268
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return dappUserSolution;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return undefined;
|
|
269
|
+
if (pendingRecord.deadlineTimestamp < currentTime) {
|
|
270
|
+
this.logger.info(() => ({
|
|
271
|
+
msg: "Deadline for responding to captcha has expired"
|
|
272
|
+
}));
|
|
273
|
+
return false;
|
|
232
274
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
maxVerifiedTime = maxVerifiedTime || 60 * 1000;
|
|
255
|
-
if (maxVerifiedTime) {
|
|
256
|
-
const currentTime = Date.now();
|
|
257
|
-
const timeSinceCompletion = currentTime - solution.requestedAtTimestamp;
|
|
258
|
-
if (timeSinceCompletion > maxVerifiedTime) {
|
|
259
|
-
this.logger.debug(() => ({
|
|
260
|
-
msg: "Not verified - timed out",
|
|
261
|
-
}));
|
|
262
|
-
return {
|
|
263
|
-
status: "API.USER_NOT_VERIFIED_TIME_EXPIRED",
|
|
264
|
-
verified: false,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
275
|
+
if (pendingRecord) {
|
|
276
|
+
const pendingHashComputed = computePendingRequestHash(
|
|
277
|
+
captchaIds,
|
|
278
|
+
userAccount,
|
|
279
|
+
pendingRecord.salt
|
|
280
|
+
);
|
|
281
|
+
return requestHash === pendingHashComputed;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
/*
|
|
286
|
+
* Get dapp user solution from database
|
|
287
|
+
*/
|
|
288
|
+
async getDappUserCommitmentById(commitmentId) {
|
|
289
|
+
const dappUserSolution = await this.db.getDappUserCommitmentById(commitmentId);
|
|
290
|
+
if (!dappUserSolution) {
|
|
291
|
+
throw new ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", {
|
|
292
|
+
context: {
|
|
293
|
+
failedFuncName: this.getDappUserCommitmentById.name,
|
|
294
|
+
commitmentId
|
|
267
295
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return dappUserSolution;
|
|
299
|
+
}
|
|
300
|
+
/* Check if dapp user has verified solution in cache */
|
|
301
|
+
async getDappUserCommitmentByAccount(userAccount, dappAccount) {
|
|
302
|
+
const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(
|
|
303
|
+
userAccount,
|
|
304
|
+
dappAccount
|
|
305
|
+
);
|
|
306
|
+
if (dappUserSolutions.length > 0) {
|
|
307
|
+
for (const dappUserSolution of dappUserSolutions) {
|
|
308
|
+
if (dappUserSolution.result.status === CaptchaStatus.approved) {
|
|
309
|
+
return dappUserSolution;
|
|
281
310
|
}
|
|
282
|
-
|
|
283
|
-
status: isApproved ? "API.USER_VERIFIED" : "API.USER_NOT_VERIFIED",
|
|
284
|
-
verified: isApproved,
|
|
285
|
-
commitmentId: solution.id.toString(),
|
|
286
|
-
...(score && { score }),
|
|
287
|
-
};
|
|
311
|
+
}
|
|
288
312
|
}
|
|
289
|
-
|
|
290
|
-
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
async verifyImageCaptchaSolution(user, dapp, commitmentId, maxVerifiedTime, ip) {
|
|
316
|
+
const solution = await (commitmentId ? this.getDappUserCommitmentById(commitmentId) : this.getDappUserCommitmentByAccount(user, dapp));
|
|
317
|
+
if (!solution) {
|
|
318
|
+
this.logger.debug(() => ({
|
|
319
|
+
msg: "Not verified - no solution found"
|
|
320
|
+
}));
|
|
321
|
+
return { status: "API.USER_NOT_VERIFIED_NO_SOLUTION", verified: false };
|
|
291
322
|
}
|
|
292
|
-
|
|
323
|
+
const ipValidation = validateIpAddress(ip, solution.ipAddress, this.logger);
|
|
324
|
+
if (!ipValidation.isValid) {
|
|
325
|
+
return { status: "API.USER_NOT_VERIFIED", verified: false };
|
|
326
|
+
}
|
|
327
|
+
if (solution.serverChecked) {
|
|
328
|
+
return { status: "API.USER_ALREADY_VERIFIED", verified: false };
|
|
329
|
+
}
|
|
330
|
+
await this.db.markDappUserCommitmentsChecked([solution.id]);
|
|
331
|
+
if (solution.result.status === CaptchaStatus.disapproved) {
|
|
332
|
+
return { status: "API.USER_NOT_VERIFIED", verified: false };
|
|
333
|
+
}
|
|
334
|
+
maxVerifiedTime = maxVerifiedTime || 60 * 1e3;
|
|
335
|
+
if (maxVerifiedTime) {
|
|
336
|
+
const currentTime = Date.now();
|
|
337
|
+
const timeSinceCompletion = currentTime - solution.requestedAtTimestamp;
|
|
338
|
+
if (timeSinceCompletion > maxVerifiedTime) {
|
|
339
|
+
this.logger.debug(() => ({
|
|
340
|
+
msg: "Not verified - timed out"
|
|
341
|
+
}));
|
|
293
342
|
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
[ApiParams.commitmentId]: commitmentId,
|
|
297
|
-
}),
|
|
343
|
+
status: "API.USER_NOT_VERIFIED_TIME_EXPIRED",
|
|
344
|
+
verified: false
|
|
298
345
|
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const isApproved = solution.result.status === CaptchaStatus.approved;
|
|
349
|
+
let score;
|
|
350
|
+
if (solution.frictionlessTokenId) {
|
|
351
|
+
const tokenRecord = await this.db.getFrictionlessTokenRecordByTokenId(
|
|
352
|
+
solution.frictionlessTokenId
|
|
353
|
+
);
|
|
354
|
+
if (tokenRecord) {
|
|
355
|
+
score = computeFrictionlessScore(tokenRecord?.scoreComponents);
|
|
356
|
+
this.logger.info(() => ({
|
|
357
|
+
data: {
|
|
358
|
+
tscoreComponents: tokenRecord?.scoreComponents,
|
|
359
|
+
score
|
|
360
|
+
}
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
299
363
|
}
|
|
364
|
+
return {
|
|
365
|
+
status: isApproved ? "API.USER_VERIFIED" : "API.USER_NOT_VERIFIED",
|
|
366
|
+
verified: isApproved,
|
|
367
|
+
commitmentId: solution.id.toString(),
|
|
368
|
+
...score && { score }
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
checkLangRules(acceptLanguage) {
|
|
372
|
+
return checkLangRules(this.config, acceptLanguage);
|
|
373
|
+
}
|
|
374
|
+
getVerificationResponse(verified, clientRecord, translateFn, score, commitmentId) {
|
|
375
|
+
return {
|
|
376
|
+
...super.getVerificationResponse(
|
|
377
|
+
verified,
|
|
378
|
+
clientRecord,
|
|
379
|
+
translateFn,
|
|
380
|
+
score
|
|
381
|
+
),
|
|
382
|
+
...commitmentId && {
|
|
383
|
+
[ApiParams.commitmentId]: commitmentId
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
300
387
|
}
|
|
301
|
-
|
|
388
|
+
export {
|
|
389
|
+
ImgCaptchaManager
|
|
390
|
+
};
|