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,
next: NextFunction
) => {
if (!req.auth.isAdmin) {
if (!req.authUser.isAdmin) {
return res.status(403).json({
error: {
message: 'This action requires an administrator account.',
+19 -8
View File
@@ -1,6 +1,7 @@
import {
ServerFolderPermissions,
ServerPermissions,
ServerCredential,
ServerFolderPermission,
ServerPermission,
User,
} from '@prisma/client';
import { NextFunction, Request, Response } from 'express';
@@ -9,8 +10,9 @@ import passport from 'passport';
export type AuthUser = User & {
flatServerFolderPermissions: string[];
flatServerPermissions: string[];
serverFolderPermissions: ServerFolderPermissions[];
serverPermissions: ServerPermissions[];
serverFolderPermissions: ServerFolderPermission[];
serverId?: string;
serverPermissions: ServerPermission[];
};
export const authenticate = (
@@ -46,27 +48,36 @@ export const authenticate = (
}
const flatServerFolderPermissions = user.serverFolderPermissions.map(
(permission: ServerFolderPermissions) => permission.serverFolderId
(permission: ServerFolderPermission) => permission.serverFolderId
);
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,
enabled: user?.enabled,
flatServerFolderPermissions,
flatServerPermissions,
id: user?.id,
isAdmin: user?.isAdmin,
server: req.params.serverId,
serverCredentials,
serverFolderPermissions: user?.serverFolderPermissions,
serverPermissions: user?.serverPermissions,
updatedAt: user?.updatedAt,
username: user?.username,
};
req.auth = auth;
req.authUser = props;
return next();
})(req, res, next);
+1 -1
View File
@@ -1,5 +1,5 @@
declare namespace Express {
export interface Request {
auth: any;
authUser: any;
}
}
@@ -3,7 +3,7 @@ import { paginationValidation, idValidation } from './shared.validation';
export const list = {
body: z.object({}),
params: z.object({}),
params: z.object({ ...idValidation('serverId') }),
query: z.object({
...paginationValidation,
serverFolderIds: z.string().min(1),
@@ -12,7 +12,7 @@ export const list = {
export const detail = {
body: z.object({}),
params: z.object({ ...idValidation }),
params: z.object({ ...idValidation('id') }),
query: z.object({}),
};
+7 -3
View File
@@ -5,23 +5,27 @@ import {
orderByValidation,
paginationValidation,
serverFolderIdValidation,
serverUrlIdValidation,
} from './shared.validation';
const list = {
body: z.object({}),
params: z.object({}),
params: z.object({ ...idValidation('serverId') }),
query: z.object({
...paginationValidation,
...serverFolderIdValidation,
...orderByValidation,
...serverUrlIdValidation,
sortBy: z.nativeEnum(AlbumSort),
}),
};
const detail = {
body: z.object({}),
params: z.object(idValidation),
query: z.object({}),
params: z.object({ ...idValidation('id') }),
query: z.object({
...serverUrlIdValidation,
}),
};
export const albumsValidation = {
+1 -1
View File
@@ -20,7 +20,7 @@ const list = {
const detail = {
body: z.object({}),
params: z.object(idValidation),
params: z.object({ ...idValidation('id') }),
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 { artistsValidation } from './artists.validation';
import { authValidation } from './auth.validation';
import { serversValidation } from './servers.validation';
import { songsValidation } from './songs.validation';
import { usersValidation } from './users.validation';
@@ -6,7 +9,10 @@ import { usersValidation } from './users.validation';
export { validateRequest, TypedRequest } from './shared.validation';
export const validation = {
albumArtists: albumArtistsValidation,
albums: albumsValidation,
artists: artistsValidation,
auth: authValidation,
servers: serversValidation,
songs: songsValidation,
users: usersValidation,
+10 -3
View File
@@ -4,7 +4,7 @@ import { idValidation } from './shared.validation';
const detail = {
body: z.object({}),
params: z.object(idValidation),
params: z.object({ ...idValidation('id') }),
query: z.object({}),
};
@@ -27,18 +27,25 @@ const create = {
const scan = {
body: z.object({ serverFolderId: z.string().array().optional() }),
params: z.object(idValidation),
params: z.object({ ...idValidation('id') }),
query: z.object({}),
};
const refresh = {
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({}),
};
export const serversValidation = {
create,
createCredential,
detail,
refresh,
scan,
+38 -8
View File
@@ -27,6 +27,16 @@ type RequestValidation<TParams, TQuery, TBody> = {
type ErrorListItem = {
errors: ZodError<any>;
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>(
@@ -38,29 +48,45 @@ export const validateRequest: <TParams = any, TQuery = any, TBody = any>(
if (params) {
const parsed = params.safeParse(req.params);
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) {
const parsed = query.safeParse(req.query);
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) {
const parsed = body.safeParse(req.body);
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) {
const message = JSON.stringify(
[
`(${errors[0].type})`,
`[${errors[0].errors.issues[0].path[0]}]`,
// `(${errors[0].type})`,
`(${errors[0].type}: ${errors[0].errors.issues[0].path[0]})`,
errors[0].value && `[${errors[0].value}]`,
errors[0].errors.issues[0].message,
].join(' ')
]
.filter((x) => x)
.join(' ')
);
throw ApiError.badRequest(message);
@@ -87,8 +113,12 @@ export const paginationValidation = {
}),
};
export const idValidation = {
id: z.string().uuid(),
export const serverUrlIdValidation = {
serverUrlId: z.optional(z.string().uuid()),
};
export const idValidation = (property: string) => {
return { [property]: z.string().uuid() };
};
export const serverFolderIdValidation = {
+1 -1
View File
@@ -7,7 +7,7 @@ import {
const list = {
body: z.object({}),
params: z.object(idValidation),
params: z.object({ ...idValidation('serverId') }),
query: z.object({
...paginationValidation,
...serverFolderIdValidation,
+1 -1
View File
@@ -3,7 +3,7 @@ import { idValidation } from './shared.validation';
const detail = {
body: z.object({}),
params: z.object(idValidation),
params: z.object({ ...idValidation('id') }),
query: z.object({}),
};