Moved contents of seasoned_api up to root folder

This commit is contained in:
2022-08-19 01:03:27 +02:00
parent 0efc109992
commit 56262a45c8
134 changed files with 885 additions and 32 deletions

41
src/user/token.js Normal file
View File

@@ -0,0 +1,41 @@
const User = require("./user");
const jwt = require("jsonwebtoken");
class Token {
constructor(user, admin = false, settings = null) {
this.user = user;
this.admin = admin;
this.settings = settings;
}
/**
* Generate a new token.
* @param {String} secret a cipher of the token
* @returns {String}
*/
toString(secret) {
const { user, admin, settings } = this;
let data = { username: user.username, settings };
if (admin) data["admin"] = admin;
return jwt.sign(data, secret, { expiresIn: "90d" });
}
/**
* Decode a token.
* @param {Token} jwtToken an encrypted token
* @param {String} secret a cipher of the token
* @returns {Token}
*/
static fromString(jwtToken, secret) {
const token = jwt.verify(jwtToken, secret, { clockTolerance: 10000 });
if (token.username == null) throw new Error("Malformed token");
const { username, admin, settings } = token;
const user = new User(username);
return new Token(user, admin, settings);
}
}
module.exports = Token;

8
src/user/user.js Normal file
View File

@@ -0,0 +1,8 @@
class User {
constructor(username, email=undefined) {
this.username = username;
this.email = email;
}
}
module.exports = User;

256
src/user/userRepository.js Normal file
View File

@@ -0,0 +1,256 @@
const assert = require("assert");
const establishedDatabase = require("../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 = ?",
link: "update settings set plex_userid = ? where user_name = ?",
unlink: "update settings set plex_userid = null where user_name = ?",
createSettings: "insert into settings (user_name) values (?)",
updateSettings:
"update settings set user_name = ?, dark_mode = ?, emoji = ?",
getSettings: "select * from settings where user_name = ?"
};
}
/**
* 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 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 this.database.run(this.queries.change, [password, user.username]);
}
/**
* Link plex userid with seasoned user
* @param {String} username 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.error("db error", error);
reject({
status: 500,
message:
"An unexpected error occured while linking plex and seasoned accounts",
source: "seasoned database"
});
});
});
}
/**
* Unlink plex userid with seasoned user
* @param {User} user the user you want to lunk plex userid with
* @returns {Promsie}
*/
unlinkPlexUserId(username) {
return new Promise((resolve, reject) => {
this.database
.run(this.queries.unlink, 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 unlinking plex and seasoned accounts",
source: "seasoned database"
});
});
});
}
/**
* Check if the user has boolean flag set for admin in database
* @param {User} user object
* @returns {Promsie}
*/
checkAdmin(user) {
return this.database
.get(this.queries.getAdminStateByUser, user.username)
.then(row => row.admin);
}
/**
* Get settings for user matching string username
* @param {String} username
* @returns {Promsie}
*/
getSettings(username) {
return new Promise((resolve, reject) => {
this.database
.get(this.queries.getSettings, username)
.then(async row => {
if (row == null) {
console.debug(
`settings do not exist for user: ${username}. Creating settings entry.`
);
const userExistsWithUsername = await this.database.get(
"select * from user where user_name is ?",
username
);
if (userExistsWithUsername !== undefined) {
try {
resolve(this.dbCreateSettings(username));
} catch (error) {
reject(error);
}
} else {
reject({
status: 404,
message: "User not found, no settings to get"
});
}
}
resolve(row);
})
.catch(error => {
console.error(
"Unexpected error occured while fetching settings for your account. Error:",
error
);
reject({
status: 500,
message:
"An unexpected error occured while fetching settings for your account",
source: "seasoned database"
});
});
});
}
/**
* Update settings values for user matching string username
* @param {String} username
* @param {String} dark_mode
* @param {String} emoji
* @returns {Promsie}
*/
updateSettings(username, dark_mode = undefined, emoji = undefined) {
const settings = this.getSettings(username);
dark_mode = dark_mode !== undefined ? dark_mode : settings.dark_mode;
emoji = emoji !== undefined ? emoji : settings.emoji;
return this.dbUpdateSettings(username, dark_mode, emoji).catch(error => {
if (error.status && error.message) {
return error;
}
return {
status: 500,
message:
"An unexpected error occured while updating settings for your account"
};
});
}
/**
* Helper function for creating settings in the database
* @param {String} username
* @returns {Promsie}
*/
dbCreateSettings(username) {
return this.database
.run(this.queries.createSettings, username)
.then(() => this.database.get(this.queries.getSettings, username))
.catch(error =>
rejectUnexpectedDatabaseError(
"Unexpected error occured while creating settings",
503,
error
)
);
}
/**
* Helper function for updating settings in the database
* @param {String} username
* @returns {Promsie}
*/
dbUpdateSettings(username, dark_mode, emoji) {
return new Promise((resolve, reject) =>
this.database
.run(this.queries.updateSettings, [username, dark_mode, emoji])
.then(row => resolve(row))
);
}
}
const rejectUnexpectedDatabaseError = (
message,
status,
error,
reject = null
) => {
console.error(error);
const body = {
status,
message,
source: "seasoned database"
};
if (reject == null) {
return new Promise((resolve, reject) => reject(body));
}
reject(body);
};
module.exports = UserRepository;

75
src/user/userSecurity.js Normal file
View File

@@ -0,0 +1,75 @@
const bcrypt = require("bcrypt");
const UserRepository = require("./userRepository");
class UserSecurity {
constructor(database) {
this.userRepository = new UserRepository(database);
}
/**
* Create a new user in PlanFlix.
* @param {User} user the new user you want to create
* @param {String} clearPassword a password of the user
* @returns {Promise}
*/
createNewUser(user, clearPassword) {
if (user.username.trim() === "") {
throw new Error("The username is empty.");
} else if (clearPassword.trim() === "") {
throw new Error("The password is empty.");
} else {
return this.userRepository
.create(user)
.then(() => UserSecurity.hashPassword(clearPassword))
.then(hash => this.userRepository.changePassword(user, hash));
}
}
/**
* Login into PlanFlix.
* @param {User} user the user you want to login
* @param {String} clearPassword the user's password
* @returns {Promise}
*/
login(user, clearPassword) {
return this.userRepository
.retrieveHash(user)
.then(hash => UserSecurity.compareHashes(hash, clearPassword))
.catch(() => {
throw new Error("Incorrect username or password.");
});
}
/**
* Compare between a password and a hash password from database.
* @param {String} hash the hash password from database
* @param {String} clearPassword the user's password
* @returns {Promise}
*/
static compareHashes(hash, clearPassword) {
return new Promise((resolve, reject) => {
bcrypt.compare(clearPassword, hash, (error, match) => {
if (match) resolve(true);
reject(false);
});
});
}
/**
* Hashes a password.
* @param {String} clearPassword the user's password
* @returns {Promise}
*/
static hashPassword(clearPassword) {
return new Promise(resolve => {
const saltRounds = 10;
bcrypt.hash(clearPassword, saltRounds, (error, hash) => {
if (error) reject(error);
resolve(hash);
});
});
}
}
module.exports = UserSecurity;