From bda7858b66dc7023fb417f924c45cf24fdc28a2a Mon Sep 17 00:00:00 2001 From: CyferShepard Date: Sat, 28 May 2022 08:50:23 +0200 Subject: [PATCH] Delete auth.ts --- server/routes/auth.ts | 610 ------------------------------------------ 1 file changed, 610 deletions(-) delete mode 100644 server/routes/auth.ts diff --git a/server/routes/auth.ts b/server/routes/auth.ts deleted file mode 100644 index 69d2fd52..00000000 --- a/server/routes/auth.ts +++ /dev/null @@ -1,610 +0,0 @@ -import { Router } from 'express'; -import { getRepository } from 'typeorm'; -import JellyfinAPI from '../api/jellyfin'; -import PlexTvAPI from '../api/plextv'; -import { MediaServerType } from '../constants/server'; -import { UserType } from '../constants/user'; -import { User } from '../entity/User'; -import { Permission } from '../lib/permissions'; -import { getSettings } from '../lib/settings'; -import logger from '../logger'; -import { isAuthenticated } from '../middleware/auth'; - -const authRoutes = Router(); - -authRoutes.get('/me', isAuthenticated(), async (req, res) => { - const userRepository = getRepository(User); - if (!req.user) { - return res.status(500).json({ - status: 500, - error: 'Please sign in.', - }); - } - const user = await userRepository.findOneOrFail({ - where: { id: req.user.id }, - }); - - return res.status(200).json(user); -}); - -authRoutes.post('/plex', async (req, res, next) => { - const settings = getSettings(); - const userRepository = getRepository(User); - const body = req.body as { authToken?: string }; - - if (!body.authToken) { - return next({ - status: 500, - message: 'Authentication token required.', - }); - } - - if ( - settings.main.mediaServerType != MediaServerType.PLEX && - settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED - ) { - return res.status(500).json({ error: 'Plex login is disabled' }); - } - try { - // First we need to use this auth token to get the user's email from plex.tv - const plextv = new PlexTvAPI(body.authToken); - const account = await plextv.getUser(); - - // Next let's see if the user already exists - let user = await userRepository - .createQueryBuilder('user') - .where('user.plexId = :id', { id: account.id }) - .orWhere('user.email = :email', { - email: account.email.toLowerCase(), - }) - .getOne(); - - if (!user && !(await userRepository.count())) { - user = new User({ - email: account.email, - plexUsername: account.username, - plexId: account.id, - plexToken: account.authToken, - permissions: Permission.ADMIN, - avatar: account.thumb, - userType: UserType.PLEX, - }); - - await userRepository.save(user); - } else { - const mainUser = await userRepository.findOneOrFail({ - select: ['id', 'plexToken', 'plexId'], - order: { id: 'ASC' }, - }); - const mainPlexTv = new PlexTvAPI(mainUser.plexToken ?? ''); - - if ( - account.id === mainUser.plexId || - (await mainPlexTv.checkUserAccess(account.id)) - ) { - if (user) { - if (!user.plexId) { - logger.info( - 'Found matching Plex user; updating user with Plex data', - { - label: 'API', - ip: req.ip, - email: user.email, - userId: user.id, - plexId: account.id, - plexUsername: account.username, - } - ); - } - - user.plexToken = body.authToken; - user.plexId = account.id; - user.avatar = account.thumb; - user.email = account.email; - user.plexUsername = account.username; - user.userType = UserType.PLEX; - - await userRepository.save(user); - } else if (!settings.main.newPlexLogin) { - logger.warn( - 'Failed sign-in attempt by unimported Plex user with access to the media server', - { - label: 'API', - ip: req.ip, - email: account.email, - plexId: account.id, - plexUsername: account.username, - } - ); - return next({ - status: 403, - message: 'Access denied.', - }); - } else { - logger.info( - 'Sign-in attempt from Plex user with access to the media server; creating new Overseerr user', - { - label: 'API', - ip: req.ip, - email: account.email, - plexId: account.id, - plexUsername: account.username, - } - ); - user = new User({ - email: account.email, - plexUsername: account.username, - plexId: account.id, - plexToken: account.authToken, - permissions: settings.main.defaultPermissions, - avatar: account.thumb, - userType: UserType.PLEX, - }); - - await userRepository.save(user); - } - } else { - logger.warn( - 'Failed sign-in attempt by Plex user without access to the media server', - { - label: 'API', - ip: req.ip, - email: account.email, - plexId: account.id, - plexUsername: account.username, - } - ); - return next({ - status: 403, - message: 'Access denied.', - }); - } - } - - // Set logged in session - if (req.session) { - req.session.userId = user.id; - } - - return res.status(200).json(user?.filter() ?? {}); - } catch (e) { - logger.error('Something went wrong authenticating with Plex account', { - label: 'API', - errorMessage: e.message, - ip: req.ip, - }); - return next({ - status: 500, - message: 'Unable to authenticate.', - }); - } -}); - -authRoutes.post('/jellyfin', async (req, res, next) => { - const settings = getSettings(); - const userRepository = getRepository(User); - const body = req.body as { - username?: string; - password?: string; - hostname?: string; - email?: string; - }; - - //Make sure jellyfin login is enabled, but only if jellyfin is not already configured - if ( - settings.main.mediaServerType !== MediaServerType.JELLYFIN && - settings.jellyfin.hostname !== '' - ) { - return res.status(500).json({ error: 'Jellyfin login is disabled' }); - } else if (!body.username || !body.password) { - return res - .status(500) - .json({ error: 'You must provide an username and a password' }); - } else if (settings.jellyfin.hostname !== '' && body.hostname) { - return res - .status(500) - .json({ error: 'Jellyfin hostname already configured' }); - } else if (settings.jellyfin.hostname === '' && !body.hostname) { - return res.status(500).json({ error: 'No hostname provided.' }); - } - - try { - const hostname = - settings.jellyfin.hostname !== '' - ? settings.jellyfin.hostname - : body.hostname; - const { externalHostname } = getSettings().jellyfin; - - // Try to find deviceId that corresponds to jellyfin user, else generate a new one - let user = await userRepository.findOne({ - where: { jellyfinUsername: body.username }, - }); - - let deviceId = ''; - if (user) { - deviceId = user.jellyfinDeviceId ?? ''; - } else { - deviceId = Buffer.from(`BOT_overseerr_${body.username ?? ''}`).toString( - 'base64' - ); - } - // First we need to attempt to log the user in to jellyfin - const jellyfinserver = new JellyfinAPI(hostname ?? '', undefined, deviceId); - const jellyfinHost = - externalHostname && externalHostname.length > 0 - ? externalHostname - : hostname; - - const account = await jellyfinserver.login(body.username, body.password); - // Next let's see if the user already exists - user = await userRepository.findOne({ - where: { jellyfinUserId: account.User.Id }, - }); - - if (user) { - // Let's check if their authtoken is up to date - if (user.jellyfinAuthToken !== account.AccessToken) { - user.jellyfinAuthToken = account.AccessToken; - } - - // Update the users avatar with their jellyfin profile pic (incase it changed) - if (account.User.PrimaryImageTag) { - user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; - } else { - user.avatar = '/os_logo_square.png'; - } - - user.jellyfinUsername = account.User.Name; - - if (user.username === account.User.Name) { - user.username = ''; - } - await userRepository.save(user); - } else if (!settings.main.newPlexLogin) { - logger.warn( - 'Failed sign-in attempt by unimported Jellyfin user with access to the media server', - { - label: 'API', - ip: req.ip, - jellyfinUserId: account.User.Id, - jellyfinUsername: account.User.Name, - } - ); - return next({ - status: 403, - message: 'Access denied.', - }); - } else { - // Here we check if it's the first user. If it is, we create the user with no check - // and give them admin permissions - const totalUsers = await userRepository.count(); - if (totalUsers === 0) { - logger.info( - 'Sign-in attempt from Jellyfin user with access to the media server; creating initial admin user for Overseerr', - { - label: 'API', - ip: req.ip, - jellyfinUsername: account.User.Name, - } - ); - user = new User({ - email: body.email, - jellyfinUsername: account.User.Name, - jellyfinUserId: account.User.Id, - jellyfinDeviceId: deviceId, - jellyfinAuthToken: account.AccessToken, - permissions: Permission.ADMIN, - avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` - : '/os_logo_square.png', - userType: UserType.JELLYFIN, - }); - await userRepository.save(user); - - //Update hostname in settings if it doesn't exist (initial configuration) - //Also set mediaservertype to JELLYFIN - if (settings.jellyfin.hostname === '') { - settings.main.mediaServerType = MediaServerType.JELLYFIN; - settings.jellyfin.hostname = body.hostname ?? ''; - settings.jellyfin.serverId = account.User.ServerId; - settings.save(); - } - } - - if (!user) { - if (!body.email) { - throw new Error('add_email'); - } - - user = new User({ - email: body.email, - jellyfinUsername: account.User.Name, - jellyfinUserId: account.User.Id, - jellyfinDeviceId: deviceId, - jellyfinAuthToken: account.AccessToken, - permissions: settings.main.defaultPermissions, - avatar: account.User.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` - : '/os_logo_square.png', - userType: UserType.JELLYFIN, - }); - //initialize Jellyfin/Emby users with local login - const passedExplicitPassword = - body.password && body.password.length > 0; - if (passedExplicitPassword) { - await user.setPassword(body.password); - } - await userRepository.save(user); - } - } - - // Set logged in session - if (req.session) { - req.session.userId = user?.id; - } - - return res.status(200).json(user?.filter() ?? {}); - } catch (e) { - if (e.message === 'Unauthorized') { - logger.info( - 'Failed login attempt from user with incorrect Jellyfin credentials', - { - label: 'Auth', - account: { - ip: req.ip, - email: body.username, - password: '__REDACTED__', - }, - } - ); - return next({ - status: 401, - message: 'Unauthorized', - }); - } else if (e.message === 'add_email') { - return next({ - status: 406, - message: 'CREDENTIAL_ERROR_ADD_EMAIL', - }); - } else { - logger.error(e.message, { label: 'Auth' }); - return next({ - status: 500, - message: 'Something went wrong.', - }); - } - } -}); - -authRoutes.post('/local', async (req, res, next) => { - const settings = getSettings(); - const userRepository = getRepository(User); - const body = req.body as { email?: string; password?: string }; - - if (!settings.main.localLogin) { - return res.status(500).json({ error: 'Password sign-in is disabled.' }); - } else if (!body.email || !body.password) { - return res.status(500).json({ - error: 'You must provide both an email address and a password.', - }); - } - try { - const user = await userRepository - .createQueryBuilder('user') - .select(['user.id', 'user.email', 'user.password', 'user.plexId']) - .where('user.email = :email', { email: body.email.toLowerCase() }) - .getOne(); - - if (!user || !(await user.passwordMatch(body.password))) { - logger.warn('Failed sign-in attempt using invalid Overseerr password', { - label: 'API', - ip: req.ip, - email: body.email, - userId: user?.id, - }); - return next({ - status: 403, - message: 'Access denied.', - }); - } - - const mainUser = await userRepository.findOneOrFail({ - select: ['id', 'plexToken', 'plexId'], - order: { id: 'ASC' }, - }); - const mainPlexTv = new PlexTvAPI(mainUser.plexToken ?? ''); - - if (!user.plexId) { - try { - const plexUsersResponse = await mainPlexTv.getUsers(); - const account = plexUsersResponse.MediaContainer.User.find( - (account) => - account.$.email && - account.$.email.toLowerCase() === user.email.toLowerCase() - )?.$; - - if ( - account && - (await mainPlexTv.checkUserAccess(parseInt(account.id))) - ) { - logger.info( - 'Found matching Plex user; updating user with Plex data', - { - label: 'API', - ip: req.ip, - email: body.email, - userId: user.id, - plexId: account.id, - plexUsername: account.username, - } - ); - - user.plexId = parseInt(account.id); - user.avatar = account.thumb; - user.email = account.email; - user.plexUsername = account.username; - user.userType = UserType.PLEX; - - await userRepository.save(user); - } - } catch (e) { - logger.error('Something went wrong fetching Plex users', { - label: 'API', - errorMessage: e.message, - }); - } - } - - if ( - user.plexId && - user.plexId !== mainUser.plexId && - !(await mainPlexTv.checkUserAccess(user.plexId)) - ) { - logger.warn( - 'Failed sign-in attempt from Plex user without access to the media server', - { - label: 'API', - account: { - ip: req.ip, - email: body.email, - userId: user.id, - plexId: user.plexId, - }, - } - ); - return next({ - status: 403, - message: 'Access denied.', - }); - } - - // Set logged in session - if (user && req.session) { - req.session.userId = user.id; - } - - return res.status(200).json(user?.filter() ?? {}); - } catch (e) { - logger.error( - 'Something went wrong authenticating with Overseerr password', - { - label: 'API', - errorMessage: e.message, - ip: req.ip, - email: body.email, - } - ); - return next({ - status: 500, - message: 'Unable to authenticate.', - }); - } -}); - -authRoutes.post('/logout', (req, res, next) => { - req.session?.destroy((err) => { - if (err) { - return next({ - status: 500, - message: 'Something went wrong.', - }); - } - - return res.status(200).json({ status: 'ok' }); - }); -}); - -authRoutes.post('/reset-password', async (req, res, next) => { - const userRepository = getRepository(User); - const body = req.body as { email?: string }; - - if (!body.email) { - return next({ - status: 500, - message: 'Email address required.', - }); - } - - const user = await userRepository - .createQueryBuilder('user') - .where('user.email = :email', { email: body.email.toLowerCase() }) - .getOne(); - - if (user) { - await user.resetPassword(); - userRepository.save(user); - logger.info('Successfully sent password reset link', { - label: 'API', - ip: req.ip, - email: body.email, - }); - } else { - logger.error('Something went wrong sending password reset link', { - label: 'API', - ip: req.ip, - email: body.email, - }); - } - - return res.status(200).json({ status: 'ok' }); -}); - -authRoutes.post('/reset-password/:guid', async (req, res, next) => { - const userRepository = getRepository(User); - - if (!req.body.password || req.body.password?.length < 8) { - logger.warn('Failed password reset attempt using invalid new password', { - label: 'API', - ip: req.ip, - guid: req.params.guid, - }); - return next({ - status: 500, - message: 'Password must be at least 8 characters long.', - }); - } - - const user = await userRepository.findOne({ - where: { resetPasswordGuid: req.params.guid }, - }); - - if (!user) { - logger.warn('Failed password reset attempt using invalid recovery link', { - label: 'API', - ip: req.ip, - guid: req.params.guid, - }); - return next({ - status: 500, - message: 'Invalid password reset link.', - }); - } - - if ( - !user.recoveryLinkExpirationDate || - user.recoveryLinkExpirationDate <= new Date() - ) { - logger.warn('Failed password reset attempt using expired recovery link', { - label: 'API', - ip: req.ip, - guid: req.params.guid, - email: user.email, - }); - return next({ - status: 500, - message: 'Invalid password reset link.', - }); - } - user.recoveryLinkExpirationDate = null; - userRepository.save(user); - logger.info('Successfully reset password', { - label: 'API', - ip: req.ip, - guid: req.params.guid, - email: user.email, - }); - - return res.status(200).json({ status: 'ok' }); -}); - -export default authRoutes;