added user authentication and updated README.md
This commit is contained in:
@@ -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.
|
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
|
```Json
|
||||||
"Auth0": {
|
"domain": "[domain].auth0.com",
|
||||||
"Domain": "[domain].auth0.com",
|
"api_identifier": "https://[identifier]/api",
|
||||||
"ApiIdentifier": "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.
|
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.
|
||||||
@@ -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:
|
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
|
```Json
|
||||||
{
|
{
|
||||||
"root_music_path": "/home/dev/null/music/",
|
"root_music_path": "/dev/null/music/",
|
||||||
"temp_root_path": "/home/dev/null/music/temp/",
|
"temp_root_path": "/dev/null/music/temp/",
|
||||||
"cover_root_path": "/home/dev/null/music/coverArt/",
|
"cover_root_path": "/dev/null/music/coverArt/",
|
||||||
"archive_root_path": "/home/dev/null/music/archive/"
|
"archive_root_path": "/dev/null/music/archive/"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
* `root_music_path` - Where music will be stored in the following convention: *`Artist/Album/Songs`*
|
* `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
|
### 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
|
```Json
|
||||||
{
|
{
|
||||||
|
|
||||||
"ConnectionStrings": {
|
"server": "localhost",
|
||||||
"DefaultConnection": "Server=localhost;Database=my_db;Uid=admin;Pwd=toughpassword;"
|
"database": "my_db",
|
||||||
}
|
"username": "admin",
|
||||||
|
"password": "toughpassword"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
* 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
|
||||||
* Uid - Username
|
* user - 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.
|
||||||
|
|
||||||
### Migrations
|
### Database
|
||||||
|
|
||||||
Prior to starting the API, the Migrations must be applied. There are 6 tables with migrations being applied and thy are:
|
Prior to starting the API, the database must be created. The following tables are required:
|
||||||
* Users
|
* User
|
||||||
* Song
|
* Song
|
||||||
* Album
|
* Album
|
||||||
* Artist
|
* Artist
|
||||||
* Year
|
* Year
|
||||||
* Genre
|
* 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
|
```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<sup>*</sup>:
|
|
||||||
```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.
|
|
||||||
|
|
||||||
<sup>*</sup> 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
|
## Building and Running
|
||||||
```
|
```
|
||||||
git clone --recursive https://github.com/kdeng00/icarus
|
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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
conan install ..
|
||||||
cmake ..
|
cmake ..
|
||||||
|
make
|
||||||
bin/icarus
|
bin/icarus
|
||||||
```
|
```
|
||||||
Runs the server on localhost port 5002
|
Runs the server on localhost port 5002
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
|
|
||||||
#include "dto/LoginResultDto.hpp"
|
#include "dto/LoginResultDto.hpp"
|
||||||
#include "manager/TokenManager.h"
|
#include "manager/TokenManager.h"
|
||||||
|
#include "manager/UserManager.h"
|
||||||
#include "model/Models.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:
|
public:
|
||||||
@@ -31,11 +31,26 @@ namespace controller
|
|||||||
ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
|
ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
|
||||||
OATPP_LOGI("icarus", "logging in");
|
OATPP_LOGI("icarus", "logging in");
|
||||||
|
|
||||||
|
manager::UserManager usrMgr(m_bConf);
|
||||||
|
auto user = manager::UserManager::userDtoConv(usr);
|
||||||
|
|
||||||
|
if (!usrMgr.doesUserExist(user)) {
|
||||||
|
std::cout << "user does not exist" << std::endl;
|
||||||
|
return createResponse(Status::CODE_401, "invalid credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
manager::TokenManager tok;
|
||||||
auto token = tok.retrieveToken(m_bConf);
|
auto token = tok.retrieveToken(m_bConf);
|
||||||
|
|
||||||
auto logRes = dto::LoginResultDto::createShared();
|
auto logRes = dto::LoginResultDto::createShared();
|
||||||
logRes->id = 0; // TODO: change this later on to something meaningful
|
logRes->id = 0; // TODO: change this later on to something meaningful or remove it
|
||||||
logRes->username = usr->username->c_str();
|
logRes->username = usr->username->c_str();
|
||||||
logRes->token = token.accessToken.c_str();
|
logRes->token = token.accessToken.c_str();
|
||||||
logRes->token_type = token.tokenType.c_str();
|
logRes->token_type = token.tokenType.c_str();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "database/BaseRepository.h"
|
#include "database/BaseRepository.h"
|
||||||
#include "model/Models.h"
|
#include "model/Models.h"
|
||||||
|
#include "type/SaltFilter.h"
|
||||||
#include "type/UserFilter.h"
|
#include "type/UserFilter.h"
|
||||||
|
|
||||||
namespace database {
|
namespace database {
|
||||||
@@ -15,10 +16,16 @@ namespace database {
|
|||||||
UserRepository(const model::BinaryPath&);
|
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);
|
||||||
|
|
||||||
|
bool doesUserRecordExist(const model::User&, type::UserFilter);
|
||||||
|
|
||||||
void saveUserRecord(const model::User&);
|
void saveUserRecord(const model::User&);
|
||||||
|
void saveUserSalt(const model::PassSec&);
|
||||||
private:
|
private:
|
||||||
struct UserLengths;
|
struct UserLengths;
|
||||||
|
struct SaltLengths;
|
||||||
|
|
||||||
struct UserLengths
|
struct UserLengths
|
||||||
{
|
{
|
||||||
unsigned long firstnameLength;
|
unsigned long firstnameLength;
|
||||||
@@ -28,14 +35,22 @@ namespace database {
|
|||||||
unsigned long usernameLength;
|
unsigned long usernameLength;
|
||||||
unsigned long passwordLength;
|
unsigned long passwordLength;
|
||||||
};
|
};
|
||||||
|
struct SaltLengths
|
||||||
|
{
|
||||||
|
unsigned long saltLength;
|
||||||
|
};
|
||||||
|
|
||||||
std::shared_ptr<MYSQL_BIND> insertUserValues(const model::User&, std::shared_ptr<UserLengths>);
|
std::shared_ptr<MYSQL_BIND> insertUserValues(const model::User&, std::shared_ptr<UserLengths>);
|
||||||
|
std::shared_ptr<MYSQL_BIND> insertSaltValues(const model::PassSec&, std::shared_ptr<SaltLengths>);
|
||||||
std::shared_ptr<MYSQL_BIND> valueBind(model::User&, std::tuple<char*, char*, char*, char*, char*, char*>&);
|
std::shared_ptr<MYSQL_BIND> valueBind(model::User&, std::tuple<char*, char*, char*, char*, char*, char*>&);
|
||||||
|
std::shared_ptr<MYSQL_BIND> saltValueBind(model::PassSec&, char*);
|
||||||
std::shared_ptr<UserLengths> fetchUserLengths(const model::User&);
|
std::shared_ptr<UserLengths> fetchUserLengths(const model::User&);
|
||||||
|
std::shared_ptr<SaltLengths> fetchSaltLengths(const model::PassSec&);
|
||||||
|
|
||||||
std::tuple<char*, char*, char*, char*, char*, char*> fetchUV();
|
std::tuple<char*, char*, char*, char*, char*, char*> fetchUV();
|
||||||
|
|
||||||
model::User parseRecord(MYSQL_STMT*);
|
model::User parseRecord(MYSQL_STMT*);
|
||||||
|
model::PassSec parseSaltRecord(MYSQL_STMT*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,19 @@
|
|||||||
#include "dto/LoginResultDto.hpp"
|
#include "dto/LoginResultDto.hpp"
|
||||||
#include "model/Models.h"
|
#include "model/Models.h"
|
||||||
|
|
||||||
namespace manager
|
namespace manager {
|
||||||
{
|
|
||||||
class UserManager
|
class UserManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UserManager(const model::BinaryPath&);
|
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 printUser(const model::User&);
|
||||||
void registerUser(model::User&);
|
void registerUser(model::User&);
|
||||||
|
|
||||||
|
static model::User userDtoConv(dto::UserDto::ObjectWrapper&);
|
||||||
private:
|
private:
|
||||||
model::BinaryPath m_bConf;
|
model::BinaryPath m_bConf;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace model
|
namespace model {
|
||||||
{
|
|
||||||
struct Song
|
struct Song
|
||||||
{
|
{
|
||||||
Song() = default;
|
Song() = default;
|
||||||
@@ -129,8 +128,10 @@ namespace model
|
|||||||
|
|
||||||
struct PassSec
|
struct PassSec
|
||||||
{
|
{
|
||||||
|
int id;
|
||||||
std::string hashPassword;
|
std::string hashPassword;
|
||||||
std::string salt;
|
std::string salt;
|
||||||
|
int userId;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AuthCredentials
|
struct AuthCredentials
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ namespace type {
|
|||||||
enum class SaltFilter
|
enum class SaltFilter
|
||||||
{
|
{
|
||||||
id = 0,
|
id = 0,
|
||||||
salt
|
salt,
|
||||||
|
userId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
|
|
||||||
#include "model/Models.h"
|
#include "model/Models.h"
|
||||||
|
|
||||||
namespace utility
|
namespace utility {
|
||||||
{
|
|
||||||
class MetadataRetriever
|
class MetadataRetriever
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace utility {
|
|||||||
class PasswordEncryption {
|
class PasswordEncryption {
|
||||||
public:
|
public:
|
||||||
model::PassSec hashPassword(const model::User&);
|
model::PassSec hashPassword(const model::User&);
|
||||||
|
|
||||||
|
bool isPasswordValid(const model::User&, const model::PassSec&);
|
||||||
private:
|
private:
|
||||||
int saltSize();
|
int saltSize();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ model::User UserRepository::retrieveUserRecord(model::User& user,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qry << " LIMIT 1";
|
||||||
|
|
||||||
const auto query = qry.str();
|
const auto query = qry.str();
|
||||||
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
|
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
|
||||||
status = mysql_stmt_bind_param(stmt, params);
|
status = mysql_stmt_bind_param(stmt, params);
|
||||||
@@ -51,7 +53,86 @@ model::User UserRepository::retrieveUserRecord(model::User& user,
|
|||||||
return 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)
|
void UserRepository::saveUserRecord(const model::User& user)
|
||||||
{
|
{
|
||||||
std::cout << "inserting user record" << std::endl;
|
std::cout << "inserting user record" << std::endl;
|
||||||
@@ -75,6 +156,28 @@ void UserRepository::saveUserRecord(const model::User& user)
|
|||||||
mysql_close(conn);
|
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<MYSQL_BIND> UserRepository::insertUserValues(const model::User& user,
|
std::shared_ptr<MYSQL_BIND> UserRepository::insertUserValues(const model::User& user,
|
||||||
std::shared_ptr<UserRepository::UserLengths> lengths)
|
std::shared_ptr<UserRepository::UserLengths> lengths)
|
||||||
@@ -114,6 +217,22 @@ std::shared_ptr<MYSQL_BIND> UserRepository::insertUserValues(const model::User&
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MYSQL_BIND> UserRepository::insertSaltValues(const model::PassSec& passSec,
|
||||||
|
std::shared_ptr<UserRepository::SaltLengths> lengths)
|
||||||
|
{
|
||||||
|
std::shared_ptr<MYSQL_BIND> 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<MYSQL_BIND> UserRepository::valueBind(model::User& user,
|
std::shared_ptr<MYSQL_BIND> UserRepository::valueBind(model::User& user,
|
||||||
std::tuple<char*, char*, char*, char*, char*, char*>& us)
|
std::tuple<char*, char*, char*, char*, char*, char*>& us)
|
||||||
{
|
{
|
||||||
@@ -150,6 +269,24 @@ std::shared_ptr<MYSQL_BIND> UserRepository::valueBind(model::User& user,
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MYSQL_BIND> UserRepository::saltValueBind(model::PassSec& userSalt, char *salt)
|
||||||
|
{
|
||||||
|
std::shared_ptr<MYSQL_BIND> 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::UserLengths> UserRepository::fetchUserLengths(const model::User& user)
|
std::shared_ptr<UserRepository::UserLengths> UserRepository::fetchUserLengths(const model::User& user)
|
||||||
{
|
{
|
||||||
auto userLen = std::make_shared<UserLengths>();
|
auto userLen = std::make_shared<UserLengths>();
|
||||||
@@ -163,6 +300,14 @@ std::shared_ptr<UserRepository::UserLengths> UserRepository::fetchUserLengths(co
|
|||||||
return userLen;
|
return userLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UserRepository::SaltLengths> UserRepository::fetchSaltLengths(const model::PassSec& passSec)
|
||||||
|
{
|
||||||
|
auto saltLen = std::make_shared<SaltLengths>();
|
||||||
|
saltLen->saltLength = passSec.salt.size();
|
||||||
|
|
||||||
|
return saltLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::tuple<char*, char*, char*, char*, char*, char*> UserRepository::fetchUV()
|
std::tuple<char*, char*, char*, char*, char*, char*> UserRepository::fetchUV()
|
||||||
{
|
{
|
||||||
@@ -195,4 +340,18 @@ model::User UserRepository::parseRecord(MYSQL_STMT *stmt)
|
|||||||
|
|
||||||
return user;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-11
@@ -2,26 +2,35 @@
|
|||||||
|
|
||||||
#include "database/UserRepository.h"
|
#include "database/UserRepository.h"
|
||||||
#include "type/UserFilter.h"
|
#include "type/UserFilter.h"
|
||||||
|
#include "type/SaltFilter.h"
|
||||||
#include "utility/MetadataRetriever.h"
|
#include "utility/MetadataRetriever.h"
|
||||||
#include "utility/PasswordEncryption.h"
|
#include "utility/PasswordEncryption.h"
|
||||||
|
|
||||||
namespace manager {
|
namespace manager {
|
||||||
UserManager::UserManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
|
UserManager::UserManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
|
||||||
|
|
||||||
|
bool UserManager::doesUserExist(const model::User& user)
|
||||||
model::User UserManager::userDtoConv(dto::UserDto::ObjectWrapper& userDto)
|
|
||||||
{
|
{
|
||||||
model::User user;
|
database::UserRepository userRepo(m_bConf);
|
||||||
|
|
||||||
user.id = (userDto->userId.getPtr() == nullptr) ? 0 : userDto->userId->getValue();
|
return userRepo.doesUserRecordExist(user, type::UserFilter::username);
|
||||||
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;
|
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;
|
std::cout << usr.username << std::endl;
|
||||||
usr = usrRepo.retrieveUserRecord(usr, type::UserFilter::username);
|
usr = usrRepo.retrieveUserRecord(usr, type::UserFilter::username);
|
||||||
|
hashed.hashPassword = usr.password;
|
||||||
|
hashed.userId = usr.id;
|
||||||
|
|
||||||
|
usrRepo.saveUserSalt(hashed);
|
||||||
printUser(usr);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,17 @@ model::PassSec PasswordEncryption::hashPassword(const model::User& user)
|
|||||||
return passSec;
|
return passSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PasswordEncryption::isPasswordValid(const model::User& user, const model::PassSec& userSalt)
|
||||||
|
{
|
||||||
|
std::unique_ptr<char[]> 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()
|
int PasswordEncryption::saltSize()
|
||||||
{
|
{
|
||||||
constexpr auto size = 10000;
|
constexpr auto size = 10000;
|
||||||
|
|||||||
Reference in New Issue
Block a user