Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
{
"paramType": "query",
"paramName": "operations",
"value": "min,max"
"value": "[userEmail]"
}
]
}
Expand Down
9 changes: 7 additions & 2 deletions src/routes/aggregate/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export function registerAggregateRoutes(
`/${model.name}/aggregation/${field.name}`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
// doing validation here because we need the user for SSP
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -83,6 +83,9 @@ export function registerAggregateRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand All @@ -103,6 +106,8 @@ export function registerAggregateRoutes(
.map(s => s.trim())
.filter(Boolean);

console.log({requestedOps});

// validation: we must have at least one valid operation
if (requestedOps.length === 0) {
return reply
Expand Down
6 changes: 4 additions & 2 deletions src/routes/custom-queries/custom-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ export function registerCustomQueryRoutes(
method: cq.method,
url: routePath,
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -248,6 +247,9 @@ export function registerCustomQueryRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
7 changes: 4 additions & 3 deletions src/routes/operations/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ export function registerDeleteRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
// checking the authorization
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -81,6 +79,9 @@ export function registerDeleteRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
14 changes: 9 additions & 5 deletions src/routes/operations/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,7 @@ export function registerEditRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema: buildRouteSchema('PATCH'),
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -247,6 +246,9 @@ export function registerEditRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand All @@ -266,9 +268,8 @@ export function registerEditRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema: buildRouteSchema('PUT'),
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
if (config.auth?.enableAuth) {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
} catch {
Expand All @@ -283,6 +284,9 @@ export function registerEditRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
8 changes: 4 additions & 4 deletions src/routes/operations/get-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,9 @@ export function registerGetAllRoutes(
`/${model.name}/`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
console.log('I am here');
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
console.log('I am running to verify JWT?');
await request.jwtVerify();
} catch {
return reply
Expand All @@ -78,6 +75,9 @@ export function registerGetAllRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
6 changes: 4 additions & 2 deletions src/routes/operations/index-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ export function registerIndexRoutes(
`/${model.name}/${field.name}/:${field.name}`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -88,6 +87,9 @@ export function registerIndexRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
6 changes: 4 additions & 2 deletions src/routes/operations/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ export function registerPostRoutes(
`/${model.name}/`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -71,6 +70,9 @@ export function registerPostRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
6 changes: 4 additions & 2 deletions src/routes/operations/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ export function registerSearchRoutes(
`/${model.name}/search/${field.name}`,
{
schema,
preValidation: async request => enforceSSP(sspConfig, request),
preHandler: async (request, reply) => {
preValidation: async (request, reply) => {
if (config.auth?.enableAuth && authorization) {
try {
await request.jwtVerify();
Expand All @@ -83,6 +82,9 @@ export function registerSearchRoutes(
);
}
}
enforceSSP(sspConfig, request);
},
preHandler: async request => {
await callWebhook('request', webhookConfig, request, null, app.log);
},
onSend: async (request, _, payload) => {
Expand Down
18 changes: 13 additions & 5 deletions src/types/fastify.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {FastifyJWT} from '@fastify/jwt';

import {DatabaseQuery, StructuredResponse} from '@/interfaces';

import '@fastify/jwt';
import 'fastify';

import type Redis from 'ioredis';

import {DatabaseQuery, StructuredResponse} from '@/interfaces';

declare module '@fastify/jwt' {
interface FastifyJWT {
user: {
id: string;
email: string;
};
}
}

declare module 'fastify' {
interface FastifyInstance {
db: DatabaseQuery;
Expand All @@ -16,6 +24,6 @@ declare module 'fastify' {
raw_data?: R,
) => StructuredResponse<T, R>;
redis?: Redis;
jwt: FastifyJWT;
jwt: import('@fastify/jwt').JWT;
}
}
10 changes: 9 additions & 1 deletion src/utils/ssp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ export function enforceSSP(ssps: SspConfig[], request: FastifyRequest) {
const record = val as Record<string, unknown>;
ssps.forEach(ssp => {
if (ssp.paramType === type) {
record[ssp.paramName] = ssp.value;
// if ssp.value is a magic variable then replace it with request.user
// aloowed magic variables are [userId] and [userEmail]
if (ssp.value === '[userId]') {
record[ssp.paramName] = request.user?.id;
} else if (ssp.value === '[userEmail]') {
record[ssp.paramName] = request.user?.email;
} else {
record[ssp.paramName] = ssp.value;
}
}
});
}
Expand Down
45 changes: 45 additions & 0 deletions tests/utils/ssp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,49 @@ describe('enforceSSP', () => {

expect(request.query).toEqual({duplicateKey: 'secondValue'});
});

it('should replace [userId] magic variable with request.user.id', () => {
const request = {
query: {},
user: {id: 42, email: 'test@example.com'},
} as unknown as FastifyRequest;

const ssps: SspConfig[] = [
{paramType: 'query', paramName: 'ownerId', value: '[userId]'},
];

enforceSSP(ssps, request);

expect(request.query).toEqual({ownerId: 42});
});

it('should replace [userEmail] magic variable with request.user.email', () => {
const request = {
body: {},
user: {id: 42, email: 'test@example.com'},
} as unknown as FastifyRequest;

const ssps: SspConfig[] = [
{paramType: 'body', paramName: 'user_email', value: '[userEmail]'},
];

enforceSSP(ssps, request);

expect(request.body).toEqual({user_email: 'test@example.com'});
});

it('should handle missing request.user when magic variables are used', () => {
const request = {
query: {},
// no user object
} as unknown as FastifyRequest;

const ssps: SspConfig[] = [
{paramType: 'query', paramName: 'ownerId', value: '[userId]'},
];

enforceSSP(ssps, request);

expect(request.query).toEqual({ownerId: undefined});
});
});
Loading