diff --git a/README.md b/README.md
index 306ae15..a6cf025 100644
--- a/README.md
+++ b/README.md
@@ -43,10 +43,10 @@ Create the API and enter an approrpiate name and identified. For the identified,
Replace [domain] with the domain name of the created tenant. This can be found in the Default App from the Application menu. Replace [identifier] with the identifer root name in the appsettings environment file. Not the friendly name but the root name of the identifier, omitting the http protocol and the *api* path.
```Json
- "Auth0": {
- "Domain": "[domain].auth0.com",
- "ApiIdentifier": "https://[identifier]/api"
- },
+ "domain": "[domain].auth0.com",
+ "api_identifier": "https://[identifier]/api",
+ "client_id": "iamunique",
+ "client_secret": "Icankeepasecret"
```
For the sake of this section, I will not go over configuring the API to accept the signing algorithm since it has already been configured in the [Startip](Startup.cs).cs file. Click on permissions to create the permissions for the API.
@@ -73,7 +73,7 @@ From the Application page, copy the client id and client secret. These values wi
-Enter the information in the corresponding ``authcredentials.json`` file
+Enter the information in the corresponding ``authcredentials.json`` file
```Json
{
"domain": "somedomain.auth0.com",
@@ -88,10 +88,10 @@ Enter the information in the corresponding ``authcredentials.json`` file
For the purposes of properly uploading, downloading, updating, deleting, and streaming songs the API filesystem paths must be configured. For that purpose you have to open the ``paths.json`` file. What is meant by this is that the `root_music_path` directory where all music will be stored must exist. The `cover_root_path`, `archive_root_path`, and `temp_root_path` paths must exist and be accessible. An example on a Linux system:
```Json
{
- "root_music_path": "/home/dev/null/music/",
- "temp_root_path": "/home/dev/null/music/temp/",
- "cover_root_path": "/home/dev/null/music/coverArt/",
- "archive_root_path": "/home/dev/null/music/archive/"
+ "root_music_path": "/dev/null/music/",
+ "temp_root_path": "/dev/null/music/temp/",
+ "cover_root_path": "/dev/null/music/coverArt/",
+ "archive_root_path": "/dev/null/music/archive/"
}
```
* `root_music_path` - Where music will be stored in the following convention: *`Artist/Album/Songs`*
@@ -104,55 +104,56 @@ For the purposes of properly uploading, downloading, updating, deleting, and str
### Database connection string
-In order for Database functionality to be operable, there must be a valid connection string and credentials with appropriate permissions. **At the moment there is only support for MySQL**. Depending on your environment `Release` or `Debug` you will need to edit the appsettings.json or appsettings.Development.json accordingly. An example of the fields to change are below:
+In order for Database functionality to be operable, there must be a valid connection string and MySQL credentials with appropriate permissions. **At the moment there is only support for MySQL**. Edit the database.json file accordingly. An example of the fields to change are below:
```Json
{
- "ConnectionStrings": {
- "DefaultConnection": "Server=localhost;Database=my_db;Uid=admin;Pwd=toughpassword;"
- }
+ "server": "localhost",
+ "database": "my_db",
+ "username": "admin",
+ "password": "toughpassword"
}
```
-* Server - The address or domain name of the MySQL server
-* Database - The database name
-* Uid - Username
-* Password - Self-explanatory
+* server - The address or domain name of the MySQL server
+* database - The database name
+* user - Username
+* 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.
-### Migrations
+### Database
-Prior to starting the API, the Migrations must be applied. There are 6 tables with migrations being applied and thy are:
-* Users
+Prior to starting the API, the database must be created. The following tables are required:
+* User
* Song
* Album
* Artist
* Year
* Genre
+* CoverArt
-There is a script for Linux systems to apply these migrations, it can be found in the [Scripts/Migrations/Linux](https://github.com/amazing-username/Icarus/blob/master/Scripts/Migrations/Linux/AddUpdate.sh) 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
-scripts/Migrations/Linux/AddUpdate.sh
+mysql -u ** -p < Scripts/MySQL/create_database.sql
```
-Or you can manually add the migrations like so for each migration:
-```shell
-dotnet ef migrations Add [Migration] --context [Migration]Context
-```
-Then update the migrations to the database like so*:
-```shell
-dotnet ef database update --context [Migration]Context
-```
-From this point the database has been successfully configured. Metadata and song filesystem locations can be saved.
-* Will only need to execute this for UserContext and SongContext because the Song table has relational constraints with Album, Artist, Year, and Genre.
+From this point the database has been successfully created. Metadata and song filesystem locations can be saved.
+
## Building and Running
```
git clone --recursive https://github.com/kdeng00/icarus
+cd 3rdparty/libbcrypt/
+make
+cp bcrypt.a libbcrypt.a
+cp bcrypt.o libbcrypt.o
+cd ../..
mkdir build
cd build
+conan install ..
cmake ..
+make
bin/icarus
```
Runs the server on localhost port 5002
diff --git a/include/controller/LoginController.hpp b/include/controller/LoginController.hpp
index c303534..0d51079 100644
--- a/include/controller/LoginController.hpp
+++ b/include/controller/LoginController.hpp
@@ -11,44 +11,59 @@
#include "dto/LoginResultDto.hpp"
#include "manager/TokenManager.h"
+#include "manager/UserManager.h"
#include "model/Models.h"
-namespace controller
+namespace controller {
+class LoginController : public oatpp::web::server::api::ApiController
{
- class LoginController : public oatpp::web::server::api::ApiController
- {
- public:
- LoginController(std::string p, OATPP_COMPONENT(std::shared_ptr, objectMapper))
- : oatpp::web::server::api::ApiController(objectMapper), exe_path(p)
- { }
- LoginController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr, objectMapper))
- :oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
- { }
+public:
+ LoginController(std::string p, OATPP_COMPONENT(std::shared_ptr, objectMapper))
+ : oatpp::web::server::api::ApiController(objectMapper), exe_path(p)
+ { }
+ LoginController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr, objectMapper))
+ :oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
+ { }
- #include OATPP_CODEGEN_BEGIN(ApiController)
+ #include OATPP_CODEGEN_BEGIN(ApiController)
- ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
- OATPP_LOGI("icarus", "logging in");
+ ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
+ OATPP_LOGI("icarus", "logging in");
- manager::TokenManager tok;
- auto token = tok.retrieveToken(m_bConf);
+ manager::UserManager usrMgr(m_bConf);
+ auto user = manager::UserManager::userDtoConv(usr);
- auto logRes = dto::LoginResultDto::createShared();
- logRes->id = 0; // TODO: change this later on to something meaningful
- logRes->username = usr->username->c_str();
- logRes->token = token.accessToken.c_str();
- logRes->token_type = token.tokenType.c_str();
- logRes->expiration = token.expiration;
- logRes->message = "Successful";
-
- return createDtoResponse(Status::CODE_200, logRes);
+ if (!usrMgr.doesUserExist(user)) {
+ std::cout << "user does not exist" << std::endl;
+ return createResponse(Status::CODE_401, "invalid credentials");
}
- #include OATPP_CODEGEN_END(ApiController)
- private:
- std::string exe_path;
- model::BinaryPath m_bConf;
- };
+ std::cout << "user exists" << std::endl;
+
+ if (!usrMgr.validatePassword(user)) {
+ std::cout << "password is not valid" << std::endl;
+ return createResponse(Status::CODE_401, "invalid credentials");
+ }
+
+ manager::TokenManager tok;
+ auto token = tok.retrieveToken(m_bConf);
+
+ auto logRes = dto::LoginResultDto::createShared();
+ logRes->id = 0; // TODO: change this later on to something meaningful or remove it
+ logRes->username = usr->username->c_str();
+ logRes->token = token.accessToken.c_str();
+ logRes->token_type = token.tokenType.c_str();
+ logRes->expiration = token.expiration;
+ logRes->message = "Successful";
+
+ return createDtoResponse(Status::CODE_200, logRes);
+ }
+
+ #include OATPP_CODEGEN_END(ApiController)
+private:
+ std::string exe_path;
+ model::BinaryPath m_bConf;
+};
}
#endif
diff --git a/include/database/UserRepository.h b/include/database/UserRepository.h
index a99efb1..1de10a8 100644
--- a/include/database/UserRepository.h
+++ b/include/database/UserRepository.h
@@ -7,36 +7,51 @@
#include "database/BaseRepository.h"
#include "model/Models.h"
+#include "type/SaltFilter.h"
#include "type/UserFilter.h"
namespace database {
- class UserRepository : BaseRepository {
- public:
- UserRepository(const model::BinaryPath&);
+class UserRepository : BaseRepository {
+public:
+ UserRepository(const model::BinaryPath&);
- model::User retrieveUserRecord(model::User&, type::UserFilter);
+ model::User retrieveUserRecord(model::User&, type::UserFilter);
+ model::PassSec retrieverUserSaltRecord(model::PassSec&, type::SaltFilter);
- void saveUserRecord(const model::User&);
- private:
- struct UserLengths;
- struct UserLengths
- {
- unsigned long firstnameLength;
- unsigned long lastnameLength;
- unsigned long phoneLength;
- unsigned long emailLength;
- unsigned long usernameLength;
- unsigned long passwordLength;
- };
+ bool doesUserRecordExist(const model::User&, type::UserFilter);
- std::shared_ptr insertUserValues(const model::User&, std::shared_ptr);
- std::shared_ptr valueBind(model::User&, std::tuple&);
- std::shared_ptr fetchUserLengths(const model::User&);
+ void saveUserRecord(const model::User&);
+ void saveUserSalt(const model::PassSec&);
+private:
+ struct UserLengths;
+ struct SaltLengths;
- std::tuple fetchUV();
-
- model::User parseRecord(MYSQL_STMT*);
+ struct UserLengths
+ {
+ unsigned long firstnameLength;
+ unsigned long lastnameLength;
+ unsigned long phoneLength;
+ unsigned long emailLength;
+ unsigned long usernameLength;
+ unsigned long passwordLength;
};
+ struct SaltLengths
+ {
+ unsigned long saltLength;
+ };
+
+ std::shared_ptr insertUserValues(const model::User&, std::shared_ptr);
+ std::shared_ptr insertSaltValues(const model::PassSec&, std::shared_ptr);
+ std::shared_ptr valueBind(model::User&, std::tuple&);
+ std::shared_ptr saltValueBind(model::PassSec&, char*);
+ std::shared_ptr fetchUserLengths(const model::User&);
+ std::shared_ptr fetchSaltLengths(const model::PassSec&);
+
+ std::tuple fetchUV();
+
+ model::User parseRecord(MYSQL_STMT*);
+ model::PassSec parseSaltRecord(MYSQL_STMT*);
+};
}
diff --git a/include/manager/UserManager.h b/include/manager/UserManager.h
index fa65f44..e33a538 100644
--- a/include/manager/UserManager.h
+++ b/include/manager/UserManager.h
@@ -6,20 +6,22 @@
#include "dto/LoginResultDto.hpp"
#include "model/Models.h"
-namespace manager
+namespace manager {
+class UserManager
{
- class UserManager
- {
- public:
- UserManager(const model::BinaryPath&);
+public:
+ UserManager(const model::BinaryPath&);
- model::User userDtoConv(dto::UserDto::ObjectWrapper&);
+ bool doesUserExist(const model::User&);
+ bool validatePassword(const model::User&);
- void printUser(const model::User&);
- void registerUser(model::User&);
- private:
- model::BinaryPath m_bConf;
- };
+ void printUser(const model::User&);
+ void registerUser(model::User&);
+
+ static model::User userDtoConv(dto::UserDto::ObjectWrapper&);
+private:
+ model::BinaryPath m_bConf;
+};
}
#endif
diff --git a/include/model/Models.h b/include/model/Models.h
index feb4ebf..13f3789 100644
--- a/include/model/Models.h
+++ b/include/model/Models.h
@@ -4,162 +4,163 @@
#include
#include
-namespace model
+namespace model {
+struct Song
{
- struct Song
+ Song() = default;
+ Song(const int id) : id(id) { }
+
+ int id;
+ std::string title;
+ std::string artist;
+ std::string album;
+ std::string genre;
+ int year;
+ int duration;
+ int track;
+ int disc;
+ std::string songPath;
+ std::vector data;
+ int coverArtId;
+ int artistId;
+ int albumId;
+ int genreId;
+ int yearId;
+};
+
+struct Artist
+{
+ Artist() = default;
+ Artist(const Song& song)
{
- Song() = default;
- Song(const int id) : id(id) { }
+ id = song.artistId;
+ artist = song.artist;
+ }
+ Artist(const int id) : id(id) { }
- int id;
- std::string title;
- std::string artist;
- std::string album;
- std::string genre;
- int year;
- int duration;
- int track;
- int disc;
- std::string songPath;
- std::vector data;
- int coverArtId;
- int artistId;
- int albumId;
- int genreId;
- int yearId;
- };
+ int id;
+ std::string artist;
+};
- struct Artist
+struct Album
+{
+ Album() = default;
+ Album(const Song& song)
{
- Artist() = default;
- Artist(const Song& song)
- {
- id = song.artistId;
- artist = song.artist;
- }
- Artist(const int id) : id(id) { }
+ id = song.albumId;
+ title = song.album;
+ year = song.year;
+ }
+ Album(const int id) : id(id) { }
- int id;
- std::string artist;
- };
+ int id;
+ std::string title;
+ int year;
+ std::vector songs;
+};
- struct Album
+struct Genre
+{
+ Genre() = default;
+ Genre(const Song& song)
{
- Album() = default;
- Album(const Song& song)
- {
- id = song.albumId;
- title = song.album;
- year = song.year;
- }
- Album(const int id) : id(id) { }
+ id = song.genreId;
+ category = song.genre;
+ }
+ Genre(const int id) : id(id) { }
- int id;
- std::string title;
- int year;
- std::vector songs;
- };
+ int id;
+ std::string category;
- struct Genre
+};
+
+struct Year
+{
+ Year() = default;
+ Year(const Song& song)
{
- Genre() = default;
- Genre(const Song& song)
- {
- id = song.genreId;
- category = song.genre;
- }
- Genre(const int id) : id(id) { }
+ id = song.yearId;
+ year = song.year;
+ }
+ Year(const int id) : id(id) { }
- int id;
- std::string category;
+ int id;
+ int year;
+};
- };
-
- struct Year
+struct Cover
+{
+ Cover() = default;
+ Cover(const Song& song)
{
- Year() = default;
- Year(const Song& song)
- {
- id = song.yearId;
- year = song.year;
- }
- Year(const int id) : id(id) { }
+ id = song.coverArtId;
+ songTitle = song.title;
+ }
+ Cover(const int id) : id(id) { }
- int id;
- int year;
- };
+ int id;
+ std::string songTitle;
+ std::string imagePath;
+ // Not being used but it should be
+ std::vector data;
+};
- struct Cover
- {
- Cover() = default;
- Cover(const Song& song)
- {
- id = song.coverArtId;
- songTitle = song.title;
- }
- Cover(const int id) : id(id) { }
+struct LoginResult
+{
+ int userId;
+ std::string username;
+ std::string accessToken;
+ std::string tokenType;
+ std::string message;
+ int expiration;
+};
- int id;
- std::string songTitle;
- std::string imagePath;
- // Not being used but it should be
- std::vector data;
- };
+struct User
+{
+ int id;
+ std::string firstname;
+ std::string lastname;
+ std::string email;
+ std::string phone;
+ std::string username;
+ std::string password;
+};
- struct LoginResult
- {
- int userId;
- std::string username;
- std::string accessToken;
- std::string tokenType;
- std::string message;
- int expiration;
- };
+struct PassSec
+{
+ int id;
+ std::string hashPassword;
+ std::string salt;
+ int userId;
+};
- struct User
- {
- int id;
- std::string firstname;
- std::string lastname;
- std::string email;
- std::string phone;
- std::string username;
- std::string password;
- };
+struct AuthCredentials
+{
+ std::string domain;
+ std::string apiIdentifier;
+ std::string clientId;
+ std::string clientSecret;
+ std::string uri;
+ std::string endpoint;
+};
- struct PassSec
- {
- std::string hashPassword;
- std::string salt;
- };
+struct DatabaseConnection
+{
+ std::string server;
+ std::string username;
+ std::string password;
+ std::string database;
+};
- struct AuthCredentials
- {
- std::string domain;
- std::string apiIdentifier;
- std::string clientId;
- std::string clientSecret;
- std::string uri;
- std::string endpoint;
- };
+struct BinaryPath
+{
+ BinaryPath() = default;
+ BinaryPath(const char *p) : path(std::move(p)) { }
+ BinaryPath(std::string& p) : path(p) { }
+ BinaryPath(const std::string& p) : path(p) { }
- struct DatabaseConnection
- {
- std::string server;
- std::string username;
- std::string password;
- std::string database;
- };
-
- struct BinaryPath
- {
- BinaryPath() = default;
- BinaryPath(const char *p) : path(std::move(p)) { }
- BinaryPath(std::string& p) : path(p) { }
- BinaryPath(const std::string& p) : path(p) { }
-
- std::string path;
- };
+ std::string path;
+};
}
#endif
diff --git a/include/type/SaltFilter.h b/include/type/SaltFilter.h
index b3efa14..b2e6ab5 100644
--- a/include/type/SaltFilter.h
+++ b/include/type/SaltFilter.h
@@ -5,7 +5,8 @@ namespace type {
enum class SaltFilter
{
id = 0,
- salt
+ salt,
+ userId
};
}
diff --git a/include/utility/MetadataRetriever.h b/include/utility/MetadataRetriever.h
index 215fe08..ebc539e 100644
--- a/include/utility/MetadataRetriever.h
+++ b/include/utility/MetadataRetriever.h
@@ -6,22 +6,21 @@
#include "model/Models.h"
-namespace utility
+namespace utility {
+class MetadataRetriever
{
- class MetadataRetriever
- {
- public:
- model::Song retrieveMetadata(std::string&);
+public:
+ model::Song retrieveMetadata(std::string&);
- model::Cover updateCoverArt(const model::Song&, model::Cover&, const std::string&);
- model::Cover applyStockCoverArt(const model::Song&, model::Cover&, const std::string&);
- model::Cover applyCoverArt(const model::Song&, model::Cover&);
+ model::Cover updateCoverArt(const model::Song&, model::Cover&, const std::string&);
+ model::Cover applyStockCoverArt(const model::Song&, model::Cover&, const std::string&);
+ model::Cover applyCoverArt(const model::Song&, model::Cover&);
- bool songContainsCoverArt(const model::Song&);
+ bool songContainsCoverArt(const model::Song&);
- void updateMetadata(model::Song&, const model::Song&);
- private:
- };
+ void updateMetadata(model::Song&, const model::Song&);
+private:
+};
}
#endif
diff --git a/include/utility/PasswordEncryption.h b/include/utility/PasswordEncryption.h
index e5662db..186ad37 100644
--- a/include/utility/PasswordEncryption.h
+++ b/include/utility/PasswordEncryption.h
@@ -6,12 +6,14 @@
#include "model/Models.h"
namespace utility {
- class PasswordEncryption {
- public:
- model::PassSec hashPassword(const model::User&);
- private:
- int saltSize();
- };
+class PasswordEncryption {
+public:
+ model::PassSec hashPassword(const model::User&);
+
+ bool isPasswordValid(const model::User&, const model::PassSec&);
+private:
+ int saltSize();
+};
}
#endif
diff --git a/src/database/UserRepository.cpp b/src/database/UserRepository.cpp
index 6b5574a..dc867a7 100644
--- a/src/database/UserRepository.cpp
+++ b/src/database/UserRepository.cpp
@@ -38,6 +38,8 @@ model::User UserRepository::retrieveUserRecord(model::User& user,
break;
}
+ qry << " LIMIT 1";
+
const auto query = qry.str();
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, params);
@@ -51,7 +53,86 @@ model::User UserRepository::retrieveUserRecord(model::User& user,
return user;
}
+model::PassSec UserRepository::retrieverUserSaltRecord(model::PassSec& userSec, type::SaltFilter filter)
+{
+ std::stringstream qry;
+ auto conn = setupMysqlConnection();
+ auto stmt = mysql_stmt_init(conn);
+ qry << "SELECT * FROM Salt WHERE ";
+
+ MYSQL_BIND params[1];
+ std::memset(params, 0, sizeof(params));
+
+ switch (filter) {
+ case type::SaltFilter::userId:
+ qry << "UserId = ?";
+
+ params[0].buffer_type = MYSQL_TYPE_LONG;
+ params[0].buffer = (char*)&userSec.userId;
+ params[0].length = 0;
+ params[0].is_null = 0;
+ break;
+ default:
+ break;
+ }
+
+ qry << " LIMIT 1";
+
+ const auto query = qry.str();
+ auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
+ status = mysql_stmt_bind_param(stmt, params);
+ status = mysql_stmt_execute(stmt);
+
+ userSec = parseSaltRecord(stmt);
+
+ mysql_stmt_close(stmt);
+ mysql_close(conn);
+
+ return userSec;
+}
+
+
+bool UserRepository::doesUserRecordExist(const model::User& user, type::UserFilter filter)
+{
+ std::stringstream qry;
+ auto conn = setupMysqlConnection();
+ auto stmt = mysql_stmt_init(conn);
+
+ qry << "SELECT * FROM User WHERE ";
+
+ MYSQL_BIND params[1];
+ std::memset(params, 0, sizeof(params));
+
+ auto userLength = user.username.size();
+ switch (filter) {
+ case type::UserFilter::username:
+ qry << "Username = ?";
+
+ params[0].buffer_type = MYSQL_TYPE_STRING;
+ params[0].buffer = (char*)user.username.c_str();
+ params[0].length = &userLength;
+ params[0].is_null = 0;
+ break;
+ default:
+ break;
+ }
+
+ qry << " LIMIT 1";
+
+ const auto query = qry.str();
+ auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
+ status = mysql_stmt_bind_param(stmt, params);
+ status = mysql_stmt_execute(stmt);
+
+ mysql_stmt_store_result(stmt);
+ const auto rowCount = mysql_stmt_num_rows(stmt);
+
+ mysql_stmt_close(stmt);
+ mysql_close(conn);
+
+ return (rowCount > 0) ? true : false;
+}
void UserRepository::saveUserRecord(const model::User& user)
{
std::cout << "inserting user record" << std::endl;
@@ -75,6 +156,28 @@ void UserRepository::saveUserRecord(const model::User& user)
mysql_close(conn);
}
+void UserRepository::saveUserSalt(const model::PassSec& userSec)
+{
+ std::cout << "inserting user salt record" << std::endl;
+ auto conn = setupMysqlConnection();
+ auto stmt = mysql_stmt_init(conn);
+
+ std::stringstream qry;
+ qry << "INSERT INTO Salt(Salt, UserId) VALUES(?,?)";
+
+ const auto query = qry.str();
+
+ auto sizes = fetchSaltLengths(userSec);
+ auto values = insertSaltValues(userSec, sizes);
+
+ auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
+ status = mysql_stmt_bind_param(stmt, values.get());
+ status = mysql_stmt_execute(stmt);
+
+ mysql_stmt_close(stmt);
+ mysql_close(conn);
+}
+
std::shared_ptr UserRepository::insertUserValues(const model::User& user,
std::shared_ptr lengths)
@@ -114,6 +217,22 @@ std::shared_ptr UserRepository::insertUserValues(const model::User&
return values;
}
+std::shared_ptr UserRepository::insertSaltValues(const model::PassSec& passSec,
+ std::shared_ptr lengths)
+{
+ std::shared_ptr values((MYSQL_BIND*) std::calloc(6, sizeof(MYSQL_BIND)));
+
+ values.get()[0].buffer_type = MYSQL_TYPE_STRING;
+ values.get()[0].buffer = (char*)passSec.hashPassword.c_str();
+ values.get()[0].length = &(lengths->saltLength);
+
+ values.get()[1].buffer_type = MYSQL_TYPE_LONG;
+ values.get()[1].buffer = (char*)&passSec.userId;
+
+
+ return values;
+}
+
std::shared_ptr UserRepository::valueBind(model::User& user,
std::tuple& us)
{
@@ -150,6 +269,24 @@ std::shared_ptr UserRepository::valueBind(model::User& user,
return values;
}
+std::shared_ptr UserRepository::saltValueBind(model::PassSec& userSalt, char *salt)
+{
+ std::shared_ptr values((MYSQL_BIND*) std::calloc(3, sizeof(MYSQL_BIND)));
+ constexpr auto strLen = 1024;
+
+ values.get()[0].buffer_type = MYSQL_TYPE_LONG;
+ values.get()[0].buffer = (char*)&userSalt.id;
+
+ values.get()[1].buffer_type = MYSQL_TYPE_STRING;
+ values.get()[1].buffer = (char*)salt;
+ values.get()[1].buffer_length = strLen;
+
+ values.get()[2].buffer_type = MYSQL_TYPE_LONG;
+ values.get()[2].buffer = (char*)&userSalt.userId;
+
+ return values;
+}
+
std::shared_ptr UserRepository::fetchUserLengths(const model::User& user)
{
auto userLen = std::make_shared();
@@ -163,6 +300,14 @@ std::shared_ptr UserRepository::fetchUserLengths(co
return userLen;
}
+std::shared_ptr UserRepository::fetchSaltLengths(const model::PassSec& passSec)
+{
+ auto saltLen = std::make_shared();
+ saltLen->saltLength = passSec.salt.size();
+
+ return saltLen;
+}
+
std::tuple UserRepository::fetchUV()
{
@@ -195,4 +340,18 @@ model::User UserRepository::parseRecord(MYSQL_STMT *stmt)
return user;
}
+
+model::PassSec UserRepository::parseSaltRecord(MYSQL_STMT* stmt)
+{
+ model::PassSec userSalt;
+ char saltKey[1024];
+
+ auto bindedValues = saltValueBind(userSalt, saltKey);
+ auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
+ status = mysql_stmt_fetch(stmt);
+
+ userSalt.salt = saltKey;
+
+ return userSalt;
+}
}
diff --git a/src/manager/UserManager.cpp b/src/manager/UserManager.cpp
index 8c7940e..650f01c 100644
--- a/src/manager/UserManager.cpp
+++ b/src/manager/UserManager.cpp
@@ -2,26 +2,35 @@
#include "database/UserRepository.h"
#include "type/UserFilter.h"
+#include "type/SaltFilter.h"
#include "utility/MetadataRetriever.h"
#include "utility/PasswordEncryption.h"
namespace manager {
UserManager::UserManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
-
-model::User UserManager::userDtoConv(dto::UserDto::ObjectWrapper& userDto)
+bool UserManager::doesUserExist(const model::User& user)
{
- model::User user;
+ database::UserRepository userRepo(m_bConf);
- user.id = (userDto->userId.getPtr() == nullptr) ? 0 : userDto->userId->getValue();
- user.firstname = (userDto->firstname == nullptr) ? "" : userDto->firstname->c_str();
- user.lastname = (userDto->lastname == nullptr) ? "" : userDto->lastname->c_str();
- user.phone = (userDto->phone == nullptr) ? "" : userDto->phone->c_str();
- user.email = (userDto->email == nullptr) ? "" : userDto->email->c_str();
- user.username = (userDto->username == nullptr) ? "" : userDto->username->c_str();
- user.password = (userDto->password == nullptr) ? "" : userDto->password->c_str();
+ return userRepo.doesUserRecordExist(user, type::UserFilter::username);
+}
- return user;
+bool UserManager::validatePassword(const model::User& user)
+{
+ database::UserRepository userRepo(m_bConf);
+ model::User usr;
+ usr.username = user.username;
+ usr = userRepo.retrieveUserRecord(usr, type::UserFilter::username);
+
+ model::PassSec userSec;
+ userSec.userId = usr.id;
+ userSec = userRepo.retrieverUserSaltRecord(userSec, type::SaltFilter::userId);
+ userSec.hashPassword = usr.password;
+
+ utility::PasswordEncryption passEnc;
+
+ return passEnc.isPasswordValid(user, userSec);
}
@@ -53,7 +62,26 @@ void UserManager::registerUser(model::User& user)
std::cout << usr.username << std::endl;
usr = usrRepo.retrieveUserRecord(usr, type::UserFilter::username);
+ hashed.hashPassword = usr.password;
+ hashed.userId = usr.id;
+
+ usrRepo.saveUserSalt(hashed);
printUser(usr);
}
+
+model::User UserManager::userDtoConv(dto::UserDto::ObjectWrapper& userDto)
+{
+ model::User user;
+
+ user.id = (userDto->userId.getPtr() == nullptr) ? 0 : userDto->userId->getValue();
+ user.firstname = (userDto->firstname == nullptr) ? "" : userDto->firstname->c_str();
+ user.lastname = (userDto->lastname == nullptr) ? "" : userDto->lastname->c_str();
+ user.phone = (userDto->phone == nullptr) ? "" : userDto->phone->c_str();
+ user.email = (userDto->email == nullptr) ? "" : userDto->email->c_str();
+ user.username = (userDto->username == nullptr) ? "" : userDto->username->c_str();
+ user.password = (userDto->password == nullptr) ? "" : userDto->password->c_str();
+
+ return user;
+}
}
diff --git a/src/utility/PasswordEncryption.cpp b/src/utility/PasswordEncryption.cpp
index 74af613..a905fa3 100644
--- a/src/utility/PasswordEncryption.cpp
+++ b/src/utility/PasswordEncryption.cpp
@@ -28,6 +28,17 @@ model::PassSec PasswordEncryption::hashPassword(const model::User& user)
return passSec;
}
+
+bool PasswordEncryption::isPasswordValid(const model::User& user, const model::PassSec& userSalt)
+{
+ std::unique_ptr passwordHashed(new char[BCRYPT_HASHSIZE]);
+
+ bcrypt_hashpw(user.password.c_str(), userSalt.salt.c_str(), passwordHashed.get());
+
+ return (userSalt.hashPassword.compare(passwordHashed.get()) == 0) ? true : false;
+}
+
+
int PasswordEncryption::saltSize()
{
constexpr auto size = 10000;