Created endpoint for authenticating with plex and linking your plex user to the logged in seasoned user. User database table gets a new plex_userid column. This will be used to fetch tautulli stats for your account.
This commit is contained in:
		| @@ -1,8 +1,9 @@ | |||||||
| CREATE TABLE IF NOT EXISTS user ( | CREATE TABLE IF NOT EXISTS user ( | ||||||
|     user_name varchar(127) UNIQUE, |     user_name varchar(127) UNIQUE, | ||||||
|     password varchar(127), |     password varchar(127), | ||||||
|     email varchar(127) UNIQUE, |  | ||||||
|     admin boolean DEFAULT 0, |     admin boolean DEFAULT 0, | ||||||
|  |     email varchar(127) UNIQUE, | ||||||
|  |     plex_userid varchar(127) DEFAULT NULL, | ||||||
|     primary key (user_name) |     primary key (user_name) | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,64 +2,87 @@ const assert = require('assert'); | |||||||
| const establishedDatabase = require('src/database/database'); | const establishedDatabase = require('src/database/database'); | ||||||
|  |  | ||||||
| class UserRepository { | class UserRepository { | ||||||
|    constructor(database) { |   constructor(database) { | ||||||
|       this.database = database || establishedDatabase; |     this.database = database || establishedDatabase; | ||||||
|       this.queries = { |     this.queries = { | ||||||
|          read: 'select * from user where lower(user_name) = lower(?)', |       read: 'select * from user where lower(user_name) = lower(?)', | ||||||
|          create: 'insert into user (user_name) values (?)', |       create: 'insert into user (user_name) values (?)', | ||||||
|          change: 'update user set password = ? where user_name = ?', |       change: 'update user set password = ? where user_name = ?', | ||||||
|          retrieveHash: 'select * from user where user_name = ?', |       retrieveHash: 'select * from user where user_name = ?', | ||||||
|          getAdminStateByUser: 'select admin from user where user_name = ?' |       getAdminStateByUser: 'select admin from user where user_name = ?', | ||||||
|       }; |       link: 'update user set plex_userid = ? where user_name = ?' | ||||||
|    } |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|    /** | /** | ||||||
|    * Create a user in a database. | * Create a user in a database. | ||||||
|    * @param {User} user the user you want to create | * @param {User} user the user you want to create | ||||||
|    * @returns {Promise} | * @returns {Promise} | ||||||
|    */ | */ | ||||||
|    create(user) { | create(user) { | ||||||
|       return Promise.resolve() |   return this.database.get(this.queries.read, user.username) | ||||||
|          .then(() => this.database.get(this.queries.read, user.username)) |     .then(() => this.database.run(this.queries.create, user.username)) | ||||||
|          .then(() => this.database.run(this.queries.create, user.username)) |     .catch((error) => { | ||||||
|          .catch((error) => { |       if (error.name === 'AssertionError' || error.message.endsWith('user_name')) { | ||||||
|             if (error.name === 'AssertionError' || error.message.endsWith('user_name')) { |          throw new Error('That username is already registered'); | ||||||
|                throw new Error('That username is already registered'); |       } | ||||||
|             } |       throw Error(error) | ||||||
|             throw Error(error) |     }); | ||||||
|          }); | } | ||||||
|    } |  | ||||||
|  |  | ||||||
|    /** | /** | ||||||
|    * Retrieve a password from a database. | * Retrieve a password from a database. | ||||||
|    * @param {User} user the user you want to retrieve the password | * @param {User} user the user you want to retrieve the password | ||||||
|    * @returns {Promise} | * @returns {Promise} | ||||||
|    */ | */ | ||||||
|    retrieveHash(user) { | retrieveHash(user) { | ||||||
|       return Promise.resolve() |   console.log('retrieving hash for user', user) | ||||||
|          .then(() => this.database.get(this.queries.retrieveHash, user.username)) |   return this.database.get(this.queries.retrieveHash, user.username) | ||||||
|          .then((row) => { |     .then(row => { | ||||||
|             assert(row, 'The user does not exist.'); |       assert(row, 'The user does not exist.'); | ||||||
|             return row.password; |       return row.password; | ||||||
|          }) |     }) | ||||||
|          .catch((err) => { console.log(error); throw new Error('Unable to find your user.'); }); |     .catch(err => { console.log(error); throw new Error('Unable to find your user.'); }) | ||||||
|    } | } | ||||||
|  |  | ||||||
|    /** | /** | ||||||
|    * Change a user's password in a database. | * Change a user's password in a database. | ||||||
|    * @param {User} user the user you want to create | * @param {User} user the user you want to create | ||||||
|    * @param {String} password the new password you want to change | * @param {String} password the new password you want to change | ||||||
|    * @returns {Promise} | * @returns {Promise} | ||||||
|    */ | */ | ||||||
|    changePassword(user, password) { | changePassword(user, password) { | ||||||
|       return Promise.resolve(this.database.run(this.queries.change, [password, user.username])); |   return this.database.run(this.queries.change, [password, user.username]) | ||||||
|    } | } | ||||||
|  |  | ||||||
|    checkAdmin(user) { | /** | ||||||
|       return this.database.get(this.queries.getAdminStateByUser, user.username).then((row) => { | * Link plex userid with seasoned user | ||||||
|          return row.admin; | * @param {User} user the user you want to lunk plex userid with | ||||||
|       }); | * @param {Number} plexUserID plex unique id | ||||||
|    } | * @returns {Promsie} | ||||||
|  | */ | ||||||
|  | linkPlexUserId(username, plexUserID) { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     this.database.run(this.queries.link, [plexUserID, username]) | ||||||
|  |       .then(row => resolve(row)) | ||||||
|  |       .catch(error => { | ||||||
|  |         // TODO log this unknown db error | ||||||
|  |         console.log('db error', error) | ||||||
|  |  | ||||||
|  |         reject({ | ||||||
|  |           status: 500, | ||||||
|  |           message: 'An unexpected error occured while linking plex and seasoned accounts', | ||||||
|  |           source: 'seasoned database' | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | checkAdmin(user) { | ||||||
|  |   return this.database.get(this.queries.getAdminStateByUser, user.username).then((row) => { | ||||||
|  |       return row.admin; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = UserRepository; | module.exports = UserRepository; | ||||||
|   | |||||||
| @@ -55,6 +55,7 @@ router.post('/v1/user', require('./controllers/user/register.js')); | |||||||
| router.post('/v1/user/login', require('./controllers/user/login.js')); | router.post('/v1/user/login', require('./controllers/user/login.js')); | ||||||
| router.get('/v1/user/history', mustBeAuthenticated, require('./controllers/user/history.js')); | router.get('/v1/user/history', mustBeAuthenticated, require('./controllers/user/history.js')); | ||||||
| router.get('/v1/user/requests', mustBeAuthenticated, require('./controllers/user/requests.js')); | router.get('/v1/user/requests', mustBeAuthenticated, require('./controllers/user/requests.js')); | ||||||
|  | router.post('/v1/user/authenticate', mustBeAuthenticated, require('./controllers/user/authenticatePlexAccount.js')); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Seasoned |  * Seasoned | ||||||
|   | |||||||
| @@ -0,0 +1,73 @@ | |||||||
|  | const UserRepository = require('src/user/userRepository'); | ||||||
|  | const userRepository = new UserRepository(); | ||||||
|  |  const fetch = require('node-fetch'); | ||||||
|  |  const FormData = require('form-data'); | ||||||
|  |  | ||||||
|  | function handleError(error, res) { | ||||||
|  |   let { status, message, source } = error; | ||||||
|  |  | ||||||
|  |   if (status && message) { | ||||||
|  |     if (status === 401) { | ||||||
|  |       message = 'Unauthorized. Please check plex credentials.', | ||||||
|  |       source = 'plex' | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     res.status(status).send({ success: false, message, source }) | ||||||
|  |   } else { | ||||||
|  |     console.log('caught authenticate plex account controller error', error) | ||||||
|  |     res.status(500).send({ | ||||||
|  |       message: 'An unexpected error occured while authenticating your account with plex', | ||||||
|  |       source | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function handleResponse(response) { | ||||||
|  |   if (!response.ok) { | ||||||
|  |     throw { | ||||||
|  |       success: false, | ||||||
|  |       status: response.status, | ||||||
|  |       message: response.statusText | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return response.json() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function plexAuthenticate(username, password) { | ||||||
|  |   const url = 'https://plex.tv/api/v2/users/signin' | ||||||
|  |  | ||||||
|  |   const form = new FormData() | ||||||
|  |   form.append('login', username) | ||||||
|  |   form.append('password', password) | ||||||
|  |   form.append('rememberMe', 'false') | ||||||
|  |  | ||||||
|  |   const headers = { | ||||||
|  |     'Accept': 'application/json, text/plain, */*', | ||||||
|  |     'Content-Type': form.getHeaders()['content-type'], | ||||||
|  |     'X-Plex-Client-Identifier': 'seasonedRequest' | ||||||
|  |   } | ||||||
|  |   const options = { | ||||||
|  |     method: 'POST', | ||||||
|  |     headers, | ||||||
|  |     body: form | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return fetch(url, options) | ||||||
|  |     .then(resp => handleResponse(resp)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function authenticatePlexAccountController(req, res) { | ||||||
|  |   const user = req.loggedInUser; | ||||||
|  |   const { username, password } = req.body; | ||||||
|  |  | ||||||
|  |   return plexAuthenticate(username, password) | ||||||
|  |     .then(plexUser => userRepository.linkPlexUserId(user.username, plexUser.id)) | ||||||
|  |     .then(response => res.send({ | ||||||
|  |       success: true, | ||||||
|  |       message: "Successfully authenticated and linked plex account with seasoned request." | ||||||
|  |     })) | ||||||
|  |     .catch(error => handleError(error, res)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = authenticatePlexAccountController; | ||||||
		Reference in New Issue
	
	Block a user