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:
2019-11-04 00:43:42 +01:00
parent f8cc19b510
commit 05b001de2e
4 changed files with 153 additions and 55 deletions

View File

@@ -1,8 +1,9 @@
CREATE TABLE IF NOT EXISTS user (
user_name varchar(127) UNIQUE,
password varchar(127),
email varchar(127) UNIQUE,
admin boolean DEFAULT 0,
email varchar(127) UNIQUE,
plex_userid varchar(127) DEFAULT NULL,
primary key (user_name)
);

View File

@@ -2,64 +2,87 @@ const assert = require('assert');
const establishedDatabase = require('src/database/database');
class UserRepository {
constructor(database) {
this.database = database || establishedDatabase;
this.queries = {
read: 'select * from user where lower(user_name) = lower(?)',
create: 'insert into user (user_name) values (?)',
change: 'update user set password = ? where user_name = ?',
retrieveHash: 'select * from user where user_name = ?',
getAdminStateByUser: 'select admin from user where user_name = ?'
};
}
constructor(database) {
this.database = database || establishedDatabase;
this.queries = {
read: 'select * from user where lower(user_name) = lower(?)',
create: 'insert into user (user_name) values (?)',
change: 'update user set password = ? where user_name = ?',
retrieveHash: 'select * 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.
* @param {User} user the user you want to create
* @returns {Promise}
*/
create(user) {
return Promise.resolve()
.then(() => this.database.get(this.queries.read, user.username))
.then(() => this.database.run(this.queries.create, user.username))
.catch((error) => {
if (error.name === 'AssertionError' || error.message.endsWith('user_name')) {
throw new Error('That username is already registered');
}
throw Error(error)
});
}
/**
* Create a user in a database.
* @param {User} user the user you want to create
* @returns {Promise}
*/
create(user) {
return this.database.get(this.queries.read, user.username)
.then(() => this.database.run(this.queries.create, user.username))
.catch((error) => {
if (error.name === 'AssertionError' || error.message.endsWith('user_name')) {
throw new Error('That username is already registered');
}
throw Error(error)
});
}
/**
* Retrieve a password from a database.
* @param {User} user the user you want to retrieve the password
* @returns {Promise}
*/
retrieveHash(user) {
return Promise.resolve()
.then(() => this.database.get(this.queries.retrieveHash, user.username))
.then((row) => {
assert(row, 'The user does not exist.');
return row.password;
})
.catch((err) => { console.log(error); throw new Error('Unable to find your user.'); });
}
/**
* Retrieve a password from a database.
* @param {User} user the user you want to retrieve the password
* @returns {Promise}
*/
retrieveHash(user) {
console.log('retrieving hash for user', user)
return this.database.get(this.queries.retrieveHash, user.username)
.then(row => {
assert(row, 'The user does not exist.');
return row.password;
})
.catch(err => { console.log(error); throw new Error('Unable to find your user.'); })
}
/**
* Change a user's password in a database.
* @param {User} user the user you want to create
* @param {String} password the new password you want to change
* @returns {Promise}
*/
changePassword(user, password) {
return Promise.resolve(this.database.run(this.queries.change, [password, user.username]));
}
/**
* Change a user's password in a database.
* @param {User} user the user you want to create
* @param {String} password the new password you want to change
* @returns {Promise}
*/
changePassword(user, password) {
return this.database.run(this.queries.change, [password, user.username])
}
checkAdmin(user) {
return this.database.get(this.queries.getAdminStateByUser, user.username).then((row) => {
return row.admin;
});
}
/**
* Link plex userid with seasoned user
* @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;

View File

@@ -55,6 +55,7 @@ router.post('/v1/user', require('./controllers/user/register.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/requests', mustBeAuthenticated, require('./controllers/user/requests.js'));
router.post('/v1/user/authenticate', mustBeAuthenticated, require('./controllers/user/authenticatePlexAccount.js'));
/**
* Seasoned

View File

@@ -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;