From 514714071dfe4be04e607fe6412f5b3f0ef74dd4 Mon Sep 17 00:00:00 2001 From: sct Date: Sat, 19 Sep 2020 23:28:39 +0900 Subject: [PATCH] feat(api): allow plex logins from users who have access to the server --- package.json | 1 + server/api/plextv.ts | 71 ++++++++++++++++++++++++++++++++++++++++++ server/lib/settings.ts | 5 ++- server/routes/auth.ts | 18 +++++++++-- yarn.lock | 7 +++++ 5 files changed, 97 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4055ad53..944ef8e7 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@types/react-transition-group": "^4.4.0", "@types/swagger-ui-express": "^4.1.2", "@types/uuid": "^8.3.0", + "@types/xml2js": "^0.4.5", "@types/yamljs": "^0.2.31", "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^3.10.1", diff --git a/server/api/plextv.ts b/server/api/plextv.ts index 26f090fb..cc6ba9a1 100644 --- a/server/api/plextv.ts +++ b/server/api/plextv.ts @@ -1,4 +1,6 @@ import axios, { AxiosInstance } from 'axios'; +import xml2js from 'xml2js'; +import { getSettings } from '../lib/settings'; interface PlexAccountResponse { user: PlexUser; @@ -26,6 +28,33 @@ interface PlexUser { entitlements: string[]; } +interface ServerResponse { + $: { + id: string; + serverId: string; + machineIdentifier: string; + name: string; + lastSeenAt: string; + numLibraries: string; + owned: string; + }; +} + +interface FriendResponse { + MediaContainer: { + User: { + $: { + id: string; + title: string; + username: string; + email: string; + thumb: string; + }; + Server: ServerResponse[]; + }[]; + }; +} + class PlexTvAPI { private authToken: string; private axios: AxiosInstance; @@ -57,6 +86,48 @@ class PlexTvAPI { throw new Error('Invalid auth token'); } } + + public async getFriends(): Promise { + const response = await this.axios.get('/pms/friends/all', { + transformResponse: [], + responseType: 'text', + }); + + const parsedXml = (await xml2js.parseStringPromise( + response.data + )) as FriendResponse; + + return parsedXml; + } + + public async checkUserAccess(authUser: PlexUser): Promise { + const settings = getSettings(); + + try { + if (!settings.plex.machineId) { + throw new Error('Plex is not configured!'); + } + + const friends = await this.getFriends(); + + const users = friends.MediaContainer.User; + + const user = users.find((u) => Number(u.$.id) === authUser.id); + + if (!user) { + throw new Error( + 'This user does not exist on the main plex accounts shared list' + ); + } + + return !!user.Server.find( + (server) => server.$.machineIdentifier === settings.plex.machineId + ); + } catch (e) { + console.log(`Error checking user access: ${e.message}`); + return false; + } + } } export default PlexTvAPI; diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 67abf17d..013272ed 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -10,7 +10,7 @@ interface Library { interface PlexSettings { name: string; - machineId: string; + machineId?: string; ip: string; port: number; libraries: Library[]; @@ -67,10 +67,9 @@ class Settings { apiKey: 'temp', }, plex: { - name: 'Main Server', + name: '', ip: '127.0.0.1', port: 32400, - machineId: '', libraries: [], }, radarr: [], diff --git a/server/routes/auth.ts b/server/routes/auth.ts index b386f9cb..df5f0a35 100644 --- a/server/routes/auth.ts +++ b/server/routes/auth.ts @@ -70,8 +70,22 @@ authRoutes.post('/login', async (req, res) => { // If we get to this point, the user does not already exist so we need to create the // user _assuming_ they have access to the plex server - // (We cant do this until we finish the settings sytem and actually - // store the user token in ticket #55) + const mainUser = await userRepository.findOneOrFail({ + select: ['id', 'plexToken'], + order: { id: 'ASC' }, + }); + const mainPlexTv = new PlexTvAPI(mainUser.plexToken ?? ''); + if (await mainPlexTv.checkUserAccess(account)) { + user = new User({ + email: account.email, + username: account.username, + plexId: account.id, + plexToken: account.authToken, + permissions: Permission.REQUEST, + avatar: account.thumb, + }); + await userRepository.save(user); + } } // Set logged in session diff --git a/yarn.lock b/yarn.lock index 427ead99..c5285e9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1845,6 +1845,13 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== +"@types/xml2js@^0.4.5": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa" + integrity sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w== + dependencies: + "@types/node" "*" + "@types/yamljs@^0.2.31": version "0.2.31" resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245"