Properly handled http headers for subsonic API. ref #119

This commit is contained in:
emeric
2021-03-05 13:23:47 +01:00
parent cc28d893f5
commit 1b49e8f6a6
13 changed files with 149 additions and 102 deletions
+96 -63
View File
@@ -26,6 +26,7 @@
#include <Wt/WLocalDateTime.h>
#include "auth/IPasswordService.hpp"
#include "auth/IEnvService.hpp"
#include "cover/ICoverArtGrabber.hpp"
#include "database/Artist.hpp"
#include "database/Cluster.hpp"
@@ -209,14 +210,33 @@ static
void
checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username)
{
if (username != context.userName)
{
User::pointer currentUser {User::getByLoginName(context.dbSession, context.userName)};
if (!currentUser)
throw RequestedDataNotFoundError {};
User::pointer currentUser {User::getById(context.dbSession, context.userId)};
if (!currentUser)
throw RequestedDataNotFoundError {};
if (!currentUser->isAdmin())
throw UserNotAuthorizedError {};
if (currentUser->getLoginName() != username && !currentUser->isAdmin())
throw UserNotAuthorizedError {};
}
static
void
checkUserIsAdmin(RequestContext& context)
{
LMS_LOG(API_SUBSONIC, DEBUG) << "Check user is admin";
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer currentUser {User::getById(context.dbSession, context.userId)};
if (!currentUser)
{
LMS_LOG(API_SUBSONIC, DEBUG) << "NOT FOUND";
throw RequestedDataNotFoundError {};
}
if (!currentUser->isAdmin())
{
LMS_LOG(API_SUBSONIC, DEBUG) << "NOT ADMIN";
throw UserNotAuthorizedError {};
}
}
@@ -327,7 +347,6 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User:
trackResponse.setAttribute("coverArt", IdToString({Id::Type::Track, track.id()}));
const std::vector<Artist::pointer>& artists {track->getArtists({TrackArtistLinkType::Artist})};
LMS_LOG(API_SUBSONIC, DEBUG) << "Artists count = " << artists.size();
if (!artists.empty())
{
trackResponse.setAttribute("artist", getArtistNames(artists));
@@ -573,7 +592,7 @@ handleCreatePlaylistRequest(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -629,12 +648,12 @@ handleCreateUserRequest(RequestContext& context)
}
auto removeCreatedUser {[&]()
{
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getById(context.dbSession, userId)};
if (user)
user.remove();
}};
{
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getById(context.dbSession, userId)};
if (user)
user.remove();
}};
try
{
@@ -664,7 +683,7 @@ handleDeletePlaylistRequest(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -687,16 +706,16 @@ handleDeleteUserRequest(RequestContext& context)
{
std::string username {getMandatoryParameterAs<std::string>(context.parameters, "username")};
// cannot delete ourself
if (username == context.userName)
throw UserNotAuthorizedError {};
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, username)};
if (!user)
throw RequestedDataNotFoundError {};
// cannot delete ourself
if (user.id() == context.userId)
throw UserNotAuthorizedError {};
user.remove();
return Response::createOkResponse(context);
@@ -726,7 +745,7 @@ handleGetRandomSongsRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -758,7 +777,7 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -860,7 +879,7 @@ handleGetAlbumRequest(RequestContext& context)
if (!release)
throw RequestedDataNotFoundError {};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -892,7 +911,7 @@ handleGetArtistRequest(RequestContext& context)
if (!artist)
throw RequestedDataNotFoundError {};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -943,7 +962,7 @@ handleGetArtistInfoRequestCommon(RequestContext& context, bool id3)
{
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -987,7 +1006,7 @@ handleGetArtistsRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1031,7 +1050,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1137,7 +1156,7 @@ handleGetIndexesRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1190,7 +1209,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
if (!artist)
throw RequestedDataNotFoundError {};
const User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
const User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1239,7 +1258,7 @@ handleGetStarredRequestCommon(RequestContext& context, bool id3)
{
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1313,7 +1332,7 @@ handleGetPlaylistRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1339,7 +1358,7 @@ handleGetPlaylistsRequest(RequestContext& context)
{
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1376,7 +1395,7 @@ handleGetSongsByGenreRequest(RequestContext& context)
if (!cluster)
throw RequestedDataNotFoundError {};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1446,7 +1465,7 @@ handleSearchRequestCommon(RequestContext& context, bool id3)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1528,7 +1547,7 @@ handleStarRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1584,7 +1603,7 @@ handleUnstarRequest(RequestContext& context)
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw RequestedDataNotFoundError {};
@@ -1631,7 +1650,7 @@ handleScrobble(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw RequestedDataNotFoundError {};
@@ -1707,7 +1726,7 @@ handleUpdatePlaylistRequest(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1756,7 +1775,7 @@ handleGetBookmarks(RequestContext& context)
{
auto transaction {context.dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1790,7 +1809,7 @@ handleCreateBookmark(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
const User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
const User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1821,7 +1840,7 @@ handleDeleteBookmark(RequestContext& context)
auto transaction {context.dbSession.createUniqueTransaction()};
const User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
const User::pointer user {User::getById(context.dbSession, context.userId)};
if (!user)
throw UserNotAuthorizedError {};
@@ -1995,6 +2014,39 @@ static std::unordered_map<std::string, MediaRetrievalHandlerFunc> mediaRetrieval
{"getCoverArt", handleGetCoverArt},
};
static
Database::IdType
authenticateUser(const Wt::Http::Request &request, const ClientInfo& clientInfo, Session& dbSession)
{
if (auto *authEnvService {Service<::Auth::IEnvService>::get()})
{
const auto checkResult {authEnvService->processRequest(dbSession, request)};
if (checkResult.state != ::Auth::IEnvService::CheckResult::State::Granted)
throw UserNotAuthorizedError {};
return *checkResult.userId;
}
else if (auto *authPasswordService {Service<::Auth::IPasswordService>::get()})
{
const auto checkResult {authPasswordService->checkUserPassword(dbSession,
boost::asio::ip::address::from_string(request.clientAddress()),
clientInfo.user, clientInfo.password)};
switch (checkResult.state)
{
case Auth::IPasswordService::CheckResult::State::Granted:
return *checkResult.userId;
break;
case Auth::IPasswordService::CheckResult::State::Denied:
throw WrongUsernameOrPasswordError {};
case Auth::IPasswordService::CheckResult::State::Throttled:
throw LoginThrottledGenericError {};
}
}
throw InternalErrorGenericError {"No service avalaible to authenticate user"};
}
void
SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response)
{
@@ -2024,21 +2076,8 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
Session& dbSession {_db.getTLSSession()};
const Auth::IPasswordService::CheckResult checkResult {Service<Auth::IPasswordService>::get()->checkUserPassword(dbSession,
boost::asio::ip::address::from_string(request.clientAddress()),
clientInfo.user, clientInfo.password)};
switch (checkResult.state)
{
case Auth::IPasswordService::CheckResult::State::Granted:
break;
case Auth::IPasswordService::CheckResult::State::Denied:
throw WrongUsernameOrPasswordError {};
case Auth::IPasswordService::CheckResult::State::Throttled:
throw LoginThrottledGenericError {};
}
RequestContext requestContext {parameters, dbSession, clientInfo.user, clientInfo.name};
const Database::IdType userId {authenticateUser(request, clientInfo, dbSession)};
RequestContext requestContext {parameters, dbSession, userId, clientInfo.name};
auto itEntryPoint {requestEntryPoints.find(requestPath)};
if (itEntryPoint != requestEntryPoints.end())
@@ -2047,13 +2086,7 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
itEntryPoint->second.checkFunc();
if (itEntryPoint->second.mustBeAdmin)
{
auto transaction {dbSession.createSharedTransaction()};
User::pointer user {User::getByLoginName(dbSession, clientInfo.user)};
if (!user || !user->isAdmin())
throw UserNotAuthorizedError {};
}
checkUserIsAdmin(requestContext);
Response resp {(itEntryPoint->second.func)(requestContext)};