Removed not that useful config options

This commit is contained in:
emeric
2026-07-14 23:51:40 +02:00
parent 340711daad
commit cb6863fe1e
11 changed files with 22 additions and 92 deletions
-2
View File
@@ -19,8 +19,6 @@ OpenSubsonic is an initiative to patch and extend the legacy Subsonic API. You'l
_LMS_ supports the [API Key Authentication](https://opensubsonic.netlify.app/docs/extensions/apikeyauth/) method. Each user has to generate their own API key on the settings page to use the Subsonic API. _LMS_ supports the [API Key Authentication](https://opensubsonic.netlify.app/docs/extensions/apikeyauth/) method. Each user has to generate their own API key on the settings page to use the Subsonic API.
If a client's login screen has no dedicated API key field, enter the API key as the password instead. If a client's login screen has no dedicated API key field, enter the API key as the password instead.
__Note__: the legacy Subsonic authentication methods can be disabled using the `lms.conf` file.
## Extra fields ## Extra fields
The following extra fields are implemented: The following extra fields are implemented:
* `Album` response: * `Album` response:
-2
View File
@@ -54,11 +54,9 @@
<div class="card mt-3"> <div class="card mt-3">
<div class="card-header"> <div class="card-header">
${tr:Lms.Settings.subsonic-token} ${tr:Lms.Settings.subsonic-token}
${<if-has-subsonic-token-usage>}
<button type="button" class="btn btn-sm p-0" data-bs-toggle="tooltip" data-bs-placement="right" title="${tr:Lms.Settings.subsonic-token-usage}"> <button type="button" class="btn btn-sm p-0" data-bs-toggle="tooltip" data-bs-placement="right" title="${tr:Lms.Settings.subsonic-token-usage}">
<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i> <i class="fa fa-fw fa-info-circle" aria-hidden="true"></i>
</button> </button>
${</if-has-subsonic-token-usage>}
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="input-group mb-3"> <div class="input-group mb-3">
-6
View File
@@ -84,12 +84,6 @@ login-throttler-max-entries = 10000;
# API # API
api-subsonic = true; api-subsonic = true;
# Enable or disable user/password authentication (params 'u' and 'p')
api-subsonic-support-password-auth = true;
# Enable or disable token/salt authentication (params 'u', 's' and 't')
api-subsonic-support-token-auth = true;
# List of clients for whom open subsonic extensions and extra fields are disabled # List of clients for whom open subsonic extensions and extra fields are disabled
api-open-subsonic-disabled-clients = ("DSub"); api-open-subsonic-disabled-clients = ("DSub");
+3 -9
View File
@@ -60,7 +60,7 @@ namespace lms::api::subsonic::utils
} }
} // namespace } // namespace
AuthenticationRequest parseAndValidateAuthenticationRequest(const Wt::Http::ParameterMap& parameters, const SubsonicResourceConfig& config) AuthenticationRequest parseAndValidateAuthenticationRequest(const Wt::Http::ParameterMap& parameters)
{ {
const std::optional<std::string> user{ getParameterAs<std::string>(parameters, "u") }; const std::optional<std::string> user{ getParameterAs<std::string>(parameters, "u") };
const std::optional<std::string> password{ getParameterAs<std::string>(parameters, "p") }; const std::optional<std::string> password{ getParameterAs<std::string>(parameters, "p") };
@@ -70,12 +70,6 @@ namespace lms::api::subsonic::utils
const bool passwordAuthRequested{ password.has_value() }; const bool passwordAuthRequested{ password.has_value() };
const bool tokenAuthRequested{ token.has_value() || salt.has_value() }; const bool tokenAuthRequested{ token.has_value() || salt.has_value() };
const bool passwordAuthAttempted{ passwordAuthRequested || (user.has_value() && !tokenAuthRequested) };
if (!config.supportPasswordAuthentication && passwordAuthAttempted)
throw ProvidedAuthenticationMechanismNotSupportedError{};
if (!config.supportTokenAuthentication && tokenAuthRequested)
throw ProvidedAuthenticationMechanismNotSupportedError{};
if (passwordAuthRequested && tokenAuthRequested) if (passwordAuthRequested && tokenAuthRequested)
throw MultipleConflictingAuthenticationMechanismsProvidedError{}; throw MultipleConflictingAuthenticationMechanismsProvidedError{};
@@ -130,9 +124,9 @@ namespace lms::api::subsonic::utils
throw UserNotAuthorizedError{}; throw UserNotAuthorizedError{};
} }
db::UserId authenticateUser(const Wt::Http::Request& request, db::Session& session, const SubsonicResourceConfig& config) db::UserId authenticateUser(const Wt::Http::Request& request, db::Session& session)
{ {
const AuthenticationRequest authRequest{ parseAndValidateAuthenticationRequest(request.getParameterMap(), config) }; const AuthenticationRequest authRequest{ parseAndValidateAuthenticationRequest(request.getParameterMap()) };
const auto clientAddress{ boost::asio::ip::make_address(request.clientAddress()) }; const auto clientAddress{ boost::asio::ip::make_address(request.clientAddress()) };
auto& authTokenService{ *core::Service<auth::IAuthTokenService>::get() }; auto& authTokenService{ *core::Service<auth::IAuthTokenService>::get() };
+2 -4
View File
@@ -28,8 +28,6 @@
#include "database/objects/User.hpp" #include "database/objects/User.hpp"
#include "database/objects/UserId.hpp" #include "database/objects/UserId.hpp"
#include "SubsonicResourceConfig.hpp"
namespace lms::db namespace lms::db
{ {
class Session; class Session;
@@ -58,7 +56,7 @@ namespace lms::api::subsonic::utils
using AuthenticationRequest = std::variant<PasswordAuthentication, TokenAuthentication, ApiKeyAuthentication>; using AuthenticationRequest = std::variant<PasswordAuthentication, TokenAuthentication, ApiKeyAuthentication>;
// Throws on error // Throws on error
AuthenticationRequest parseAndValidateAuthenticationRequest(const Wt::Http::ParameterMap& parameters, const SubsonicResourceConfig& config); AuthenticationRequest parseAndValidateAuthenticationRequest(const Wt::Http::ParameterMap& parameters);
// Checks token == hex(md5(apiKey + salt)), case insensitive // Checks token == hex(md5(apiKey + salt)), case insensitive
bool checkAuthToken(std::string_view apiKey, std::string_view salt, std::string_view token); bool checkAuthToken(std::string_view apiKey, std::string_view salt, std::string_view token);
@@ -67,5 +65,5 @@ namespace lms::api::subsonic::utils
db::User::pointer getUserFromUserId(db::Session& session, db::UserId userId); db::User::pointer getUserFromUserId(db::Session& session, db::UserId userId);
// Throws on error // Throws on error
db::UserId authenticateUser(const Wt::Http::Request& request, db::Session& session, const SubsonicResourceConfig& config); db::UserId authenticateUser(const Wt::Http::Request& request, db::Session& session);
} // namespace lms::api::subsonic::utils } // namespace lms::api::subsonic::utils
+1 -1
View File
@@ -415,6 +415,6 @@ namespace lms::api::subsonic
db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request) db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request)
{ {
return utils::authenticateUser(request, _db.getTLSSession(), _config); return utils::authenticateUser(request, _db.getTLSSession());
} }
} // namespace lms::api::subsonic } // namespace lms::api::subsonic
@@ -43,8 +43,6 @@ namespace lms::api::subsonic
{ {
return SubsonicResourceConfig{ return SubsonicResourceConfig{
.openSubsonicDisabledClients = readOpenSubsonicDisabledClients(config), .openSubsonicDisabledClients = readOpenSubsonicDisabledClients(config),
.supportPasswordAuthentication = config.getBool("api-subsonic-support-password-auth", true),
.supportTokenAuthentication = config.getBool("api-subsonic-support-token-auth", true)
}; };
} }
} // namespace lms::api::subsonic } // namespace lms::api::subsonic
@@ -32,8 +32,6 @@ namespace lms::api::subsonic
struct SubsonicResourceConfig struct SubsonicResourceConfig
{ {
std::unordered_set<std::string> openSubsonicDisabledClients; std::unordered_set<std::string> openSubsonicDisabledClients;
bool supportPasswordAuthentication;
bool supportTokenAuthentication;
}; };
SubsonicResourceConfig readSubsonicResourceConfig(core::IConfig& _config); SubsonicResourceConfig readSubsonicResourceConfig(core::IConfig& _config);
@@ -47,7 +47,6 @@ namespace lms::api::subsonic
ServerMustUpgrade = 30, ServerMustUpgrade = 30,
WrongUsernameOrPassword = 40, WrongUsernameOrPassword = 40,
TokenAuthenticationNotSupportedForLDAPUsers = 41, TokenAuthenticationNotSupportedForLDAPUsers = 41,
ProvidedAuthenticationMechanismNotSupported = 42,
MultipleConflictingAuthenticationMechanismsProvided = 43, MultipleConflictingAuthenticationMechanismsProvided = 43,
InvalidAPIkey = 44, InvalidAPIkey = 44,
UserNotAuthorized = 50, UserNotAuthorized = 50,
@@ -131,19 +130,6 @@ namespace lms::api::subsonic
std::string getMessage() const override { return "Token authentication not supported for LDAP users."; } std::string getMessage() const override { return "Token authentication not supported for LDAP users."; }
}; };
class ProvidedAuthenticationMechanismNotSupportedError : public Error
{
public:
ProvidedAuthenticationMechanismNotSupportedError()
: Error{ Code::ProvidedAuthenticationMechanismNotSupported } {}
private:
std::string getMessage() const override
{
return "Provided authentication mechanism not supported.";
}
};
class MultipleConflictingAuthenticationMechanismsProvidedError : public Error class MultipleConflictingAuthenticationMechanismsProvidedError : public Error
{ {
public: public:
+16 -44
View File
@@ -24,27 +24,6 @@
namespace lms::api::subsonic::utils::tests namespace lms::api::subsonic::utils::tests
{ {
namespace
{
const SubsonicResourceConfig bothMechanismsSupported{
.openSubsonicDisabledClients = {},
.supportPasswordAuthentication = true,
.supportTokenAuthentication = true,
};
const SubsonicResourceConfig noneSupported{
.openSubsonicDisabledClients = {},
.supportPasswordAuthentication = false,
.supportTokenAuthentication = false,
};
const SubsonicResourceConfig passwordUnsupported{
.openSubsonicDisabledClients = {},
.supportPasswordAuthentication = false,
.supportTokenAuthentication = true,
};
} // namespace
// Reference values from the Subsonic API documentation (apiKey = "sesame", salt = "c19b2d") // Reference values from the Subsonic API documentation (apiKey = "sesame", salt = "c19b2d")
TEST(AuthUtils, checkAuthToken_ValidToken) TEST(AuthUtils, checkAuthToken_ValidToken)
{ {
@@ -73,66 +52,59 @@ namespace lms::api::subsonic::utils::tests
TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyOnly) TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyOnly)
{ {
const auto request{ parseAndValidateAuthenticationRequest({ { "apiKey", { "apiKey" } } }, bothMechanismsSupported) }; const auto request{ parseAndValidateAuthenticationRequest({ { "apiKey", { "apiKey" } } }) };
ASSERT_TRUE(std::holds_alternative<ApiKeyAuthentication>(request)); ASSERT_TRUE(std::holds_alternative<ApiKeyAuthentication>(request));
EXPECT_EQ(std::get<ApiKeyAuthentication>(request).apiKey, "apiKey"); EXPECT_EQ(std::get<ApiKeyAuthentication>(request).apiKey, "apiKey");
EXPECT_THROW(parseAndValidateAuthenticationRequest({}, bothMechanismsSupported), RequiredParameterMissingError); EXPECT_THROW(parseAndValidateAuthenticationRequest({}), RequiredParameterMissingError);
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_password) TEST(AuthUtils, parseAndValidateAuthenticationRequest_password)
{ {
const auto request{ parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } } }, bothMechanismsSupported) }; const auto request{ parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } } }) };
ASSERT_TRUE(std::holds_alternative<PasswordAuthentication>(request)); ASSERT_TRUE(std::holds_alternative<PasswordAuthentication>(request));
EXPECT_EQ(std::get<PasswordAuthentication>(request).user, "user"); EXPECT_EQ(std::get<PasswordAuthentication>(request).user, "user");
EXPECT_EQ(std::get<PasswordAuthentication>(request).password, "password"); EXPECT_EQ(std::get<PasswordAuthentication>(request).password, "password");
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "p", { "password" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing u EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "p", { "password" } } }), RequiredParameterMissingError); // missing u
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing p EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } } }), RequiredParameterMissingError); // missing p
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_token) TEST(AuthUtils, parseAndValidateAuthenticationRequest_token)
{ {
const auto request{ parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } }, { "s", { "salt" } } }, bothMechanismsSupported) }; const auto request{ parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } }, { "s", { "salt" } } }) };
ASSERT_TRUE(std::holds_alternative<TokenAuthentication>(request)); ASSERT_TRUE(std::holds_alternative<TokenAuthentication>(request));
EXPECT_EQ(std::get<TokenAuthentication>(request).user, "user"); EXPECT_EQ(std::get<TokenAuthentication>(request).user, "user");
EXPECT_EQ(std::get<TokenAuthentication>(request).token, "token"); EXPECT_EQ(std::get<TokenAuthentication>(request).token, "token");
EXPECT_EQ(std::get<TokenAuthentication>(request).salt, "salt"); EXPECT_EQ(std::get<TokenAuthentication>(request).salt, "salt");
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "t", { "token" } }, { "s", { "salt" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing u EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "t", { "token" } }, { "s", { "salt" } } }), RequiredParameterMissingError); // missing u
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "s", { "salt" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing t EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "s", { "salt" } } }), RequiredParameterMissingError); // missing t
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing s EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } } }), RequiredParameterMissingError); // missing s
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_conflicts) TEST(AuthUtils, parseAndValidateAuthenticationRequest_conflicts)
{ {
// password + token // password + token
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } }, { "t", { "token" } }, { "s", { "salt" } } }, bothMechanismsSupported), MultipleConflictingAuthenticationMechanismsProvidedError); EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } }, { "t", { "token" } }, { "s", { "salt" } } }), MultipleConflictingAuthenticationMechanismsProvidedError);
// apiKey + password // apiKey + password
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } }, { "apiKey", { "apiKey" } } }, bothMechanismsSupported), MultipleConflictingAuthenticationMechanismsProvidedError); EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } }, { "apiKey", { "apiKey" } } }), MultipleConflictingAuthenticationMechanismsProvidedError);
// apiKey + token // apiKey + token
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } }, { "s", { "salt" } }, { "apiKey", { "apiKey" } } }, bothMechanismsSupported), MultipleConflictingAuthenticationMechanismsProvidedError); EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } }, { "s", { "salt" } }, { "apiKey", { "apiKey" } } }), MultipleConflictingAuthenticationMechanismsProvidedError);
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_mechanismNotSupported) TEST(AuthUtils, parseAndValidateAuthenticationRequest_usernameOnlyRequiresPassword)
{ {
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "p", { "password" } } }, noneSupported), ProvidedAuthenticationMechanismNotSupportedError); EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } } }), RequiredParameterMissingError); // missing p
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } }, { "t", { "token" } }, { "s", { "salt" } } }, noneSupported), ProvidedAuthenticationMechanismNotSupportedError);
EXPECT_NO_THROW(parseAndValidateAuthenticationRequest({ { "apiKey", { "apiKey" } } }, noneSupported)); // apiKey is unaffected by these two flags
}
TEST(AuthUtils, parseAndValidateAuthenticationRequest_usernameOnlyTreatedAsPasswordAttempt)
{
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "u", { "user" } } }, passwordUnsupported), ProvidedAuthenticationMechanismNotSupportedError);
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyWithPartialPassword) TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyWithPartialPassword)
{ {
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "p", { "password" } }, { "apiKey", { "apiKey" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing u EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "p", { "password" } }, { "apiKey", { "apiKey" } } }), RequiredParameterMissingError); // missing u
} }
TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyWithPartialToken) TEST(AuthUtils, parseAndValidateAuthenticationRequest_apiKeyWithPartialToken)
{ {
EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "s", { "salt" } }, { "apiKey", { "apiKey" } } }, bothMechanismsSupported), RequiredParameterMissingError); // missing u EXPECT_THROW(parseAndValidateAuthenticationRequest({ { "s", { "salt" } }, { "apiKey", { "apiKey" } } }), RequiredParameterMissingError); // missing u
} }
} // namespace lms::api::subsonic::utils::tests } // namespace lms::api::subsonic::utils::tests
@@ -27,7 +27,6 @@
#include <Wt/WString.h> #include <Wt/WString.h>
#include <Wt/WTemplateFormView.h> #include <Wt/WTemplateFormView.h>
#include "core/IConfig.hpp"
#include "core/Service.hpp" #include "core/Service.hpp"
#include "core/UUID.hpp" #include "core/UUID.hpp"
@@ -185,11 +184,6 @@ namespace lms::ui
auto* t{ addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Settings.subsonic.template.key")) }; auto* t{ addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Settings.subsonic.template.key")) };
t->addFunction("tr", &Wt::WTemplate::Functions::tr); t->addFunction("tr", &Wt::WTemplate::Functions::tr);
{
core::IConfig& config{ *core::Service<core::IConfig>::get() };
t->setCondition("if-has-subsonic-token-usage", config.getBool("api-subsonic-support-password-auth", true) || config.getBool("api-subsonic-support-token-auth", true));
}
std::string currentToken; std::string currentToken;
{ {
auto transaction{ LmsApp->getDbSession().createReadTransaction() }; auto transaction{ LmsApp->getDbSession().createReadTransaction() };