Add validations, req.authUser

This commit is contained in:
jeffvli
2022-10-18 01:45:46 -07:00
parent 238c90478e
commit 27b4b36cbf
12 changed files with 120 additions and 29 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ export const authenticateAdmin = (
res: Response, res: Response,
next: NextFunction next: NextFunction
) => { ) => {
if (!req.auth.isAdmin) { if (!req.authUser.isAdmin) {
return res.status(403).json({ return res.status(403).json({
error: { error: {
message: 'This action requires an administrator account.', message: 'This action requires an administrator account.',
+19 -8
View File
@@ -1,6 +1,7 @@
import { import {
ServerFolderPermissions, ServerCredential,
ServerPermissions, ServerFolderPermission,
ServerPermission,
User, User,
} from '@prisma/client'; } from '@prisma/client';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
@@ -9,8 +10,9 @@ import passport from 'passport';
export type AuthUser = User & { export type AuthUser = User & {
flatServerFolderPermissions: string[]; flatServerFolderPermissions: string[];
flatServerPermissions: string[]; flatServerPermissions: string[];
serverFolderPermissions: ServerFolderPermissions[]; serverFolderPermissions: ServerFolderPermission[];
serverPermissions: ServerPermissions[]; serverId?: string;
serverPermissions: ServerPermission[];
}; };
export const authenticate = ( export const authenticate = (
@@ -46,27 +48,36 @@ export const authenticate = (
} }
const flatServerFolderPermissions = user.serverFolderPermissions.map( const flatServerFolderPermissions = user.serverFolderPermissions.map(
(permission: ServerFolderPermissions) => permission.serverFolderId (permission: ServerFolderPermission) => permission.serverFolderId
); );
const flatServerPermissions = user.serverPermissions.map( const flatServerPermissions = user.serverPermissions.map(
(permission: ServerPermissions) => permission.serverId (permission: ServerPermission) => permission.serverId
); );
const auth = { const serverCredentials = user.serverCredentials.map(
(credential: ServerCredential) => ({
id: credential.id,
serverId: credential.serverId,
})
);
const props = {
createdAt: user?.createdAt, createdAt: user?.createdAt,
enabled: user?.enabled, enabled: user?.enabled,
flatServerFolderPermissions, flatServerFolderPermissions,
flatServerPermissions, flatServerPermissions,
id: user?.id, id: user?.id,
isAdmin: user?.isAdmin, isAdmin: user?.isAdmin,
server: req.params.serverId,
serverCredentials,
serverFolderPermissions: user?.serverFolderPermissions, serverFolderPermissions: user?.serverFolderPermissions,
serverPermissions: user?.serverPermissions, serverPermissions: user?.serverPermissions,
updatedAt: user?.updatedAt, updatedAt: user?.updatedAt,
username: user?.username, username: user?.username,
}; };
req.auth = auth; req.authUser = props;
return next(); return next();
})(req, res, next); })(req, res, next);
+1 -1
View File
@@ -1,5 +1,5 @@
declare namespace Express { declare namespace Express {
export interface Request { export interface Request {
auth: any; authUser: any;
} }
} }
@@ -3,7 +3,7 @@ import { paginationValidation, idValidation } from './shared.validation';
export const list = { export const list = {
body: z.object({}), body: z.object({}),
params: z.object({}), params: z.object({ ...idValidation('serverId') }),
query: z.object({ query: z.object({
...paginationValidation, ...paginationValidation,
serverFolderIds: z.string().min(1), serverFolderIds: z.string().min(1),
@@ -12,7 +12,7 @@ export const list = {
export const detail = { export const detail = {
body: z.object({}), body: z.object({}),
params: z.object({ ...idValidation }), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };
+7 -3
View File
@@ -5,23 +5,27 @@ import {
orderByValidation, orderByValidation,
paginationValidation, paginationValidation,
serverFolderIdValidation, serverFolderIdValidation,
serverUrlIdValidation,
} from './shared.validation'; } from './shared.validation';
const list = { const list = {
body: z.object({}), body: z.object({}),
params: z.object({}), params: z.object({ ...idValidation('serverId') }),
query: z.object({ query: z.object({
...paginationValidation, ...paginationValidation,
...serverFolderIdValidation, ...serverFolderIdValidation,
...orderByValidation, ...orderByValidation,
...serverUrlIdValidation,
sortBy: z.nativeEnum(AlbumSort), sortBy: z.nativeEnum(AlbumSort),
}), }),
}; };
const detail = { const detail = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({
...serverUrlIdValidation,
}),
}; };
export const albumsValidation = { export const albumsValidation = {
+1 -1
View File
@@ -20,7 +20,7 @@ const list = {
const detail = { const detail = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };
+33
View File
@@ -0,0 +1,33 @@
import { z } from 'zod';
const login = {
body: z.object({
password: z.string(),
username: z.string(),
}),
params: z.object({}),
query: z.object({}),
};
const register = {
body: z.object({
password: z.string().min(6).max(255),
username: z.string().min(4).max(26),
}),
params: z.object({}),
query: z.object({}),
};
const refresh = {
body: z.object({
refreshToken: z.string(),
}),
params: z.object({}),
query: z.object({}),
};
export const authValidation = {
login,
refresh,
register,
};
+6
View File
@@ -1,4 +1,7 @@
import { albumArtistsValidation } from './album-artists.validation';
import { albumsValidation } from './albums.validation'; import { albumsValidation } from './albums.validation';
import { artistsValidation } from './artists.validation';
import { authValidation } from './auth.validation';
import { serversValidation } from './servers.validation'; import { serversValidation } from './servers.validation';
import { songsValidation } from './songs.validation'; import { songsValidation } from './songs.validation';
import { usersValidation } from './users.validation'; import { usersValidation } from './users.validation';
@@ -6,7 +9,10 @@ import { usersValidation } from './users.validation';
export { validateRequest, TypedRequest } from './shared.validation'; export { validateRequest, TypedRequest } from './shared.validation';
export const validation = { export const validation = {
albumArtists: albumArtistsValidation,
albums: albumsValidation, albums: albumsValidation,
artists: artistsValidation,
auth: authValidation,
servers: serversValidation, servers: serversValidation,
songs: songsValidation, songs: songsValidation,
users: usersValidation, users: usersValidation,
+10 -3
View File
@@ -4,7 +4,7 @@ import { idValidation } from './shared.validation';
const detail = { const detail = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };
@@ -27,18 +27,25 @@ const create = {
const scan = { const scan = {
body: z.object({ serverFolderId: z.string().array().optional() }), body: z.object({ serverFolderId: z.string().array().optional() }),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };
const refresh = { const refresh = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}),
};
const createCredential = {
body: z.object({ credential: z.string() }),
params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };
export const serversValidation = { export const serversValidation = {
create, create,
createCredential,
detail, detail,
refresh, refresh,
scan, scan,
+38 -8
View File
@@ -27,6 +27,16 @@ type RequestValidation<TParams, TQuery, TBody> = {
type ErrorListItem = { type ErrorListItem = {
errors: ZodError<any>; errors: ZodError<any>;
type: ValidationType; type: ValidationType;
value: string;
};
const getErrorValue = (object: any, error: ZodError<any>) => {
const e = error.errors[0];
if (e.code === z.ZodIssueCode.invalid_type) {
return e.expected;
}
return object[e.path[0]][e.path[1]];
}; };
export const validateRequest: <TParams = any, TQuery = any, TBody = any>( export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
@@ -38,29 +48,45 @@ export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
if (params) { if (params) {
const parsed = params.safeParse(req.params); const parsed = params.safeParse(req.params);
if (!parsed.success) { if (!parsed.success) {
errors.push({ errors: parsed.error, type: ValidationType.PARAMS }); errors.push({
errors: parsed.error,
type: ValidationType.PARAMS,
value: getErrorValue(req.params, parsed.error),
});
} }
} }
if (query) { if (query) {
const parsed = query.safeParse(req.query); const parsed = query.safeParse(req.query);
if (!parsed.success) { if (!parsed.success) {
errors.push({ errors: parsed.error, type: ValidationType.QUERY }); const value = getErrorValue(req.query, parsed.error);
errors.push({
errors: parsed.error,
type: ValidationType.QUERY,
value,
});
} }
} }
if (body) { if (body) {
const parsed = body.safeParse(req.body); const parsed = body.safeParse(req.body);
if (!parsed.success) { if (!parsed.success) {
errors.push({ errors: parsed.error, type: ValidationType.BODY }); errors.push({
errors: parsed.error,
type: ValidationType.BODY,
value: getErrorValue(req.body, parsed.error),
});
} }
} }
if (errors.length > 0) { if (errors.length > 0) {
const message = JSON.stringify( const message = JSON.stringify(
[ [
`(${errors[0].type})`, // `(${errors[0].type})`,
`[${errors[0].errors.issues[0].path[0]}]`, `(${errors[0].type}: ${errors[0].errors.issues[0].path[0]})`,
errors[0].value && `[${errors[0].value}]`,
errors[0].errors.issues[0].message, errors[0].errors.issues[0].message,
].join(' ') ]
.filter((x) => x)
.join(' ')
); );
throw ApiError.badRequest(message); throw ApiError.badRequest(message);
@@ -87,8 +113,12 @@ export const paginationValidation = {
}), }),
}; };
export const idValidation = { export const serverUrlIdValidation = {
id: z.string().uuid(), serverUrlId: z.optional(z.string().uuid()),
};
export const idValidation = (property: string) => {
return { [property]: z.string().uuid() };
}; };
export const serverFolderIdValidation = { export const serverFolderIdValidation = {
+1 -1
View File
@@ -7,7 +7,7 @@ import {
const list = { const list = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('serverId') }),
query: z.object({ query: z.object({
...paginationValidation, ...paginationValidation,
...serverFolderIdValidation, ...serverFolderIdValidation,
+1 -1
View File
@@ -3,7 +3,7 @@ import { idValidation } from './shared.validation';
const detail = { const detail = {
body: z.object({}), body: z.object({}),
params: z.object(idValidation), params: z.object({ ...idValidation('id') }),
query: z.object({}), query: z.object({}),
}; };