Skip to content

Commit fee2859

Browse files
Merge PR: Restrict user data access
2 parents dc7358d + 021c996 commit fee2859

2 files changed

Lines changed: 57 additions & 18 deletions

File tree

src/api/v1/users/users.router.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Request, Router } from 'express';
1+
import { NextFunction, Request, Router } from 'express';
22
import {
33
createJwtForUser,
44
findFilteredPosts,
@@ -12,6 +12,7 @@ import { AuthResponse, NewUserInput } from '../../../types';
1212
import { Prisma } from '../../../../prisma/generated/client';
1313
import {
1414
authValidator,
15+
adminValidator,
1516
optionalAuthValidator,
1617
createAdminOrOwnerValidator,
1718
} from '../../../middlewares/validators';
@@ -25,23 +26,28 @@ import usersService from './users.service';
2526

2627
export const usersRouter = Router();
2728

28-
/*
29-
* NOTE: There are no restriction on any GET method,
30-
* because the API responding on a limited set of origins.
31-
* See the CORS settings in the app's entry point.
32-
*/
33-
34-
usersRouter.get('/', async (req, res) => {
29+
usersRouter.get('/', authValidator, adminValidator, async (req, res) => {
3530
const users = await usersService.getAllUsers();
3631
res.json(users);
3732
});
3833

39-
usersRouter.get('/:idOrUsername', async (req, res) => {
40-
const user = await usersService.findUserByIdOrByUsernameOrThrow(
41-
req.params.idOrUsername
42-
);
43-
res.json(user);
44-
});
34+
usersRouter.get(
35+
'/:idOrUsername',
36+
optionalAuthValidator,
37+
async (req, res, next) => {
38+
const param = req.params.idOrUsername;
39+
const user = await usersService.findUserByIdOrByUsernameOrThrow(param);
40+
if (param === user.id) {
41+
res.json(user);
42+
} else {
43+
const nextWrapper: NextFunction = (x: unknown) => {
44+
if (x) next(x);
45+
else res.json(user);
46+
};
47+
await createAdminOrOwnerValidator(() => user.id)(req, res, nextWrapper);
48+
}
49+
}
50+
);
4551

4652
usersRouter.get('/:id/posts', optionalAuthValidator, async (req, res) => {
4753
const authorId = req.params.id;

src/tests/api/v1/users.int.test.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ describe('Users endpoint', async () => {
143143
});
144144

145145
describe(`GET ${USERS_URL}`, () => {
146+
it('should respond with 401 on request without JWT', async () => {
147+
const res = await api.get(USERS_URL);
148+
assertUnauthorizedErrorRes(res);
149+
});
150+
151+
it('should respond with 401 on request with non-admin JWT', async () => {
152+
await createUser(userData);
153+
const { authorizedApi } = await prepForAuthorizedTest(userData);
154+
const res = await authorizedApi.get(USERS_URL);
155+
assertUnauthorizedErrorRes(res);
156+
});
157+
146158
it('should respond with users list, on request with admin JWT', async () => {
147159
await createUser(adminData);
148160
const dbUser = await createUser(userData);
@@ -173,14 +185,35 @@ describe('Users endpoint', async () => {
173185
assertNotFoundErrorRes(res);
174186
});
175187

176-
it('should respond with the found user on request with id or username', async () => {
177-
await createUser(adminData);
188+
it('should respond with 401 on non-owner request with username', async () => {
189+
await createUser(xUserData);
190+
const dbUser = await createUser(userData);
191+
const { authorizedApi } = await prepForAuthorizedTest(xUserData);
192+
const res = await authorizedApi.get(`${USERS_URL}/${dbUser.username}`);
193+
assertUnauthorizedErrorRes(res);
194+
});
195+
196+
it('should respond with the found user on request with id for anyone', async () => {
178197
const dbUser = await createUser(userData);
198+
const res = await api.get(`${USERS_URL}/${dbUser.id}`);
199+
const resUser = res.body as User;
200+
expect(res.statusCode).toBe(200);
201+
expect(res.type).toMatch(/json/);
202+
expect(resUser.id).toBe(dbUser.id);
203+
expect(resUser.isAdmin).toStrictEqual(false);
204+
expect(resUser.username).toBe(dbUser.username);
205+
expect(resUser.fullname).toBe(dbUser.fullname);
206+
expect(resUser.password).toBeUndefined();
207+
});
208+
209+
it('should respond with the found user on owner request with id or username', async () => {
210+
const dbUser = await createUser(userData);
211+
const { authorizedApi } = await prepForAuthorizedTest(userData);
179212
for (const param of [dbUser.id, dbUser.username]) {
180-
const res = await api.get(`${USERS_URL}/${param}`);
213+
const res = await authorizedApi.get(`${USERS_URL}/${param}`);
181214
const resUser = res.body as User;
182-
expect(res.type).toMatch(/json/);
183215
expect(res.statusCode).toBe(200);
216+
expect(res.type).toMatch(/json/);
184217
expect(resUser.id).toBe(dbUser.id);
185218
expect(resUser.isAdmin).toStrictEqual(false);
186219
expect(resUser.username).toBe(dbUser.username);

0 commit comments

Comments
 (0)