SQLite
parent
1011152fbe
commit
ef671700e1
|
@ -12,8 +12,13 @@ cmake_policy(SET CMP0057 NEW)
|
|||
find_package(Crow)
|
||||
find_package(CURL)
|
||||
find_package(OpenSSL)
|
||||
find_package(SQLite3)
|
||||
|
||||
message("SSL ${OPENSSL_INCLUDE_DIR}")
|
||||
if (NOT SQLite3_FOUND)
|
||||
message("Failed to find SQLite3")
|
||||
endif ()
|
||||
message("SQLite ${SQLite3_INCLUDE_DIRS} ${SQLite3_LIBRARIES}")
|
||||
|
||||
if (NOT CURL_FOUND)
|
||||
message("libcurl is required!")
|
||||
|
@ -24,6 +29,8 @@ endif ()
|
|||
add_subdirectory(libs/BLT)
|
||||
include_directories(include/)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
include_directories(${SQLite3_INCLUDE_DIRS})
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
file(GLOB_RECURSE source_files src/*.cpp)
|
||||
|
||||
|
@ -33,6 +40,7 @@ target_link_libraries(crowsite BLT)
|
|||
target_link_libraries(crowsite Crow::Crow)
|
||||
target_link_libraries(crowsite ${CURL_LIBRARIES})
|
||||
target_link_libraries(crowsite OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(crowsite SQLite::SQLite3)
|
||||
target_compile_options(crowsite PRIVATE -Wall -Wextra -Wpedantic)
|
||||
|
||||
if (${ENABLE_ADDRSAN} MATCHES ON)
|
||||
|
|
Binary file not shown.
|
@ -1 +1,2 @@
|
|||
jlbpNSZuchBeVInZ 1692336738
|
||||
wDTvp2olKnTzXs0q 1692378069
|
||||
vUJR5OaiqtXupR8v 1692378591
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"clientID":"2e68464b-a37c-58a2-a970-97fedf56e8f9","clientToken":"00000000-0000-0000-0000-000000000000"}
|
|
@ -0,0 +1 @@
|
|||
{"clientID":"50a21c33-66c4-5a0f-902f-9434632025e6","clientToken":"yfuMydsUxrYprB6ykuXBcJe3SDuu17W7OrZns1nweWBUnSUUdsHszJN/YAKTVYsPjsEVd8rGCpUly5VsYfx6FA=="}
|
|
@ -0,0 +1 @@
|
|||
{"clientID":"50a21c33-66c4-5a0f-902f-9434632025e6","clientToken":"6Ft+YVGtURGwMwi9yTemzakVoVpwkE3iRzshpUn/u58X6BWECdBZvE6nDCg4v628MLqHLwui59GIVyxc9HN0ww=="}
|
|
@ -21,7 +21,6 @@ namespace cs::jellyfin
|
|||
std::string name;
|
||||
std::string serverId;
|
||||
std::string Id;
|
||||
std::string primaryImageTag;
|
||||
bool hasPassword;
|
||||
bool hasConfiguredPassword;
|
||||
bool hasConfiguredEasyPassword;
|
||||
|
@ -29,12 +28,13 @@ namespace cs::jellyfin
|
|||
std::string lastLoginDate;
|
||||
std::string lastActivityDate;
|
||||
} user;
|
||||
std::string accessToken;
|
||||
bool isAdmin{};
|
||||
};
|
||||
|
||||
void setToken(std::string_view token);
|
||||
|
||||
void processUserData();
|
||||
const client_data& getUserData(const std::string& username);
|
||||
|
||||
std::string generateAuthHeader();
|
||||
std::string getUserData();
|
||||
|
|
|
@ -10,12 +10,64 @@
|
|||
|
||||
namespace cs {
|
||||
|
||||
constexpr uint32_t PERM_ADMIN = 0x1;
|
||||
constexpr uint32_t PERM_READ_FILES = 0x2;
|
||||
constexpr uint32_t PERM_WRITE_FILES = 0x4;
|
||||
constexpr uint32_t PERM_CREATE_POSTS = 0x8;
|
||||
constexpr uint32_t PERM_CREATE_COMMENTS = 0x10;
|
||||
constexpr uint32_t PERM_CREATE_SHARES = 0x20;
|
||||
constexpr uint32_t PERM_EDIT_POSTS = 0x40;
|
||||
constexpr uint32_t PERM_EDIT_COMMENTS = 0x80;
|
||||
|
||||
constexpr uint32_t PERM_DEFAULT = PERM_READ_FILES | PERM_CREATE_COMMENTS;
|
||||
|
||||
namespace auth {
|
||||
void init();
|
||||
void cleanup();
|
||||
}
|
||||
|
||||
struct cookie_data {
|
||||
std::string clientID;
|
||||
std::string clientToken;
|
||||
};
|
||||
|
||||
bool handleLoginPost(cs::parser::Post& postData, cookie_data& cookieOut);
|
||||
/**
|
||||
* An interface function which is used to validate login information provided as post data. Is is up to the caller
|
||||
* to inform the user of the clientID and clientToken, along with the auth system of these values.
|
||||
* Which is to say that this function is purely for auth.
|
||||
*
|
||||
* @param postData post data container class. This function checks for the existence of "username" and "password" in postData
|
||||
* @related createUserAuthTokens(...)
|
||||
* @related storeUserData(...)
|
||||
* @return true if user is valid and authorized, false otherwise (including if "username" || "password" does not exist).
|
||||
*/
|
||||
bool checkUserAuthorization(cs::parser::Post& postData);
|
||||
|
||||
/**
|
||||
* Generates a clientID (UUIDv5 based on user-agent and username) along with a unique high security (512 bit) base64 encoded token string
|
||||
* @param postData post data including a "username"
|
||||
* @param useragent user agent of the requesting client
|
||||
* @return cookie_data structure containing clientId and clientToken
|
||||
*/
|
||||
cookie_data createUserAuthTokens(cs::parser::Post& postData, const std::string& useragent);
|
||||
|
||||
/**
|
||||
* Informs the internal auth database of a successfully login attempt, updating the internal storage of the clientID -> Token.
|
||||
* Username is passed directly as this function should only be called after checkUserAuthorization(...) returns true.
|
||||
* @param username username of the user.
|
||||
* @param useragent user-agent of the user
|
||||
* @param tokens generated client tokens
|
||||
* @return false if something failed (error will be logged!)
|
||||
* @related createUserAuthTokens(...)
|
||||
* @related checkUserAuthorization(...)
|
||||
*/
|
||||
bool storeUserData(const std::string& username, const std::string& useragent, const cookie_data& tokens);
|
||||
|
||||
bool isUserLoggedIn(const std::string& clientID, const std::string& token);
|
||||
|
||||
std::string getUserFromID(const std::string& clientID);
|
||||
bool isUserAdmin(const std::string& username);
|
||||
uint32_t getUserPermissions(const std::string& username);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// Created by brett on 17/08/23.
|
||||
//
|
||||
|
||||
#ifndef CROWSITE_SQL_HELPER_H
|
||||
#define CROWSITE_SQL_HELPER_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace cs::sql
|
||||
{
|
||||
|
||||
static int prepareStatement(sqlite3* db, const std::string& sqlStatement, sqlite3_stmt** ppStmt)
|
||||
{
|
||||
return sqlite3_prepare_v2(db, sqlStatement.c_str(), static_cast<int>(sqlStatement.size()) + 1, ppStmt, nullptr);
|
||||
}
|
||||
|
||||
class statement
|
||||
{
|
||||
private:
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
sqlite3* db;
|
||||
int err;
|
||||
public:
|
||||
statement(sqlite3* db, const std::string& statement): db(db)
|
||||
{
|
||||
err = prepareStatement(db, statement, &stmt);
|
||||
if (err)
|
||||
BLT_ERROR("Failed to execute statement, error code %d, error: %s.", err, sqlite3_errstr(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the last statement failed. Should be checked after construction!
|
||||
*/
|
||||
[[nodiscard]] bool fail() const
|
||||
{
|
||||
return !(err == SQLITE_OK || err == SQLITE_DONE || err == SQLITE_ROW);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasRow() const
|
||||
{ return err == SQLITE_ROW; }
|
||||
|
||||
[[nodiscard]] bool complete() const
|
||||
{ return err == SQLITE_DONE; }
|
||||
|
||||
[[nodiscard]] int error() const
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
bool execute()
|
||||
{
|
||||
if (err != SQLITE_ROW)
|
||||
sqlite3_reset(stmt);
|
||||
err = sqlite3_step(stmt);
|
||||
return !fail();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
statement* set(const T& t, int column)
|
||||
{
|
||||
// make api consistent
|
||||
column = column - 1;
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
err = sqlite3_bind_double(stmt, column, t);
|
||||
} else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
|
||||
err = sqlite3_bind_int64(stmt, column, t);
|
||||
else
|
||||
err = sqlite3_bind_int(stmt, column, t);
|
||||
} else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
err = sqlite3_bind_text(stmt, column, t.c_str(), -1, nullptr);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get(int column)
|
||||
{
|
||||
if (err != SQLITE_ROW)
|
||||
throw std::runtime_error("Unable to get data as statement didn't return a row!");
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
return sqlite3_column_double(stmt, column);
|
||||
} else if constexpr (std::is_integral_v<T>)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
|
||||
return sqlite3_column_int64(stmt, column);
|
||||
else
|
||||
return sqlite3_column_int(stmt, column);
|
||||
} else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
return std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, column)));
|
||||
} else
|
||||
{
|
||||
return sqlite3_column_blob(stmt, column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* do not run execute before this function. ever.
|
||||
* This function will return a default initialized T if execute doesn't return a row.
|
||||
*/
|
||||
template<typename T>
|
||||
T executeAndGet(int column)
|
||||
{
|
||||
execute();
|
||||
if (err != SQLITE_ROW)
|
||||
return T{};
|
||||
return get<T>(column);
|
||||
}
|
||||
|
||||
~statement()
|
||||
{
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //CROWSITE_SQL_HELPER_H
|
2
libs/BLT
2
libs/BLT
|
@ -1 +1 @@
|
|||
Subproject commit 1e8f431f9eb06582083efde6489b59380f3e19ac
|
||||
Subproject commit 1d03938f950568dd1082abfd55f664ede6023995
|
|
@ -13,8 +13,7 @@ namespace cs::jellyfin
|
|||
struct
|
||||
{
|
||||
std::string token;
|
||||
HASHMAP<std::string, std::string> user_ids;
|
||||
HASHMAP<std::string, client_data> logged_in_users;
|
||||
HASHMAP<std::string, client_data> user_ids;
|
||||
} GLOBALS;
|
||||
|
||||
void setToken(std::string_view token)
|
||||
|
@ -24,16 +23,29 @@ namespace cs::jellyfin
|
|||
|
||||
void processUserData()
|
||||
{
|
||||
auto data = getUserData();
|
||||
auto usr_data = getUserData();
|
||||
|
||||
auto json = crow::json::load(data);
|
||||
auto json = crow::json::load(usr_data);
|
||||
|
||||
for (const auto& user : json)
|
||||
for (const auto& users : json)
|
||||
{
|
||||
auto username = std::string(user["Name"].s());
|
||||
auto userid = std::string(user["Id"].s());
|
||||
const auto& policy = users["Policy"];
|
||||
|
||||
client_data data;
|
||||
data.isAdmin = policy["IsAdministrator"].b();
|
||||
auto& user = data.user;
|
||||
user.name = users["Name"].s();
|
||||
user.serverId = users["ServerId"].s();
|
||||
user.Id = users["Id"].s();
|
||||
user.hasPassword = users["HasPassword"].b();
|
||||
user.hasConfiguredPassword = users["HasConfiguredPassword"].b();
|
||||
user.hasConfiguredEasyPassword = users["HasConfiguredEasyPassword"].b();
|
||||
user.enableAutoLogin = users["EnableAutoLogin"].b();
|
||||
user.lastLoginDate = users["LastLoginDate"].s();
|
||||
user.lastActivityDate = users["LastActivityDate"].s();
|
||||
|
||||
//BLT_TRACE("Processing %s = %s", username.operator std::string().c_str(), userid.operator std::string().c_str());
|
||||
GLOBALS.user_ids[username] = userid;
|
||||
GLOBALS.user_ids[user.name] = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,31 +93,14 @@ namespace cs::jellyfin
|
|||
auto response = cs::request::getResponseAndClear(l_url);
|
||||
|
||||
if (post.status() == 200)
|
||||
{
|
||||
crow::json::rvalue read = crow::json::load(response);
|
||||
|
||||
const auto& users = read["User"];
|
||||
|
||||
client_data data;
|
||||
data.accessToken = read["AccessToken"].s();
|
||||
auto& user = data.user;
|
||||
user.name = users["Name"].s();
|
||||
user.serverId = users["ServerId"].s();
|
||||
user.Id = users["Id"].s();
|
||||
user.primaryImageTag = users["PrimaryImageTag"].s();
|
||||
user.hasPassword = users["HasPassword"].b();
|
||||
user.hasConfiguredPassword = users["HasConfiguredPassword"].b();
|
||||
user.hasConfiguredEasyPassword = users["HasConfiguredEasyPassword"].b();
|
||||
user.enableAutoLogin = users["EnableAutoLogin"].b();
|
||||
user.lastLoginDate = users["LastLoginDate"].s();
|
||||
user.lastActivityDate = users["LastActivityDate"].s();
|
||||
|
||||
GLOBALS.logged_in_users[std::string(username)] = data;
|
||||
|
||||
return auth_response::AUTHORIZED;
|
||||
}
|
||||
|
||||
return auth_response::ERROR;
|
||||
}
|
||||
|
||||
const client_data& jellyfin::getUserData(const std::string& username)
|
||||
{
|
||||
return GLOBALS.user_ids[username];
|
||||
}
|
||||
|
||||
}
|
|
@ -2,15 +2,49 @@
|
|||
// Created by brett on 16/08/23.
|
||||
//
|
||||
#include <crowsite/site/auth.h>
|
||||
#include <crowsite/config.h>
|
||||
#include <crowsite/requests/jellyfin.h>
|
||||
#include "blt/std/logging.h"
|
||||
#include "blt/std/uuid.h"
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <crowsite/sql_helper.h>
|
||||
#include <random>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace blt;
|
||||
|
||||
namespace cs {
|
||||
namespace cs
|
||||
{
|
||||
sqlite3* user_database;
|
||||
|
||||
bool handleLoginPost(parser::Post& postData, cookie_data& cookieOut)
|
||||
// https://stackoverflow.com/questions/5288076/base64-encoding-and-decoding-with-openssl
|
||||
|
||||
char* base64(const unsigned char* input, int length)
|
||||
{
|
||||
const auto pl = 4 * ((length + 2) / 3);
|
||||
auto output = reinterpret_cast<char*>(calloc(pl + 1, 1)); //+1 for the terminating null that EVP_EncodeBlock adds on
|
||||
const auto ol = EVP_EncodeBlock(reinterpret_cast<unsigned char*>(output), input, length);
|
||||
if (pl != ol)
|
||||
{
|
||||
std::cerr << "Whoops, encode predicted " << pl << " but we got " << ol << "\n";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
unsigned char* decode64(const char* input, int length)
|
||||
{
|
||||
const auto pl = 3 * length / 4;
|
||||
auto output = reinterpret_cast<unsigned char*>(calloc(pl + 1, 1));
|
||||
const auto ol = EVP_DecodeBlock(output, reinterpret_cast<const unsigned char*>(input), length);
|
||||
if (pl != ol)
|
||||
{
|
||||
std::cerr << "Whoops, decode predicted " << pl << " but we got " << ol << "\n";
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
bool checkUserAuthorization(cs::parser::Post& postData)
|
||||
{
|
||||
// javascript should make sure we don't send post requests without information
|
||||
// this way it can be interactive
|
||||
|
@ -18,9 +52,163 @@ namespace cs {
|
|||
return false;
|
||||
auto auth = jellyfin::authenticateUser(postData["username"], postData["password"]);
|
||||
|
||||
cookieOut.clientID = uuid::toString(uuid::genV5("ClientID?"));
|
||||
cookieOut.clientToken = uuid::toString(uuid::genV4());
|
||||
|
||||
return auth == jellyfin::auth_response::AUTHORIZED;
|
||||
}
|
||||
|
||||
cookie_data createUserAuthTokens(parser::Post& postData, const std::string& useragent)
|
||||
{
|
||||
cookie_data cookieOut;
|
||||
cookieOut.clientID = uuid::toString(uuid::genV5(postData["username"] + "::" + useragent));
|
||||
|
||||
std::string token;
|
||||
|
||||
std::random_device rd;
|
||||
std::seed_seq seed{rd(), rd(), rd(), rd()};
|
||||
std::mt19937_64 gen(seed);
|
||||
std::uniform_int_distribution<int> charDist(0, 92);
|
||||
|
||||
for (int i = 0; i < SHA512_DIGEST_LENGTH * 8; i++)
|
||||
token += char(33 + charDist(gen));
|
||||
|
||||
unsigned char hash[SHA512_DIGEST_LENGTH + 1];
|
||||
hash[SHA512_DIGEST_LENGTH] = '\0';
|
||||
|
||||
SHA512(reinterpret_cast<const unsigned char*>(token.c_str()), token.size(), hash);
|
||||
|
||||
auto b64str = base64(hash, SHA512_DIGEST_LENGTH);
|
||||
|
||||
cookieOut.clientToken = std::string(reinterpret_cast<const char*>(b64str));
|
||||
|
||||
free(b64str);
|
||||
|
||||
return cookieOut;
|
||||
}
|
||||
|
||||
bool storeUserData(const std::string& username, const std::string& useragent, const cookie_data& tokens)
|
||||
{
|
||||
sql::statement insertStmt{
|
||||
user_database,
|
||||
"INSERT OR REPLACE INTO user_sessions (clientID, username, useragent, token) VALUES (?, ?, ?, ?);"
|
||||
};
|
||||
|
||||
if (insertStmt.fail())
|
||||
return false;
|
||||
|
||||
insertStmt.set(tokens.clientID, 0);
|
||||
insertStmt.set(username, 1);
|
||||
insertStmt.set(useragent, 2);
|
||||
insertStmt.set(tokens.clientToken, 3);
|
||||
|
||||
if (!insertStmt.execute())
|
||||
return false;
|
||||
|
||||
sql::statement insertAuth {
|
||||
user_database,
|
||||
"INSERT OR REPLACE INTO user_permissions (username, permission) VALUES (?, ?);"
|
||||
};
|
||||
if (insertAuth.fail())
|
||||
return false;
|
||||
insertStmt.set(username, 0);
|
||||
insertStmt.set(PERM_DEFAULT | (jellyfin::getUserData(username).isAdmin ? PERM_ADMIN : 0), 1);
|
||||
|
||||
if (!insertAuth.execute())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isUserLoggedIn(const std::string& clientID, const std::string& token)
|
||||
{
|
||||
sql::statement stmt {
|
||||
user_database,
|
||||
"SELECT username FROM user_sessions WHERE clientID='?' AND token='?';"
|
||||
};
|
||||
if (stmt.fail())
|
||||
return false;
|
||||
stmt.set(clientID, 0);
|
||||
stmt.set(token, 1);
|
||||
stmt.execute();
|
||||
return stmt.hasRow();
|
||||
}
|
||||
|
||||
bool isUserAdmin(const std::string& username)
|
||||
{
|
||||
return getUserPermissions(username) & PERM_ADMIN;
|
||||
}
|
||||
|
||||
std::string getUserFromID(const std::string& clientID)
|
||||
{
|
||||
sql::statement stmt {
|
||||
user_database,
|
||||
"SELECT username FROM user_sessions WHERE clientID='?';"
|
||||
};
|
||||
if (stmt.fail())
|
||||
return "";
|
||||
stmt.set(clientID, 0);
|
||||
return stmt.executeAndGet<std::string>(0);
|
||||
}
|
||||
|
||||
uint32_t getUserPermissions(const std::string& username)
|
||||
{
|
||||
sql::statement stmt {
|
||||
user_database,
|
||||
"SELECT permission FROM user_permissions WHERE username='?';"
|
||||
};
|
||||
if (stmt.fail())
|
||||
return 0;
|
||||
stmt.set(username, 0);
|
||||
return static_cast<uint32_t>(stmt.executeAndGet<int32_t>(0));
|
||||
}
|
||||
|
||||
void auth::init()
|
||||
{
|
||||
// TODO: proper multithreading
|
||||
auto path = (std::string(SITE_FILES_PATH) + "/data/db/");
|
||||
auto dbname = "users.sqlite";
|
||||
auto full_path = path + dbname;
|
||||
BLT_TRACE("Using %s for users database", full_path.c_str());
|
||||
std::filesystem::create_directories(path);
|
||||
if (int err = sqlite3_open_v2(full_path.c_str(), &user_database,
|
||||
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX,
|
||||
nullptr
|
||||
) != SQLITE_OK)
|
||||
{
|
||||
BLT_FATAL("Unable to create database connection! err %d msg %s", err, sqlite3_errstr(err));
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
sql::statement v {
|
||||
user_database,
|
||||
"SELECT SQLITE_VERSION()"
|
||||
};
|
||||
|
||||
if (v.fail())
|
||||
BLT_WARN("Failed to create statement with error code: %d msg: %s", v.error(), sqlite3_errstr(v.error()));
|
||||
|
||||
if (!v.execute())
|
||||
BLT_WARN("Failed to execute statement with error code: %d msg: %s", v.error(), sqlite3_errstr(v.error()));
|
||||
|
||||
BLT_INFO("SQLite Version: %s", v.get<std::string>(0).c_str());
|
||||
|
||||
sql::statement tableStmt{
|
||||
user_database,
|
||||
"CREATE TABLE IF NOT EXISTS user_sessions (clientID VARCHAR(36), username TEXT, useragent TEXT, token TEXT, PRIMARY KEY(clientID));"
|
||||
};
|
||||
|
||||
if (tableStmt.fail() || !tableStmt.execute())
|
||||
BLT_ERROR("Failed to execute user_sessions table creation! %d : %s", tableStmt.error(), sqlite3_errstr(tableStmt.error()));
|
||||
|
||||
sql::statement permsStmt{
|
||||
user_database,
|
||||
"CREATE TABLE IF NOT EXISTS user_permissions (username TEXT, permission INT, PRIMARY KEY(username));"
|
||||
};
|
||||
|
||||
if (permsStmt.fail() || !permsStmt.execute())
|
||||
BLT_ERROR("Failed to execute user_permissions table creation! %d : %s", permsStmt.error(), sqlite3_errstr(permsStmt.error()));
|
||||
}
|
||||
|
||||
void auth::cleanup()
|
||||
{
|
||||
sqlite3_close_v2(user_database);
|
||||
}
|
||||
}
|
27
src/main.cpp
27
src/main.cpp
|
@ -42,7 +42,8 @@ class BLT_CrowLogger : public crow::ILogHandler
|
|||
}
|
||||
};
|
||||
|
||||
inline crow::response redirect(const std::string& loc){
|
||||
inline crow::response redirect(const std::string& loc)
|
||||
{
|
||||
crow::response res;
|
||||
res.redirect(loc);
|
||||
return res;
|
||||
|
@ -63,6 +64,7 @@ int main(int argc, const char** argv)
|
|||
auto args = parser.parse_args(argc, argv);
|
||||
cs::jellyfin::setToken(blt::arg_parse::get<std::string>(args["token"]));
|
||||
cs::jellyfin::processUserData();
|
||||
cs::auth::init();
|
||||
|
||||
BLT_INFO("Starting site %s.", SITE_NAME);
|
||||
crow::mustache::set_global_base(SITE_FILES_PATH);
|
||||
|
@ -75,7 +77,7 @@ int main(int argc, const char** argv)
|
|||
const auto cookie_age = 180 * 24 * 60 * 60;
|
||||
|
||||
BLT_INFO("Init Crow with compression and logging enabled!");
|
||||
crow::App<crow::CookieParser, Session> app {Session{
|
||||
crow::App<crow::CookieParser, Session> app{Session{
|
||||
// customize cookies
|
||||
crow::CookieParser::Cookie("session").max_age(session_age).path("/"),
|
||||
// set session id length (small value only for demonstration purposes)
|
||||
|
@ -147,22 +149,28 @@ int main(int argc, const char** argv)
|
|||
|
||||
crow::response res(303);
|
||||
|
||||
cs::cookie_data data;
|
||||
std::string user_agent;
|
||||
|
||||
for (const auto& h : req.headers)
|
||||
{
|
||||
if (h.first == "User-Agent")
|
||||
user_agent = h.second;
|
||||
}
|
||||
|
||||
// either redirect to clear the form if failed or pass user to index
|
||||
if (cs::handleLoginPost(pp, data))
|
||||
if (cs::checkUserAuthorization(pp))
|
||||
{
|
||||
cs::cookie_data data = cs::createUserAuthTokens(pp, user_agent);
|
||||
cs::storeUserData(pp["username"], user_agent, data);
|
||||
|
||||
session.set("clientID", data.clientID);
|
||||
session.set("clientToken", data.clientToken);
|
||||
if (pp.hasKey("remember_me")){
|
||||
auto value = pp["remember_me"];
|
||||
auto& cookie_context = app.get_context<crow::CookieParser>(req);
|
||||
if (value[0] == 'T')
|
||||
if (pp.hasKey("remember_me") && pp["remember_me"][0] == 'T')
|
||||
{
|
||||
auto& cookie_context = app.get_context<crow::CookieParser>(req);
|
||||
cookie_context.set_cookie("clientID", data.clientID).path("/").max_age(cookie_age);
|
||||
cookie_context.set_cookie("clientToken", data.clientToken).path("/").max_age(cookie_age);
|
||||
}
|
||||
}
|
||||
res.set_header("Location", pp.hasKey("referer") ? pp["referer"] : "/");
|
||||
} else
|
||||
res.set_header("Location", "/login.html");
|
||||
|
@ -199,6 +207,7 @@ int main(int argc, const char** argv)
|
|||
app.port(8080).multithreaded().run();
|
||||
|
||||
cs::requests::cleanup();
|
||||
cs::auth::cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue