Added authentication backends: internal, pam and http-headers. fixes #119

This commit is contained in:
emeric
2021-03-04 19:32:09 +01:00
parent b94fe3e852
commit cc28d893f5
73 changed files with 2208 additions and 1254 deletions
+39 -32
View File
@@ -22,6 +22,7 @@
#include <map>
#include <mutex>
#include <thread>
#include <string_view>
#include "utils/Exception.hpp"
#include "utils/Logger.hpp"
@@ -40,17 +41,16 @@
namespace Database {
#define LMS_DATABASE_VERSION 28
using Version = std::size_t;
static constexpr Version LMS_DATABASE_VERSION {29};
using Version = std::size_t;
class VersionInfo
{
public:
using pointer = Wt::Dbo::ptr<VersionInfo>;
class VersionInfo
{
public:
using pointer = Wt::Dbo::ptr<VersionInfo>;
static VersionInfo::pointer getOrCreate(Session& session)
{
static VersionInfo::pointer getOrCreate(Session& session)
{
session.checkUniqueLocked();
pointer versionInfo {session.getDboSession().find<VersionInfo>()};
@@ -270,7 +270,7 @@ CREATE TABLE "user_backup" (
else if (version == 24)
{
// User's AuthMode
_session.execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultAuthMode)) + ")");
_session.execute("ALTER TABLE user ADD auth_mode INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultAuthMode*/0)) + ")");
}
else if (version == 25)
{
@@ -290,6 +290,31 @@ CREATE TABLE "user_backup" (
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(*this).modify()->incScanVersion();
}
else if (version == 28)
{
// Drop Auth mode
_session.execute(R"(
CREATE TABLE "user_backup" (
"id" integer primary key autoincrement,
"version" integer not null,
"type" integer not null,
"login_name" text not null,
"password_salt" text not null,
"password_hash" text not null,
"last_login" text,
"subsonic_transcode_enable" boolean not null,
"subsonic_transcode_format" integer not null,
"subsonic_transcode_bitrate" integer not null,
"subsonic_artist_list_mode" integer not null,
"ui_theme" integer not null,
"cur_playing_track_pos" integer not null,
"repeat_all" boolean not null,
"radio" boolean not null
))");
_session.execute("INSERT INTO user_backup SELECT id, version, type, login_name, password_salt, password_hash, last_login, subsonic_transcode_enable, subsonic_transcode_format, subsonic_transcode_bitrate, subsonic_artist_list_mode, ui_theme, cur_playing_track_pos, repeat_all, radio FROM user");
_session.execute("DROP TABLE user");
_session.execute("ALTER TABLE user_backup RENAME TO user");
}
else
{
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
@@ -329,46 +354,28 @@ enum class OwnedLock
Unique,
};
static thread_local std::map<std::shared_mutex*, OwnedLock> lockDebug;
UniqueTransaction::UniqueTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session)
UniqueTransaction::UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex},
_transaction {session}
{
assert(lockDebug[_lock.mutex()] == OwnedLock::None);
lockDebug[_lock.mutex()] = OwnedLock::Unique;
}
UniqueTransaction::~UniqueTransaction()
{
assert(lockDebug[_lock.mutex()] == OwnedLock::Unique);
lockDebug[_lock.mutex()] = OwnedLock::None;
}
SharedTransaction::SharedTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session)
SharedTransaction::SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
: _lock {mutex},
_transaction {session}
{
assert(lockDebug[_lock.mutex()] == OwnedLock::None);
lockDebug[_lock.mutex()] = OwnedLock::Shared;
}
SharedTransaction::~SharedTransaction()
{
assert(lockDebug[_lock.mutex()] == OwnedLock::Shared);
lockDebug[_lock.mutex()] = OwnedLock::None;
}
void
Session::checkUniqueLocked()
{
assert(lockDebug[&_db.getMutex()] == OwnedLock::Unique);
// assert(lockDebug[&_db.getMutex()] == OwnedLock::Unique);
}
void
Session::checkSharedLocked()
{
assert(lockDebug[&_db.getMutex()] != OwnedLock::None);
// assert(lockDebug[&_db.getMutex()] != OwnedLock::None);
}
UniqueTransaction
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace Wt::Dbo
{
template<>
struct sql_value_traits<std::string_view>
{
static void bind(std::string_view str, SqlStatement *statement, int column, int /* size */)
{
statement->bind(column, std::string {str});
}
};
}
+12 -3
View File
@@ -25,6 +25,7 @@
#include "database/Track.hpp"
#include "database/TrackList.hpp"
#include "utils/Logger.hpp"
#include "StringViewTraits.hpp"
namespace Database {
@@ -70,7 +71,7 @@ AuthToken::getByValue(Session& session, const std::string& value)
static const std::string playedListName {"__played_tracks__"};
static const std::string queuedListName {"__queued_tracks__"};
User::User(const std::string& loginName)
User::User(std::string_view loginName)
: _loginName {loginName}
{
}
@@ -93,8 +94,16 @@ User::getDemo(Session& session)
return res;
}
std::size_t
User::getCount(Session& session)
{
session.checkSharedLocked();
return session.getDboSession().query<int>("SELECT COUNT(*) FROM user");
}
User::pointer
User::create(Session& session, const std::string& loginName)
User::create(Session& session, std::string_view loginName)
{
session.checkUniqueLocked();
@@ -115,7 +124,7 @@ User::getById(Session& session, IdType id)
}
User::pointer
User::getByLoginName(Session& session, const std::string& name)
User::getByLoginName(Session& session, std::string_view name)
{
return session.getDboSession().find<User>()
.where("login_name = ?").bind(name);
+4 -3
View File
@@ -20,10 +20,11 @@
#pragma once
#include <filesystem>
#include <shared_mutex>
#include <Wt/Dbo/SqlConnectionPool.h>
#include "utils/RecursiveSharedMutex.hpp"
namespace Database {
class Session;
@@ -44,7 +45,7 @@ class Db
private:
friend class Session;
std::shared_mutex& getMutex() { return _sharedMutex; }
RecursiveSharedMutex& getMutex() { return _sharedMutex; }
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
class ScopedConnection
@@ -88,7 +89,7 @@ class Db
void executeSql(const std::string& sql);
std::shared_mutex _sharedMutex;
RecursiveSharedMutex _sharedMutex;
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
std::mutex _tlsSessionsMutex;
+41 -47
View File
@@ -19,74 +19,68 @@
#pragma once
#include <mutex>
#include <map>
#include <memory>
#include <shared_mutex>
#include <vector>
#include <mutex>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
namespace Database {
#include "utils/RecursiveSharedMutex.hpp"
class UniqueTransaction
namespace Database
{
public:
~UniqueTransaction();
private:
friend class Session;
UniqueTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session);
class UniqueTransaction
{
private:
friend class Session;
UniqueTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
std::unique_lock<std::shared_mutex> _lock;
Wt::Dbo::Transaction _transaction;
};
std::unique_lock<RecursiveSharedMutex> _lock;
Wt::Dbo::Transaction _transaction;
};
class SharedTransaction
{
public:
~SharedTransaction();
class SharedTransaction
{
private:
friend class Session;
SharedTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
private:
friend class Session;
SharedTransaction(std::shared_mutex& mutex, Wt::Dbo::Session& session);
std::shared_lock<RecursiveSharedMutex> _lock;
Wt::Dbo::Transaction _transaction;
};
std::shared_lock<std::shared_mutex> _lock;
Wt::Dbo::Transaction _transaction;
};
class Db;
class Session
{
public:
Session (Db& database);
class Db;
class Session
{
public:
Session (Db& database);
Session(const Session&) = delete;
Session(Session&&) = delete;
Session& operator=(const Session&) = delete;
Session& operator=(Session&&) = delete;
Session(const Session&) = delete;
Session(Session&&) = delete;
Session& operator=(const Session&) = delete;
Session& operator=(Session&&) = delete;
[[nodiscard]] UniqueTransaction createUniqueTransaction();
[[nodiscard]] SharedTransaction createSharedTransaction();
[[nodiscard]] UniqueTransaction createUniqueTransaction();
[[nodiscard]] SharedTransaction createSharedTransaction();
void checkUniqueLocked();
void checkSharedLocked();
void checkUniqueLocked();
void checkSharedLocked();
void optimize();
void optimize();
void prepareTables(); // need to run only once at startup
void prepareTables(); // need to run only once at startup
Wt::Dbo::Session& getDboSession() { return _session; }
Wt::Dbo::Session& getDboSession() { return _session; }
private:
Session(std::shared_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
private:
Session(std::shared_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
void doDatabaseMigrationIfNeeded();
void doDatabaseMigrationIfNeeded();
Db& _db;
Wt::Dbo::Session _session;
};
Db& _db;
Wt::Dbo::Session _session;
};
} // namespace Database
+6 -15
View File
@@ -20,6 +20,7 @@
#pragma once
#include <optional>
#include <string_view>
#include <vector>
#include <Wt/Dbo/Dbo.h>
@@ -100,12 +101,6 @@ class User : public Wt::Dbo::Dbo<User>
DEMO = 2,
};
enum class AuthMode
{
Internal = 0,
PAM = 1,
};
struct PasswordHash
{
std::string salt;
@@ -144,19 +139,19 @@ class User : public Wt::Dbo::Dbo<User>
static inline const Bitrate defaultSubsonicTranscodeBitrate {128000};
static inline const UITheme defaultUITheme {UITheme::Dark};
static inline const SubsonicArtistListMode defaultSubsonicArtistListMode {SubsonicArtistListMode::AllArtists};
static inline const AuthMode defaultAuthMode {AuthMode::Internal};
User() = default;
User(const std::string& loginName);
User(std::string_view loginName);
// utility
static pointer create(Session& session, const std::string& loginName);
static pointer create(Session& session, std::string_view loginName);
static pointer getById(Session& session, IdType id);
static pointer getByLoginName(Session& session, const std::string& loginName);
static pointer getByLoginName(Session& session, std::string_view loginName);
static std::vector<pointer> getAll(Session& session);
static pointer getDemo(Session& session);
static std::size_t getCount(Session& session);
// accessors
const std::string& getLoginName() const { return _loginName; }
@@ -173,7 +168,6 @@ class User : public Wt::Dbo::Dbo<User>
void setSubsonicTranscodeBitrate(Bitrate bitrate);
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
void setRadio(bool val) { _radio = val; }
void setAuthMode(AuthMode mode) { _authMode = mode;}
void setRepeatAll(bool val) { _repeatAll = val; }
void setUITheme(UITheme uiTheme) { _uiTheme = uiTheme; }
void clearAuthTokens();
@@ -188,7 +182,6 @@ class User : public Wt::Dbo::Dbo<User>
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; }
AuthMode getAuthMode() const { return _authMode; }
UITheme getUITheme() const { return _uiTheme; }
SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; }
@@ -225,7 +218,6 @@ class User : public Wt::Dbo::Dbo<User>
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
Wt::Dbo::field(a, _repeatAll, "repeat_all");
Wt::Dbo::field(a, _radio, "radio");
Wt::Dbo::field(a, _authMode, "auth_mode");
Wt::Dbo::hasMany(a, _tracklists, Wt::Dbo::ManyToOne, "user");
Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_artist_starred", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _starredReleases, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade);
@@ -248,13 +240,12 @@ class User : public Wt::Dbo::Dbo<User>
SubsonicArtistListMode _subsonicArtistListMode {defaultSubsonicArtistListMode};
bool _subsonicTranscodeEnable {defaultSubsonicTranscodeEnable};
AudioFormat _subsonicTranscodeFormat {defaultSubsonicTranscodeFormat};
int _subsonicTranscodeBitrate {defaultSubsonicTranscodeBitrate};
int _subsonicTranscodeBitrate {defaultSubsonicTranscodeBitrate};
// User's dynamic data (UI)
int _curPlayingTrackPos {}; // Current track position in queue
bool _repeatAll {};
bool _radio {};
AuthMode _authMode {defaultAuthMode};
Wt::Dbo::collection<Wt::Dbo::ptr<TrackList>> _tracklists;
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> _starredArtists;