Skip to content

Commit 1e69969

Browse files
authored
feat: add debug routes for transaction database (#113)
* feat: debug routes for SQLite * feat: restrict scopes who can debug the db * refactor: remove the need to have a search to delete a record this allows record only tokens to remove records * Update packages/mosip-api/src/routes/debug-sqlite.ts * use scopes from 1.9.0 * give actionId / eventId to debug
1 parent 65885d3 commit 1e69969

File tree

4 files changed

+127
-1
lines changed

4 files changed

+127
-1
lines changed

packages/mosip-api/src/database.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,23 @@ export const getTransactionAndDiscard = (id: string) => {
6262
};
6363
};
6464

65+
/**
66+
* Retrieves all transactions from the database.
67+
*
68+
* @warning
69+
* This function is intended for **debugging purposes only** as it exposes sensitive data.
70+
*/
71+
export const getAllTransactions = () => {
72+
return database
73+
.prepare(
74+
"SELECT id, registration_number, token, created_at FROM transactions",
75+
)
76+
.all() as Array<{
77+
id: string;
78+
registration_number: string;
79+
token: string;
80+
created_at: string;
81+
}>;
82+
};
83+
6584
export const exit = () => database.close();

packages/mosip-api/src/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import {
1919
CredentialIssuedSchema,
2020
} from "./routes/websub-credential-issued";
2121
import { initWebSub } from "./websub/subscribe";
22+
import {
23+
deleteTransactionHandler,
24+
getAllTransactionsHandler,
25+
} from "./routes/debug-sqlite";
2226
import { verifyHandler, VerifySchema } from "./routes/verify";
2327

2428
const envToLogger = {
@@ -34,6 +38,9 @@ const envToLogger = {
3438
};
3539

3640
const initRoutes = (app: FastifyInstance) => {
41+
/*
42+
* OpenCRVS birth / death registration and personal information verification
43+
*/
3744
app.withTypeProvider<ZodTypeProvider>().route({
3845
url: "/events/registration",
3946
method: "POST",
@@ -80,6 +87,21 @@ const initRoutes = (app: FastifyInstance) => {
8087
body: CredentialIssuedSchema,
8188
},
8289
});
90+
91+
/*
92+
* SQLite debug route
93+
*/
94+
app.withTypeProvider<ZodTypeProvider>().route({
95+
method: "GET",
96+
url: "/debug/transactions",
97+
handler: getAllTransactionsHandler,
98+
});
99+
100+
app.withTypeProvider<ZodTypeProvider>().route({
101+
method: "DELETE",
102+
url: "/debug/transactions/:id",
103+
handler: deleteTransactionHandler,
104+
});
83105
};
84106

85107
let corePublicKey: string;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { FastifyReply, FastifyRequest } from "fastify";
2+
import { getAllTransactions, getTransactionAndDiscard } from "../database";
3+
import { SCOPES } from "@opencrvs/toolkit/scopes";
4+
import { TokenPayload } from "./websub-credential-issued";
5+
import { decode } from "jsonwebtoken";
6+
7+
interface AuthenticatedUser {
8+
scope: string[];
9+
}
10+
11+
/**
12+
* Allow listing transactions for users that have the search scope.
13+
*
14+
* Rationale:
15+
* - Users with this scope would be able to see record UUID's and registration numbers in the UI anyway.
16+
*/
17+
const isAllowedToSearch = (scope: string[]) => {
18+
return (
19+
scope.includes(SCOPES.SEARCH_BIRTH) && scope.includes(SCOPES.SEARCH_DEATH)
20+
);
21+
};
22+
23+
/**
24+
* Allow deleting transactions for users that have `record.reject-registration` scope.
25+
*
26+
* Rationale:
27+
* - This should be accompanied with a `client.event.actions.register.reject` call via Postman which requires this scope.
28+
*/
29+
const isAllowedToDelete = (scope: string[]) =>
30+
scope.includes(SCOPES.RECORD_REJECT_REGISTRATION);
31+
32+
export const getAllTransactionsHandler = async (
33+
request: FastifyRequest,
34+
reply: FastifyReply,
35+
) => {
36+
const { scope } = request.user as AuthenticatedUser;
37+
38+
if (!isAllowedToSearch(scope)) {
39+
return reply.status(403).send({
40+
error: "You do not have permission to access this resource.",
41+
});
42+
}
43+
44+
const transactions = getAllTransactions();
45+
46+
return transactions.map(({ token, ...rest }) => {
47+
const { eventId, actionId } = decode(token) as TokenPayload;
48+
49+
return {
50+
eventId,
51+
actionId,
52+
...rest,
53+
};
54+
});
55+
};
56+
57+
export type DeleteTransactionRequest = FastifyRequest<{
58+
Params: { id: string };
59+
}>;
60+
61+
export const deleteTransactionHandler = async (
62+
request: DeleteTransactionRequest,
63+
reply: FastifyReply,
64+
) => {
65+
const { scope } = request.user as AuthenticatedUser;
66+
67+
if (!isAllowedToDelete(scope)) {
68+
return reply.status(403).send({
69+
error: "You do not have permission to access this resource.",
70+
});
71+
}
72+
73+
const { id } = request.params;
74+
75+
try {
76+
const transaction = getTransactionAndDiscard(id);
77+
78+
reply.status(200).send(transaction);
79+
} catch (error) {
80+
const message =
81+
error instanceof Error ? error.message : "Unknown error occurred";
82+
83+
reply.status(404).send({ error: message });
84+
}
85+
};

packages/mosip-api/src/routes/websub-credential-issued.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const CredentialIssuedSchema = z.object({
2828
}),
2929
});
3030

31-
interface TokenPayload {
31+
export interface TokenPayload {
3232
eventId: string;
3333
actionId: string;
3434
}

0 commit comments

Comments
 (0)