Skip to content

Commit

Permalink
refactor: reset
Browse files Browse the repository at this point in the history
  • Loading branch information
JuroUhlar committed Feb 21, 2024
1 parent 82fe97c commit 7cd779b
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 83 deletions.
17 changes: 14 additions & 3 deletions src/client/hooks/useReset/useReset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const useReset = ({ onError, onSuccess }: UseResetParams) => {
const resetMutation = useMutation<ResetResponse>(
'resetMutation',
async () => {
const { visitorId, requestId } = await getData();
const body: ResetRequest = { visitorId, requestId };
const { requestId } = await getData();
const body: ResetRequest = { requestId };

return fetch('/api/admin/reset', {
method: 'POST',
Expand All @@ -46,7 +46,18 @@ export const useReset = ({ onError, onSuccess }: UseResetParams) => {
((data) => {
enqueueSnackbar(
<div data-testid={TEST_IDS.reset.resetSuccess}>
<p>Scenarios reset successfully!</p> <p>{data.message}</p>
<p>
<b>Scenarios reset successfully!</b>
</p>{' '}
<ul>
<li>Deleted {data.result?.deletedCouponsClaims} coupon claims.</li>
<li>Deleted {data.result?.deletedLoginAttempts} login attempts.</li>
<li>Deleted {data.result?.deletedLoanRequests} loan requests.</li>
<li>Deleted {data.result?.deletedPaymentAttempts} payment attempts.</li>
<li>Deleted {data.result?.deletedArticleViews} article views.</li>
<li>Deleted {data.result?.deletedPersonalizationRecords} personalization records.</li>
<li>Deleted {data.result?.deletedBlockedIps} blocked IPs.</li>
</ul>
</div>,
{
variant: 'success',
Expand Down
126 changes: 46 additions & 80 deletions src/pages/api/admin/reset.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
Severity,
ensureValidRequestIdAndVisitorId,
getIdentificationEvent,
messageSeverity,
} from '../../../server/server';
import { Severity, isValidPostRequest } from '../../../server/server';
import { LoginAttemptDbModel } from '../credential-stuffing/authenticate';
import { PaymentAttemptDbModel } from '../payment-fraud/place-order';
import {
Expand All @@ -14,111 +9,82 @@ import {
import { LoanRequestDbModel } from '../../../server/loan-risk/database';
import { ArticleViewDbModel } from '../../../server/paywall/database';
import { CouponClaimDbModel } from '../../../server/coupon-fraud/database';
import { CheckResult, checkResultType } from '../../../server/checkResult';
import {
RuleCheck,
checkConfidenceScore,
checkFreshIdentificationRequest,
checkIpAddressIntegrity,
checkOriginsIntegrity,
} from '../../../server/checks';
import { sendForbiddenResponse, sendOkResponse } from '../../../server/response';
import { getAndValidateFingerprintResult } from '../../../server/checks';
import { NextApiRequest, NextApiResponse } from 'next';
import { isVisitorsError } from '@fingerprintjs/fingerprintjs-pro-server-api';
import { deleteBlockedIp } from '../../../server/botd-firewall/blockedIpsDatabase';
import { syncFirewallRuleset } from '../../../server/botd-firewall/cloudflareApiHelper';

export type ResetResponse = {
message: string;
severity?: Severity;
type?: string;
result?: ResetResult;
};

export type ResetRequest = {
visitorId: string;
requestId: string;
};

export default async function handler(req: NextApiRequest, res: NextApiResponse<ResetResponse>) {
// This API route accepts only POST requests.
if (req.method !== 'POST') {
res.status(405).send({ message: 'Only POST requests allowed' });
const reqValidation = isValidPostRequest(req);
if (!reqValidation.okay) {
res.status(405).send({ severity: 'error', message: reqValidation.error });
return;
}
res.setHeader('Content-Type', 'application/json');

return await tryToReset(req, res, [
checkFreshIdentificationRequest,
checkConfidenceScore,
checkIpAddressIntegrity,
checkOriginsIntegrity,
deleteVisitorData,
]);
}

async function tryToReset(req: NextApiRequest, res: NextApiResponse, ruleChecks: RuleCheck[]) {
// Get requestId and visitorId from the client.
const { visitorId, requestId } = req.body as ResetRequest;
const { requestId } = req.body as ResetRequest;

if (!ensureValidRequestIdAndVisitorId(req, res, visitorId, requestId)) {
// Get the full Identification result from Fingerprint Server API and validate its authenticity
const fingerprintResult = await getAndValidateFingerprintResult(requestId, req);
if (!fingerprintResult.okay) {
res.status(403).send({ severity: 'error', message: fingerprintResult.error });
return;
}

const eventResponse = await getIdentificationEvent(requestId);
const visitorId = fingerprintResult.data.products?.identification?.data?.visitorId;
const ip = fingerprintResult.data.products?.identification?.data?.ip;
if (!visitorId) {
res.status(403).send({ severity: 'error', message: 'Visitor ID not found.' });
return;
}

for (const ruleCheck of ruleChecks) {
const result = await ruleCheck(eventResponse, req);
const deleteResult = await deleteVisitorData(visitorId, ip ?? '');

if (result) {
switch (result.type) {
case checkResultType.Passed:
return sendOkResponse(res, result);
default:
return sendForbiddenResponse(res, result);
}
}
}
res.status(200).json({
message: 'Visitor data deleted successfully.',
severity: 'success',
result: deleteResult,
});
}

const deleteVisitorData: RuleCheck = async (eventResponse) => {
if (isVisitorsError(eventResponse)) {
return;
}

const deleteVisitorData = async (visitorId: string, ip: string) => {
const options = {
where: { visitorId: eventResponse.products?.identification?.data?.visitorId },
where: { visitorId },
};

const loginAttemptsRowsRemoved = await tryToDestroy(() => LoginAttemptDbModel.destroy(options));

const paymentAttemptsRowsRemoved = await tryToDestroy(() => PaymentAttemptDbModel.destroy(options));

const couponsRemoved = await tryToDestroy(() => CouponClaimDbModel.destroy(options));

const deletedCartItemsCount = await tryToDestroy(() => UserCartItemDbModel.destroy(options));
const deletedUserPreferencesCount = await tryToDestroy(() => UserPreferencesDbModel.destroy(options));
const deletedUserSearchHistoryCount = await tryToDestroy(() => UserSearchHistoryDbModel.destroy(options));

const deletedPersonalizationCount =
deletedCartItemsCount + deletedUserPreferencesCount + deletedUserSearchHistoryCount;

const deletedLoanRequests = await tryToDestroy(() => LoanRequestDbModel.destroy(options));
const deletedPaywallData = await tryToDestroy(() => ArticleViewDbModel.destroy(options));

const deletedBlockedIps = await tryToDestroy(async () => {
const deletedIpCount = await deleteBlockedIp(eventResponse.products?.identification?.data?.ip ?? '');
await syncFirewallRuleset();
return deletedIpCount;
});

return new CheckResult(
`Deleted ${loginAttemptsRowsRemoved} rows for Credential Stuffing problem. Deleted ${paymentAttemptsRowsRemoved} rows for Payment Fraud problem. Deleted ${deletedPersonalizationCount} entries related to personalization. Deleted ${deletedLoanRequests} loan request entries. Deleted ${deletedPaywallData} rows for the Paywall problem. Deleted ${couponsRemoved} rows for the Coupon fraud problem. Deleted ${deletedBlockedIps} blocked IPs for the Bot Firewall demo.`,
messageSeverity.Success,
checkResultType.Passed,
);
return {
deletedLoginAttempts: await tryToDestroy(() => LoginAttemptDbModel.destroy(options)),
deletedPaymentAttempts: await tryToDestroy(() => PaymentAttemptDbModel.destroy(options)),
deletedCouponsClaims: await tryToDestroy(() => CouponClaimDbModel.destroy(options)),
deletedPersonalizationRecords: await tryToDestroy(async () => {
const deletedCartItemsCount = await UserCartItemDbModel.destroy(options);
const deletedUserPreferencesCount = await UserPreferencesDbModel.destroy(options);
const deletedUserSearchHistoryCount = await UserSearchHistoryDbModel.destroy(options);
return deletedCartItemsCount + deletedUserPreferencesCount + deletedUserSearchHistoryCount;
}),
deletedLoanRequests: await tryToDestroy(() => LoanRequestDbModel.destroy(options)),
deletedArticleViews: await tryToDestroy(() => ArticleViewDbModel.destroy(options)),
deletedBlockedIps: await tryToDestroy(async () => {
const deletedIpCount = await deleteBlockedIp(ip);
await syncFirewallRuleset();
return deletedIpCount;
}),
};
};

const tryToDestroy = async (callback: () => Promise<any>) => {
type ResetResult = Awaited<ReturnType<typeof deleteVisitorData>>;

const tryToDestroy = async (callback: () => Promise<number>) => {
try {
return await callback();
} catch (err) {
Expand Down

0 comments on commit 7cd779b

Please sign in to comment.