Authorized endpoints and updated README

This commit is contained in:
kdeng00
2019-09-25 23:03:38 -04:00
parent 698ccf9099
commit 32ed611112
13 changed files with 563 additions and 459 deletions
+7 -3
View File
@@ -1,6 +1,6 @@
# Icarus # Icarus
Icarus is a music streaming API Server that interacts with [Mear](https://github.com/amazing-username/mear). Icarus is a music streaming API Server, allowing access to stream your personal music collection
### Interfacing With Icarus ### Interfacing With Icarus
@@ -8,6 +8,7 @@ One can interface with Icarus the music server either by:
* [Mear](https://github.com/amazing-username/mear) - Partially implemented (under development) * [Mear](https://github.com/amazing-username/mear) - Partially implemented (under development)
* [IcarusDownloadManager](https://github.com/amazing-username/IcarusDownloadManager) * [IcarusDownloadManager](https://github.com/amazing-username/IcarusDownloadManager)
* Create your own client to interact with the API
@@ -19,6 +20,9 @@ One can interface with Icarus the music server either by:
* [json](https://www.github.com/nlohmann/json) * [json](https://www.github.com/nlohmann/json)
* [cpr](https://www.github.com/whoshuu/cpr) * [cpr](https://www.github.com/whoshuu/cpr)
* [TagLib](https://github.com/taglib/taglib) * [TagLib](https://github.com/taglib/taglib)
* [jwt-cpp](https://github.com/Thalhammer/jwt-cpp)
* [libbcrypt](https://github.com/rg3/libbcrypt)
* [oatpp](https://github.com/oatpp/oatpp)
![image](https://user-images.githubusercontent.com/14333136/56252069-28532d00-6084-11e9-896d-1a3c378014ef.png) ![image](https://user-images.githubusercontent.com/14333136/56252069-28532d00-6084-11e9-896d-1a3c378014ef.png)
@@ -117,7 +121,7 @@ In order for Database functionality to be operable, there must be a valid connec
``` ```
* server - The address or domain name of the MySQL server * server - The address or domain name of the MySQL server
* database - The database name * database - The database name
* user - Username * username - Username
* password - Self-explanatory * password - Self-explanatory
The only requirement of the User is that the user should have full permissions to the database as well as permissions to create a database. Other than that, that is all that is required. The only requirement of the User is that the user should have full permissions to the database as well as permissions to create a database. Other than that, that is all that is required.
@@ -135,7 +139,7 @@ Prior to starting the API, the database must be created. The following tables ar
There is a MySQL script to create these tables, it can be found in the [Scripts/MySQL/](https://github.com/amazing-username/Icarus/blob/master/Scripts/MySQL/create_database.sql) directory. Just merely execute: There is a MySQL script to create these tables, it can be found in the [Scripts/MySQL/](https://github.com/amazing-username/Icarus/blob/master/Scripts/MySQL/create_database.sql) directory. Just merely execute:
```shell ```shell
mysql -u *dblikedecibel* -p < Scripts/MySQL/create_database.sql mysql -u dblikedecibel -p < Scripts/MySQL/create_database.sql
``` ```
From this point the database has been successfully created. Metadata and song filesystem locations can be saved. From this point the database has been successfully created. Metadata and song filesystem locations can be saved.
+5 -6
View File
@@ -6,22 +6,21 @@
#include "oatpp/core/data/stream/FileStream.hpp" #include "oatpp/core/data/stream/FileStream.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp" #include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp"
namespace callback namespace callback {
class StreamCallback : public oatpp::data::stream::ReadCallback
{ {
class StreamCallback : public oatpp::data::stream::ReadCallback public:
{
public:
StreamCallback(); StreamCallback();
StreamCallback(const std::string&); StreamCallback(const std::string&);
oatpp::data::v_io_size read(void*, oatpp::data::v_io_size); oatpp::data::v_io_size read(void*, oatpp::data::v_io_size);
private: private:
std::string m_songPath; std::string m_songPath;
long m_bytesRead; long m_bytesRead;
long m_counter; long m_counter;
long m_fileSize; long m_fileSize;
}; };
} }
#endif #endif
+6 -7
View File
@@ -8,12 +8,11 @@
#include "oatpp/parser/json/mapping/ObjectMapper.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/web/server/HttpConnectionHandler.hpp" #include "oatpp/web/server/HttpConnectionHandler.hpp"
namespace component namespace component {
{
class AppComponent class AppComponent
{ {
public: public:
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] { OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] {
return oatpp::network::server::SimpleTCPConnectionProvider::createShared(5002); return oatpp::network::server::SimpleTCPConnectionProvider::createShared(5002);
}()); }());
@@ -31,8 +30,8 @@ namespace component
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, apiObjectMapper)([] { OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, apiObjectMapper)([] {
return oatpp::parser::json::mapping::ObjectMapper::createShared(); return oatpp::parser::json::mapping::ObjectMapper::createShared();
}()); }());
private: private:
}; };
} }
#endif #endif
+18 -8
View File
@@ -20,17 +20,17 @@
#include "database/AlbumRepository.h" #include "database/AlbumRepository.h"
#include "dto/AlbumDto.hpp" #include "dto/AlbumDto.hpp"
#include "manager/AlbumManager.h" #include "manager/AlbumManager.h"
#include "manager/TokenManager.h"
#include "model/Models.h" #include "model/Models.h"
#include "type/Scopes.h" #include "type/Scopes.h"
#include "type/AlbumFilter.h" #include "type/AlbumFilter.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class AlbumController : public oatpp::web::server::api::ApiController
{ {
class AlbumController : public oatpp::web::server::api::ApiController public:
{
public:
AlbumController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) AlbumController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -45,6 +45,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/album", albumRecords, ENDPOINT("GET", "/api/v1/album", albumRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving album" << std::endl; std::cout << "starting process of retrieving album" << std::endl;
database::AlbumRepository albRepo(m_bConf); database::AlbumRepository albRepo(m_bConf);
auto albsDb = albRepo.retrieveRecords(); auto albsDb = albRepo.retrieveRecords();
@@ -64,7 +70,12 @@ namespace controller
// endpoint for retrieving single album record by the album id in json format // endpoint for retrieving single album record by the album id in json format
ENDPOINT("GET", "/api/v1/album/{id}", albumRecord, ENDPOINT("GET", "/api/v1/album/{id}", albumRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
database::AlbumRepository albRepo(m_bConf); database::AlbumRepository albRepo(m_bConf);
model::Album albDb(id); model::Album albDb(id);
@@ -83,10 +94,9 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); };
};
} }
#endif #endif
+18 -8
View File
@@ -20,17 +20,17 @@
#include "database/ArtistRepository.h" #include "database/ArtistRepository.h"
#include "dto/ArtistDto.hpp" #include "dto/ArtistDto.hpp"
#include "manager/ArtistManager.h" #include "manager/ArtistManager.h"
#include "manager/TokenManager.h"
#include "model/Models.h" #include "model/Models.h"
#include "type/Scopes.h" #include "type/Scopes.h"
#include "type/ArtistFilter.h" #include "type/ArtistFilter.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class ArtistController : public oatpp::web::server::api::ApiController
{ {
class ArtistController : public oatpp::web::server::api::ApiController public:
{
public:
ArtistController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) ArtistController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -45,6 +45,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/artist", artistRecords, ENDPOINT("GET", "/api/v1/artist", artistRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving artist" << std::endl; std::cout << "starting process of retrieving artist" << std::endl;
database::ArtistRepository artRepo(m_bConf); database::ArtistRepository artRepo(m_bConf);
auto artsDb = artRepo.retrieveRecords(); auto artsDb = artRepo.retrieveRecords();
@@ -63,7 +69,12 @@ namespace controller
// endpoint for retrieving single artist record by the artist id in json format // endpoint for retrieving single artist record by the artist id in json format
ENDPOINT("GET", "/api/v1/artist/{id}", artistRecord, ENDPOINT("GET", "/api/v1/artist/{id}", artistRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
database::ArtistRepository artRepo(m_bConf); database::ArtistRepository artRepo(m_bConf);
model::Artist artDb(id); model::Artist artDb(id);
@@ -81,10 +92,9 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); };
};
} }
#endif #endif
+23 -21
View File
@@ -26,11 +26,10 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class CoverArtController : public oatpp::web::server::api::ApiController
{ {
class CoverArtController : public oatpp::web::server::api::ApiController public:
{
public:
CoverArtController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) CoverArtController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -45,6 +44,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/coverart", coverArtRecords, ENDPOINT("GET", "/api/v1/coverart", coverArtRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving cover art" << std::endl; std::cout << "starting process of retrieving cover art" << std::endl;
database::CoverArtRepository covRepo(m_bConf); database::CoverArtRepository covRepo(m_bConf);
auto covsDb = covRepo.retrieveRecords(); auto covsDb = covRepo.retrieveRecords();
@@ -63,7 +68,12 @@ namespace controller
// endpoint for retrieving single cover art record by the cover art id in json format // endpoint for retrieving single cover art record by the cover art id in json format
ENDPOINT("GET", "/api/v1/coverart/{id}", coverArtRecord, ENDPOINT("GET", "/api/v1/coverart/{id}", coverArtRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
database::CoverArtRepository covRepo(m_bConf); database::CoverArtRepository covRepo(m_bConf);
model::Cover covDb; model::Cover covDb;
@@ -82,25 +92,18 @@ namespace controller
} }
ENDPOINT("GET", "/api/v1/coverart/download/{id}", downloadCoverArt, ENDPOINT("GET", "/api/v1/coverart/download/{id}", downloadCoverArt,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
database::CoverArtRepository covRepo(m_bConf); database::CoverArtRepository covRepo(m_bConf);
model::Cover covDb; model::Cover covDb;
covDb.id = id; covDb.id = id;
covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id); covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id);
/**
std::ifstream fl(covDb.imagePath.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
fl.seekg(0);
std::stringstream buf;
std::copy(std::istreambuf_iterator<char>(fl),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(buf));
fl.close();
*/
//auto rawCover = oatpp::String(buf.str().data(), (v_int32)buf.str().size(), true);
auto rawCover = oatpp::base::StrBuffer::loadFromFile(covDb.imagePath.c_str()); auto rawCover = oatpp::base::StrBuffer::loadFromFile(covDb.imagePath.c_str());
auto response = createResponse(Status::CODE_200, rawCover); auto response = createResponse(Status::CODE_200, rawCover);
@@ -110,10 +113,9 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); };
};
} }
#endif #endif
+18 -8
View File
@@ -20,17 +20,17 @@
#include "database/GenreRepository.h" #include "database/GenreRepository.h"
#include "dto/GenreDto.hpp" #include "dto/GenreDto.hpp"
#include "manager/GenreManager.h" #include "manager/GenreManager.h"
#include "manager/YearManager.h"
#include "model/Models.h" #include "model/Models.h"
#include "type/Scopes.h" #include "type/Scopes.h"
#include "type/GenreFilter.h" #include "type/GenreFilter.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class GenreController : public oatpp::web::server::api::ApiController
{ {
class GenreController : public oatpp::web::server::api::ApiController public:
{
public:
GenreController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) GenreController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -45,6 +45,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/genre", genreRecords, ENDPOINT("GET", "/api/v1/genre", genreRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving genre" << std::endl; std::cout << "starting process of retrieving genre" << std::endl;
database::GenreRepository gnrRepo(m_bConf); database::GenreRepository gnrRepo(m_bConf);
auto gnrsDb = gnrRepo.retrieveRecords(); auto gnrsDb = gnrRepo.retrieveRecords();
@@ -63,7 +69,12 @@ namespace controller
// endpoint for retrieving single genre record by the genre id in json format // endpoint for retrieving single genre record by the genre id in json format
ENDPOINT("GET", "/api/v1/genre/{id}", genreRecord, ENDPOINT("GET", "/api/v1/genre/{id}", genreRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
database::GenreRepository gnrRepo(m_bConf); database::GenreRepository gnrRepo(m_bConf);
model::Genre gnrDb(id); model::Genre gnrDb(id);
@@ -81,10 +92,9 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); };
};
} }
#endif #endif
+46 -15
View File
@@ -29,11 +29,10 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class SongController : public oatpp::web::server::api::ApiController
{ {
class SongController : public oatpp::web::server::api::ApiController public:
{
public:
SongController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) SongController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -49,11 +48,8 @@ namespace controller
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization"); auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str(); auto auth = authHeader->std_str();
manager::TokenManager tok; manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::upload), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::upload), Status::CODE_403, "Not allowed");
@@ -87,6 +83,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/song", songRecords, ENDPOINT("GET", "/api/v1/song", songRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving songs" << std::endl; std::cout << "starting process of retrieving songs" << std::endl;
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
auto songsDb = songRepo.retrieveRecords(); auto songsDb = songRepo.retrieveRecords();
@@ -113,12 +115,18 @@ namespace controller
// endpoint for retrieving song record by the song id in json format // endpoint for retrieving song record by the song id in json format
ENDPOINT("GET", "/api/v1/song/{id}", songRecord, ENDPOINT("GET", "/api/v1/song/{id}", songRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
OATPP_ASSERT_HTTP(songRepo.doesSongExist(songDb, type::SongFilter::id) , Status::CODE_403, "song does not exist"); OATPP_ASSERT_HTTP(songRepo.doesSongExist(songDb, type::SongFilter::id) , Status::CODE_403,
"song does not exist");
std::cout << "song exists" << std::endl; std::cout << "song exists" << std::endl;
songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id); songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id);
@@ -138,7 +146,12 @@ namespace controller
} }
ENDPOINT("GET", "/api/v1/song/data/{id}", downloadSong, ENDPOINT("GET", "/api/v1/song/data/{id}", downloadSong,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::download), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
@@ -152,10 +165,15 @@ namespace controller
return response; return response;
} }
// TODO: create endpoint for updating songs
ENDPOINT("UPDATE", "/api/v1/song/{id}", songUpdate, ENDPOINT("UPDATE", "/api/v1/song/{id}", songUpdate,
REQUEST(std::shared_ptr<IncomingRequest>, request),
BODY_DTO(dto::SongDto::ObjectWrapper, songDto), PATH(Int32, id)) { BODY_DTO(dto::SongDto::ObjectWrapper, songDto), PATH(Int32, id)) {
songDto->id = id; songDto->id = id;
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::updateSong), Status::CODE_403, "Not allowed");
auto updatedSong = manager::SongManager::songDtoConv(songDto); auto updatedSong = manager::SongManager::songDtoConv(songDto);
manager::SongManager songMgr(m_bConf); manager::SongManager songMgr(m_bConf);
@@ -165,7 +183,14 @@ namespace controller
} }
// endpoint to delete a song // endpoint to delete a song
ENDPOINT("DELETE", "api/v1/song/data/{id}", songDelete, PATH(Int32, id)) { ENDPOINT("DELETE", "api/v1/song/data/{id}", songDelete,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::deleteSong), Status::CODE_403, "Not allowed");
model::Song song(id); model::Song song(id);
manager::SongManager sngMgr(m_bConf); manager::SongManager sngMgr(m_bConf);
@@ -176,7 +201,13 @@ namespace controller
// endpoint for streaming a song // endpoint for streaming a song
ENDPOINT("GET", "/api/v1/song/stream/{id}", streamSong, ENDPOINT("GET", "/api/v1/song/stream/{id}", streamSong,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::stream), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id); songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id);
@@ -194,12 +225,12 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); const long m_dataSize = std::numeric_limits<long long int>::max();
}; };
} }
#endif #endif
+17 -8
View File
@@ -26,11 +26,10 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller namespace controller {
class YearController : public oatpp::web::server::api::ApiController
{ {
class YearController : public oatpp::web::server::api::ApiController public:
{
public:
YearController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) YearController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p)
{ } { }
@@ -45,6 +44,12 @@ namespace controller
ENDPOINT("GET", "/api/v1/year", yearRecords, ENDPOINT("GET", "/api/v1/year", yearRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request))
{ {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving year" << std::endl; std::cout << "starting process of retrieving year" << std::endl;
database::YearRepository yrRepo(m_bConf); database::YearRepository yrRepo(m_bConf);
auto yrsDb = yrRepo.retrieveRecords(); auto yrsDb = yrRepo.retrieveRecords();
@@ -63,7 +68,12 @@ namespace controller
// endpoint for retrieving single year record by the year id in json format // endpoint for retrieving single year record by the year id in json format
ENDPOINT("GET", "/api/v1/year/{id}", yearRecord, ENDPOINT("GET", "/api/v1/year/{id}", yearRecord,
PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
database::YearRepository yrRepo(m_bConf); database::YearRepository yrRepo(m_bConf);
model::Year yrDb(id); model::Year yrDb(id);
@@ -81,10 +91,9 @@ namespace controller
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string m_exe_path; std::string m_exe_path;
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max(); };
};
} }
#endif #endif
+5 -6
View File
@@ -13,18 +13,17 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/Scopes.h" #include "type/Scopes.h"
namespace manager namespace manager {
class TokenManager
{ {
class TokenManager public:
{
public:
TokenManager(); TokenManager();
model::LoginResult retrieveToken(const model::BinaryPath&); model::LoginResult retrieveToken(const model::BinaryPath&);
bool isTokenValid(std::string&, type::Scope); bool isTokenValid(std::string&, type::Scope);
bool testAuth(const model::BinaryPath&); bool testAuth(const model::BinaryPath&);
private: private:
cpr::Response sendRequest(std::string_view, nlohmann::json&); cpr::Response sendRequest(std::string_view, nlohmann::json&);
nlohmann::json createTokenBody(const model::AuthCredentials&); nlohmann::json createTokenBody(const model::AuthCredentials&);
@@ -36,7 +35,7 @@ namespace manager
std::pair<bool, std::vector<std::string>> fetchAuthHeader(const std::string&); std::pair<bool, std::vector<std::string>> fetchAuthHeader(const std::string&);
bool tokenSupportsScope(const std::vector<std::string>, const std::string&&); bool tokenSupportsScope(const std::vector<std::string>, const std::string&&);
}; };
} }
#endif #endif
+11 -1
View File
@@ -6,7 +6,17 @@ namespace type
enum class Scope enum class Scope
{ {
upload = 0, upload = 0,
download download,
stream,
retrieveSong,
updateSong,
deleteSong,
retrieveAlbum,
retrieveArtist,
retrieveGenre,
retrieveYear,
downloadCoverArt
}; };
} }
+5 -3
View File
@@ -4,10 +4,11 @@
#include <fstream> #include <fstream>
#include <memory> #include <memory>
callback::StreamCallback::StreamCallback() : namespace callback {
StreamCallback::StreamCallback() :
m_counter(0) { } m_counter(0) { }
callback::StreamCallback::StreamCallback(const std::string& songPath) : StreamCallback::StreamCallback(const std::string& songPath) :
m_songPath(songPath), m_songPath(songPath),
m_counter(0), m_counter(0),
m_bytesRead(0) m_bytesRead(0)
@@ -19,7 +20,7 @@ callback::StreamCallback::StreamCallback(const std::string& songPath) :
} }
oatpp::data::v_io_size callback::StreamCallback::read(void *buff, oatpp::data::v_io_size count) oatpp::data::v_io_size StreamCallback::read(void *buff, oatpp::data::v_io_size count)
{ {
if (m_counter >= m_fileSize) { if (m_counter >= m_fileSize) {
std::cout << "done streaming song" << std::endl; std::cout << "done streaming song" << std::endl;
@@ -40,3 +41,4 @@ oatpp::data::v_io_size callback::StreamCallback::read(void *buff, oatpp::data::v
return count; return count;
} }
}
+35 -16
View File
@@ -13,13 +13,13 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace manager {
manager::TokenManager::TokenManager() TokenManager::TokenManager()
{ {
} }
model::LoginResult manager::TokenManager::retrieveToken(const model::BinaryPath& bConf) model::LoginResult TokenManager::retrieveToken(const model::BinaryPath& bConf)
{ {
auto cred = parseAuthCredentials(bConf); auto cred = parseAuthCredentials(bConf);
@@ -46,7 +46,7 @@ model::LoginResult manager::TokenManager::retrieveToken(const model::BinaryPath&
} }
bool manager::TokenManager::isTokenValid(std::string& auth, type::Scope scope) bool TokenManager::isTokenValid(std::string& auth, type::Scope scope)
{ {
auto authPair = fetchAuthHeader(auth); auto authPair = fetchAuthHeader(auth);
@@ -65,9 +65,26 @@ bool manager::TokenManager::isTokenValid(std::string& auth, type::Scope scope)
switch (scope) { switch (scope) {
case type::Scope::upload: case type::Scope::upload:
return tokenSupportsScope(scopes, "upload:songs"); return tokenSupportsScope(scopes, "upload:songs");
break;
case type::Scope::download: case type::Scope::download:
return tokenSupportsScope(scopes, "download:songs"); return tokenSupportsScope(scopes, "download:songs");
case type::Scope::stream:
return tokenSupportsScope(scopes, "stream:songs");
case type::Scope::deleteSong:
return tokenSupportsScope(scopes, "delete:songs");
case type::Scope::updateSong:
return tokenSupportsScope(scopes, "update:songs");
case type::Scope::retrieveSong:
return tokenSupportsScope(scopes, "read:song_details");
case type::Scope::retrieveAlbum:
return tokenSupportsScope(scopes, "read:albums");
case type::Scope::retrieveArtist:
return tokenSupportsScope(scopes, "read:artists");
case type::Scope::retrieveGenre:
return tokenSupportsScope(scopes, "read:genre");
case type::Scope::retrieveYear:
return tokenSupportsScope(scopes, "read:year");
case type::Scope::downloadCoverArt:
return tokenSupportsScope(scopes, "download:cover_art");
default: default:
break; break;
} }
@@ -75,7 +92,7 @@ bool manager::TokenManager::isTokenValid(std::string& auth, type::Scope scope)
return false; return false;
} }
bool manager::TokenManager::testAuth(const model::BinaryPath& bConf) bool TokenManager::testAuth(const model::BinaryPath& bConf)
{ {
auto cred = parseAuthCredentials(bConf); auto cred = parseAuthCredentials(bConf);
auto reqObj = createTokenBody(cred); auto reqObj = createTokenBody(cred);
@@ -90,7 +107,7 @@ bool manager::TokenManager::testAuth(const model::BinaryPath& bConf)
} }
cpr::Response manager::TokenManager::sendRequest(std::string_view uri, nlohmann::json& obj) cpr::Response TokenManager::sendRequest(std::string_view uri, nlohmann::json& obj)
{ {
auto resp = cpr::Post(cpr::Url{uri}, cpr::Body{obj.dump()}, auto resp = cpr::Post(cpr::Url{uri}, cpr::Body{obj.dump()},
cpr::Header{{"Content-type", "application/json"}, cpr::Header{{"Content-type", "application/json"},
@@ -100,7 +117,7 @@ cpr::Response manager::TokenManager::sendRequest(std::string_view uri, nlohmann:
} }
nlohmann::json manager::TokenManager::createTokenBody(const model::AuthCredentials& auth) nlohmann::json TokenManager::createTokenBody(const model::AuthCredentials& auth)
{ {
nlohmann::json obj; nlohmann::json obj;
obj["client_id"] = auth.clientId; obj["client_id"] = auth.clientId;
@@ -112,12 +129,12 @@ nlohmann::json manager::TokenManager::createTokenBody(const model::AuthCredentia
} }
model::AuthCredentials manager::TokenManager::parseAuthCredentials(std::string_view path) model::AuthCredentials TokenManager::parseAuthCredentials(std::string_view path)
{ {
auto exe_path = manager::DirectoryManager::configPath(path); auto exe_path = DirectoryManager::configPath(path);
exe_path.append("/authcredentials.json"); exe_path.append("/authcredentials.json");
auto con = manager::DirectoryManager::credentialConfigContent(exe_path); auto con = DirectoryManager::credentialConfigContent(exe_path);
model::AuthCredentials auth; model::AuthCredentials auth;
auth.uri = "https://"; auth.uri = "https://";
@@ -129,9 +146,9 @@ model::AuthCredentials manager::TokenManager::parseAuthCredentials(std::string_v
return auth; return auth;
} }
model::AuthCredentials manager::TokenManager::parseAuthCredentials(const model::BinaryPath& bConf) model::AuthCredentials TokenManager::parseAuthCredentials(const model::BinaryPath& bConf)
{ {
auto con = manager::DirectoryManager::credentialConfigContent(bConf); auto con = DirectoryManager::credentialConfigContent(bConf);
model::AuthCredentials auth; model::AuthCredentials auth;
auth.uri = "https://"; auth.uri = "https://";
@@ -144,7 +161,7 @@ model::AuthCredentials manager::TokenManager::parseAuthCredentials(const model::
return auth; return auth;
} }
std::vector<std::string> manager::TokenManager::extractScopes(const jwt::decoded_jwt&& decoded) std::vector<std::string> TokenManager::extractScopes(const jwt::decoded_jwt&& decoded)
{ {
std::vector<std::string> scopes; std::vector<std::string> scopes;
@@ -162,7 +179,7 @@ std::vector<std::string> manager::TokenManager::extractScopes(const jwt::decoded
return scopes; return scopes;
} }
std::pair<bool, std::vector<std::string>> manager::TokenManager::fetchAuthHeader(const std::string& auth) std::pair<bool, std::vector<std::string>> TokenManager::fetchAuthHeader(const std::string& auth)
{ {
std::istringstream iss(auth); std::istringstream iss(auth);
std::vector<std::string> authHeader{std::istream_iterator<std::string>(iss), std::vector<std::string> authHeader{std::istream_iterator<std::string>(iss),
@@ -182,10 +199,12 @@ std::pair<bool, std::vector<std::string>> manager::TokenManager::fetchAuthHeader
} }
bool manager::TokenManager::tokenSupportsScope(const std::vector<std::string> scopes, const std::string&& scope) bool TokenManager::tokenSupportsScope(const std::vector<std::string> scopes,
const std::string&& scope)
{ {
return std::any_of(scopes.begin(), scopes.end(), return std::any_of(scopes.begin(), scopes.end(),
[&](std::string foundScope) { [&](std::string foundScope) {
return (foundScope.compare(scope) == 0); return (foundScope.compare(scope) == 0);
}); });
} }
}