diff --git a/server/helpers/api-model.ts b/server/helpers/api-model.ts index f5d824394..959916c8d 100644 --- a/server/helpers/api-model.ts +++ b/server/helpers/api-model.ts @@ -22,6 +22,7 @@ import { User, UserServerUrl, } from '@prisma/client'; +import { AuthUser } from '@middleware/authenticate'; const getSubsonicStreamUrl = (options: { deviceId: string; @@ -439,7 +440,7 @@ type DbAlbumInclude = { const albums = (options: { items: DbAlbum[] | any[]; serverUrl?: string; - user: User; + user: AuthUser; }) => { const { items, serverUrl, user } = options; return ( diff --git a/server/helpers/shared.helpers.ts b/server/helpers/shared.helpers.ts index e4e8fbd5a..f0e40f968 100644 --- a/server/helpers/shared.helpers.ts +++ b/server/helpers/shared.helpers.ts @@ -1,3 +1,4 @@ +import { ServerPermissionType } from '@prisma/client'; import { AuthUser } from '@/middleware'; import { ApiError } from '@/utils'; import { prisma } from '@lib/prisma'; @@ -62,6 +63,13 @@ const getAvailableServerFolderIds = async ( const serverFoldersWithAccess = await prisma.serverFolder.findMany({ where: { OR: [ + { + server: { + serverPermissions: { + some: { type: ServerPermissionType.ADMIN, userId: user.id }, + }, + }, + }, { AND: [ { diff --git a/server/middleware/authenticate.ts b/server/middleware/authenticate.ts index 4e05fcc27..5e21b9a7f 100644 --- a/server/middleware/authenticate.ts +++ b/server/middleware/authenticate.ts @@ -2,13 +2,7 @@ import { ServerFolderPermission, ServerPermission, User } from '@prisma/client'; import { NextFunction, Request, Response } from 'express'; import passport from 'passport'; -export type AuthUser = User & { - flatServerFolderPermissions: string[]; - flatServerPermissions: string[]; - serverFolderPermissions: ServerFolderPermission[]; - serverId?: string; - serverPermissions: ServerPermission[]; -}; +export type AuthUser = Request['authUser']; export const authenticate = ( req: Request, @@ -52,14 +46,15 @@ export const authenticate = ( const props = { createdAt: user?.createdAt, + deviceId: user?.deviceId, enabled: user?.enabled, flatServerFolderPermissions, flatServerPermissions, id: user?.id, isAdmin: user?.isAdmin, isSuperAdmin: user?.isSuperAdmin, - server: req.params.serverId, serverFolderPermissions: user?.serverFolderPermissions, + serverId: req.params.serverId, serverPermissions: user?.serverPermissions, updatedAt: user?.updatedAt, username: user?.username, diff --git a/server/routes/index.ts b/server/routes/index.ts index 40d20aa05..b0f73ed08 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -1,3 +1,4 @@ +import { ServerPermissionType } from '@prisma/client'; import { Router } from 'express'; import { helpers } from '../helpers'; import { authenticate } from '../middleware'; @@ -31,9 +32,16 @@ routes.param('serverId', (req, _res, next, serverId) => { req.authUser.serverId = serverId; helpers.shared.checkServerPermissions(req.authUser, { serverId }); - helpers.shared.checkServerFolderPermissions(req.authUser, { - serverFolderId, - }); + + const isNotServerAdmin = + req.authUser.serverPermissions.find((s) => s.serverId === serverId) + ?.type !== ServerPermissionType.ADMIN; + + if (isNotServerAdmin) { + helpers.shared.checkServerFolderPermissions(req.authUser, { + serverFolderId, + }); + } if (typeof req.query.serverFolderId === 'string') { req.query.serverFolderId = [req.query.serverFolderId]; diff --git a/server/services/album-artists.service.ts b/server/services/album-artists.service.ts index 86579747b..e886ffade 100644 --- a/server/services/album-artists.service.ts +++ b/server/services/album-artists.service.ts @@ -1,11 +1,11 @@ -import { User } from '@prisma/client'; import { Request } from 'express'; import { OffsetPagination } from '@/types/types'; import { ApiError } from '@/utils'; import { prisma } from '@lib/prisma'; +import { AuthUser } from '@middleware/authenticate'; import { folderPermissions } from '@utils/folder-permissions'; -const findById = async (options: { id: string; user: User }) => { +const findById = async (options: { id: string; user: AuthUser }) => { const { id, user } = options; const albumArtist = await prisma.albumArtist.findUnique({ include: { @@ -34,7 +34,7 @@ const findById = async (options: { id: string; user: User }) => { const findMany = async ( req: Request, - options: { serverFolderIds: string; user: User } & OffsetPagination + options: { serverFolderIds: string; user: AuthUser } & OffsetPagination ) => { const { user, take, serverFolderIds: rServerFolderIds, skip } = options; const serverFolderIds = rServerFolderIds.split(','); diff --git a/server/services/artists.service.ts b/server/services/artists.service.ts index 79d4bd6a6..d062fe714 100644 --- a/server/services/artists.service.ts +++ b/server/services/artists.service.ts @@ -1,10 +1,10 @@ -import { User } from '@prisma/client'; import { Request } from 'express'; +import { AuthUser } from '@middleware/authenticate'; import { prisma } from '../lib'; import { OffsetPagination } from '../types/types'; import { ApiError, folderPermissions } from '../utils'; -const findById = async (options: { id: string; user: User }) => { +const findById = async (options: { id: string; user: AuthUser }) => { const { id, user } = options; const artist = await prisma.artist.findUnique({ @@ -26,7 +26,7 @@ const findById = async (options: { id: string; user: User }) => { const findMany = async ( req: Request, - options: { serverFolderIds: string; user: User } & OffsetPagination + options: { serverFolderIds: string; user: AuthUser } & OffsetPagination ) => { const { user, skip, take, serverFolderIds: rServerFolderIds } = options; const serverFolderIds = rServerFolderIds.split(','); diff --git a/server/services/auth.service.ts b/server/services/auth.service.ts index f61522499..f501a13e9 100644 --- a/server/services/auth.service.ts +++ b/server/services/auth.service.ts @@ -1,6 +1,6 @@ -import { User } from '@prisma/client'; import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; +import { AuthUser } from '@middleware/authenticate'; import { prisma } from '../lib'; import { generateRefreshToken, generateToken } from '../lib/passport'; import { ApiSuccess, randomString } from '../utils'; @@ -57,7 +57,7 @@ const register = async (options: { password: string; username: string }) => { return user; }; -const logout = async (options: { user: User }) => { +const logout = async (options: { user: AuthUser }) => { const { user } = options; await prisma.refreshToken.deleteMany({ where: { userId: user.id }, diff --git a/server/types/express/index.d.ts b/server/types/express/index.d.ts index 7471f04c1..5ec2af8c3 100644 --- a/server/types/express/index.d.ts +++ b/server/types/express/index.d.ts @@ -1,5 +1,32 @@ declare namespace Express { export interface Request { - authUser: any; + authUser: { + createdAt: Date; + deviceId: string; + enabled: boolean; + flatServerFolderPermissions: string[]; + flatServerPermissions: string[]; + id: string; + isAdmin: boolean; + isSuperAdmin: boolean; + serverFolderPermissions: { + createdAt: Date; + id: string; + serverFolderId: string; + updatedAt: Date; + userId: string; + }[]; + serverId: string; + serverPermissions: { + createdAt: Date; + id: string; + serverId: string; + type: any; + updatedAt: Date; + userId: string; + }[]; + updatedAt: Date; + username: string; + }; } } diff --git a/server/utils/folder-permissions.ts b/server/utils/folder-permissions.ts index 74f218f7e..b446d077a 100644 --- a/server/utils/folder-permissions.ts +++ b/server/utils/folder-permissions.ts @@ -1,4 +1,5 @@ import { User } from '@prisma/client'; +import { AuthUser } from '@middleware/authenticate'; import { prisma } from '../lib'; export enum Roles { @@ -16,7 +17,10 @@ export enum FolderRoles { ADMIN = 4, } -export const folderPermissions = async (serverFolderIds: any[], user: User) => { +export const folderPermissions = async ( + serverFolderIds: any[], + user: AuthUser +) => { if (user.isAdmin) { return true; }