Properly handled http headers for subsonic API. ref #119
This commit is contained in:
@@ -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)};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user