some changes

This commit is contained in:
kdeng00
2019-10-13 11:29:40 -04:00
parent 32ed611112
commit 024134b801
14 changed files with 204 additions and 132 deletions
+2
View File
@@ -15,6 +15,7 @@ set(SOURCES
src/database/SongRepository.cpp src/database/SongRepository.cpp
src/database/UserRepository.cpp src/database/UserRepository.cpp
src/database/YearRepository.cpp src/database/YearRepository.cpp
src/dto/conversion/DtoConversions.cpp
src/Main.cpp src/Main.cpp
src/manager/AlbumManager.cpp src/manager/AlbumManager.cpp
src/manager/ArtistManager.cpp src/manager/ArtistManager.cpp
@@ -56,6 +57,7 @@ set(HEADERS
include/dto/LoginResultDto.hpp include/dto/LoginResultDto.hpp
include/dto/SongDto.hpp include/dto/SongDto.hpp
include/dto/YearDto.hpp include/dto/YearDto.hpp
include/dto/conversion/DtoConversions.h
include/manager/AlbumManager.h include/manager/AlbumManager.h
include/manager/ArtistManager.h include/manager/ArtistManager.h
include/manager/CoverArtManager.h include/manager/CoverArtManager.h
+8 -16
View File
@@ -10,13 +10,13 @@
#include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/web/server/api/ApiController.hpp"
#include "dto/LoginResultDto.hpp" #include "dto/LoginResultDto.hpp"
#include "dto/conversion/DtoConversions.h"
#include "manager/TokenManager.h" #include "manager/TokenManager.h"
#include "manager/UserManager.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:
LoginController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) LoginController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), exe_path(p)
@@ -32,29 +32,21 @@ public:
OATPP_LOGI("icarus", "logging in"); OATPP_LOGI("icarus", "logging in");
manager::UserManager usrMgr(m_bConf); manager::UserManager usrMgr(m_bConf);
auto user = manager::UserManager::userDtoConv(usr); auto user = dto::conversion::DtoConversions::toUser(usr);
if (!usrMgr.doesUserExist(user)) { if (!usrMgr.doesUserExist(user) || !usrMgr.validatePassword(user)) {
auto logRes = dto::LoginResultDto::createShared();
logRes->message = "invalid credentials";
std::cout << "user does not exist" << std::endl; std::cout << "user does not exist" << std::endl;
return createResponse(Status::CODE_401, "invalid credentials"); return createDtoResponse(Status::CODE_401, logRes);
} }
std::cout << "user exists" << std::endl; 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::conversion::DtoConversions::toLoginResultDto(user, token);
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"; logRes->message = "Successful";
return createDtoResponse(Status::CODE_200, logRes); return createDtoResponse(Status::CODE_200, logRes);
+9 -8
View File
@@ -9,13 +9,12 @@
#include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/web/server/api/ApiController.hpp"
#include "dto/LoginResultDto.hpp" #include "dto/LoginResultDto.hpp"
#include "dto/conversion/DtoConversions.h"
#include "manager/UserManager.h" #include "manager/UserManager.h"
#include "model/Models.h" #include "model/Models.h"
namespace controller namespace controller {
{ class RegisterController : public oatpp::web::server::api::ApiController {
class RegisterController : public oatpp::web::server::api::ApiController
{
public: public:
RegisterController(const model::BinaryPath& bConf, RegisterController(const model::BinaryPath& bConf,
OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) : OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) :
@@ -23,13 +22,15 @@ namespace controller
#include OATPP_CODEGEN_BEGIN(ApiController) #include OATPP_CODEGEN_BEGIN(ApiController)
ENDPOINT("POST", "api/v1/register", registerUser, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) { ENDPOINT("POST", "api/v1/register", registerUser,
BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
manager::UserManager usrMgr(m_bConf); manager::UserManager usrMgr(m_bConf);
auto user = usrMgr.userDtoConv(usr); auto user = dto::conversion::DtoConversions::toUser(usr);
usrMgr.registerUser(user); auto res = usrMgr.registerUser(user);
auto registerResult = dto::conversion::DtoConversions::toRegisterResultDto(res);
return createResponse(Status::CODE_200, "Reg"); return createDtoResponse(Status::CODE_200, registerResult);
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
+1 -2
View File
@@ -30,8 +30,7 @@
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)
+14 -6
View File
@@ -4,12 +4,12 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto #include "model/Models.h"
{
namespace dto {
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class LoginResultDto : public oatpp::data::mapping::type::Object class LoginResultDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(LoginResultDto, Object) DTO_INIT(LoginResultDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
@@ -20,8 +20,15 @@ namespace dto
DTO_FIELD(String, message); DTO_FIELD(String, message);
}; };
class UserDto : public oatpp::data::mapping::type::Object class RegisterResultDto : public oatpp::data::mapping::type::Object {
{ DTO_INIT(RegisterResultDto, Object)
DTO_FIELD(String, username);
DTO_FIELD(Boolean, registered);
DTO_FIELD(String, message);
};
class UserDto : public oatpp::data::mapping::type::Object {
DTO_INIT(UserDto, Object) DTO_INIT(UserDto, Object)
DTO_FIELD(Int32, userId); DTO_FIELD(Int32, userId);
@@ -34,6 +41,7 @@ namespace dto
}; };
#include OATPP_CODEGEN_END(DTO) #include OATPP_CODEGEN_END(DTO)
} }
#endif #endif
+4
View File
@@ -4,6 +4,8 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
#include "model/Models.h"
namespace dto namespace dto
{ {
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
@@ -24,6 +26,8 @@ namespace dto
}; };
#include OATPP_CODEGEN_END(DTO) #include OATPP_CODEGEN_END(DTO)
} }
#endif #endif
+22
View File
@@ -0,0 +1,22 @@
#ifndef DTOCONVERSIONS_H_
#define DTOCONVERSIONS_H_
#include "dto/LoginResultDto.hpp"
#include "dto/SongDto.hpp"
#include "model/Models.h"
namespace dto { namespace conversion {
class DtoConversions {
public:
static dto::LoginResultDto::ObjectWrapper toLoginResultDto(const model::User&, const model::Token&);
static dto::RegisterResultDto::ObjectWrapper toRegisterResultDto(
const model::RegisterResult&);
static model::Song toSong(dto::SongDto::ObjectWrapper&);
static model::User toUser(dto::UserDto::ObjectWrapper&);
};
}}
#endif
+3 -4
View File
@@ -14,12 +14,11 @@
#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::Token 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&);
@@ -34,7 +33,7 @@ private:
std::vector<std::string> extractScopes(const jwt::decoded_jwt&&); std::vector<std::string> extractScopes(const jwt::decoded_jwt&&);
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&&);
}; };
} }
+3 -5
View File
@@ -7,18 +7,16 @@
#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::RegisterResult registerUser(model::User&);
bool doesUserExist(const model::User&); bool doesUserExist(const model::User&);
bool validatePassword(const model::User&); bool validatePassword(const model::User&);
void printUser(const model::User&); void printUser(const model::User&);
void registerUser(model::User&);
static model::User userDtoConv(dto::UserDto::ObjectWrapper&);
private: private:
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
}; };
+34 -24
View File
@@ -5,8 +5,7 @@
#include <vector> #include <vector>
namespace model { namespace model {
struct Song struct Song {
{
Song() = default; Song() = default;
Song(const int id) : id(id) { } Song(const int id) : id(id) { }
@@ -28,8 +27,7 @@ struct Song
int yearId; int yearId;
}; };
struct Artist struct Artist {
{
Artist() = default; Artist() = default;
Artist(const Song& song) Artist(const Song& song)
{ {
@@ -42,8 +40,7 @@ struct Artist
std::string artist; std::string artist;
}; };
struct Album struct Album {
{
Album() = default; Album() = default;
Album(const Song& song) Album(const Song& song)
{ {
@@ -59,8 +56,7 @@ struct Album
std::vector<Song> songs; std::vector<Song> songs;
}; };
struct Genre struct Genre {
{
Genre() = default; Genre() = default;
Genre(const Song& song) Genre(const Song& song)
{ {
@@ -74,8 +70,7 @@ struct Genre
}; };
struct Year struct Year {
{
Year() = default; Year() = default;
Year(const Song& song) Year(const Song& song)
{ {
@@ -88,8 +83,7 @@ struct Year
int year; int year;
}; };
struct Cover struct Cover {
{
Cover() = default; Cover() = default;
Cover(const Song& song) Cover(const Song& song)
{ {
@@ -105,8 +99,22 @@ struct Cover
std::vector<unsigned char> data; std::vector<unsigned char> data;
}; };
struct LoginResult class Token {
{ public:
Token() = default;
Token(const std::string& accessToken) :
accessToken(accessToken) { }
Token(const std::string& accessToken, const std::string& tokenType,
const int expiration) :
accessToken(accessToken), tokenType(tokenType),
expiration(expiration) { }
std::string accessToken;
int expiration;
std::string tokenType;
};
struct LoginResult {
int userId; int userId;
std::string username; std::string username;
std::string accessToken; std::string accessToken;
@@ -115,8 +123,14 @@ struct LoginResult
int expiration; int expiration;
}; };
struct User class RegisterResult {
{ public:
std::string username;
bool registered;
std::string message;
};
struct User {
int id; int id;
std::string firstname; std::string firstname;
std::string lastname; std::string lastname;
@@ -126,16 +140,14 @@ struct User
std::string password; std::string password;
}; };
struct PassSec struct PassSec {
{
int id; int id;
std::string hashPassword; std::string hashPassword;
std::string salt; std::string salt;
int userId; int userId;
}; };
struct AuthCredentials struct AuthCredentials {
{
std::string domain; std::string domain;
std::string apiIdentifier; std::string apiIdentifier;
std::string clientId; std::string clientId;
@@ -144,16 +156,14 @@ struct AuthCredentials
std::string endpoint; std::string endpoint;
}; };
struct DatabaseConnection struct DatabaseConnection {
{
std::string server; std::string server;
std::string username; std::string username;
std::string password; std::string password;
std::string database; std::string database;
}; };
struct BinaryPath struct BinaryPath {
{
BinaryPath() = default; BinaryPath() = default;
BinaryPath(const char *p) : path(std::move(p)) { } BinaryPath(const char *p) : path(std::move(p)) { }
BinaryPath(std::string& p) : path(p) { } BinaryPath(std::string& p) : path(p) { }
+54
View File
@@ -0,0 +1,54 @@
#include "dto/conversion/DtoConversions.h"
namespace dto { namespace conversion {
dto::LoginResultDto::ObjectWrapper DtoConversions::toLoginResultDto(const model::User& user,
const model::Token& token) {
auto logRes = dto::LoginResultDto::createShared();
logRes->username = user.username.c_str();
logRes->token = token.accessToken.c_str();
logRes->token_type = token.tokenType.c_str();
logRes->expiration = token.expiration;
return logRes;
}
dto::RegisterResultDto::ObjectWrapper DtoConversions::toRegisterResultDto(
const model::RegisterResult& regRes) {
auto result = dto::RegisterResultDto::createShared();
result->message = regRes.message.c_str();
result->registered = regRes.registered;
result->username = regRes.username.c_str();
return result;
}
model::Song DtoConversions::toSong(dto::SongDto::ObjectWrapper& songDto) {
model::Song song;
song.id = (songDto->id.getPtr() == nullptr) ? 0 : songDto->id->getValue();
song.title = (songDto->title == nullptr) ? "" : songDto->title->c_str();
song.album = (songDto->album == nullptr) ? "" : songDto->album->c_str();
song.artist = (songDto->artist == nullptr) ? "" : songDto->artist->c_str();
song.genre = (songDto->genre == nullptr) ? "" : songDto->genre->c_str();
song.year = (songDto->year.getPtr() == nullptr) ? 0 : songDto->year->getValue();
song.track = (songDto->track.getPtr() == nullptr) ? 0 : songDto->track->getValue();
song.disc = (songDto->disc.getPtr() == nullptr) ? 0 : songDto->disc->getValue();
return song;
}
model::User DtoConversions::toUser(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;
}
}}
+1 -1
View File
@@ -308,8 +308,8 @@ void manager::SongManager::saveMisc(model::Song& song)
auto musicRootPath = pathConfigContent["root_music_path"].get<std::string>(); auto musicRootPath = pathConfigContent["root_music_path"].get<std::string>();
auto cov = covMgr.saveCover(song); auto cov = covMgr.saveCover(song);
const auto songPath = createSongPath(song);
auto songPath = createSongPath(song);
if (fs::exists(songPath)) { if (fs::exists(songPath)) {
std::cout << "deleting old song with the same metadata" << std::endl; std::cout << "deleting old song with the same metadata" << std::endl;
fs::remove(songPath); fs::remove(songPath);
+10 -16
View File
@@ -19,28 +19,21 @@ TokenManager::TokenManager()
} }
model::LoginResult TokenManager::retrieveToken(const model::BinaryPath& bConf) model::Token TokenManager::retrieveToken(const model::BinaryPath& bConf)
{ {
auto cred = parseAuthCredentials(bConf); auto cred = parseAuthCredentials(bConf);
auto reqObj = createTokenBody(cred); auto reqObj = createTokenBody(cred);
std::string uri{cred.uri}; std::string uri{cred.uri};
uri.append("/"); uri.append("/");
uri.append(cred.endpoint); uri.append(cred.endpoint);
auto r = cpr::Post(cpr::Url{uri}, auto r = sendRequest(uri, reqObj);
cpr::Body{reqObj.dump()},
cpr::Header{{"Content-Type", "application/json"},
{"Connection", "keep-alive"}});
auto postRes = nlohmann::json::parse(r.text); auto postRes = nlohmann::json::parse(r.text);
model::LoginResult lr; model::Token lr(std::move(postRes["access_token"].get<std::string>()),
lr.accessToken = postRes["access_token"].get<std::string>(); std::move(postRes["token_type"].get<std::string>()),
lr.tokenType = postRes["token_type"].get<std::string>(); postRes["expires_in"].get<int>());
lr.expiration = postRes["expires_in"].get<int>();
return lr; return lr;
} }
@@ -129,6 +122,7 @@ nlohmann::json TokenManager::createTokenBody(const model::AuthCredentials& auth)
} }
[[depreacted("use the other function with the same name")]]
model::AuthCredentials TokenManager::parseAuthCredentials(std::string_view path) model::AuthCredentials TokenManager::parseAuthCredentials(std::string_view path)
{ {
auto exe_path = DirectoryManager::configPath(path); auto exe_path = DirectoryManager::configPath(path);
@@ -165,7 +159,7 @@ std::vector<std::string> TokenManager::extractScopes(const jwt::decoded_jwt&& de
{ {
std::vector<std::string> scopes; std::vector<std::string> scopes;
for (auto d : decoded.get_payload_claims()) { for (auto& d : decoded.get_payload_claims()) {
if (d.first.compare("scope") == 0) { if (d.first.compare("scope") == 0) {
std::cout << "found scope" << std::endl; std::cout << "found scope" << std::endl;
std::string allScopes(d.second.to_json().get<std::string>()); std::string allScopes(d.second.to_json().get<std::string>());
@@ -188,7 +182,7 @@ std::pair<bool, std::vector<std::string>> TokenManager::fetchAuthHeader(const st
bool foundBearer = false; bool foundBearer = false;
if (std::any_of(authHeader.begin(), authHeader.end(), if (std::any_of(authHeader.begin(), authHeader.end(),
[](std::string word) { [&](std::string_view word) {
return (word.compare("Bearer") == 0); return (word.compare("Bearer") == 0);
})) { })) {
std::cout << "Bearer found" << std::endl; std::cout << "Bearer found" << std::endl;
@@ -199,11 +193,11 @@ std::pair<bool, std::vector<std::string>> TokenManager::fetchAuthHeader(const st
} }
bool TokenManager::tokenSupportsScope(const std::vector<std::string> scopes, bool TokenManager::tokenSupportsScope(const std::vector<std::string>& scopes,
const std::string&& scope) 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_view foundScope) {
return (foundScope.compare(scope) == 0); return (foundScope.compare(scope) == 0);
}); });
} }
+38 -49
View File
@@ -9,45 +9,10 @@
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)
{
database::UserRepository userRepo(m_bConf);
return userRepo.doesUserRecordExist(user, type::UserFilter::username); model::RegisterResult UserManager::registerUser(model::User& user) {
} model::RegisterResult result;
result.username = user.username;
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);
}
void UserManager::printUser(const model::User& user)
{
std::cout << "\nuser info" << std::endl;
std::cout << "id: " << user.id << std::endl;
std::cout << "firstname: " << user.firstname << std::endl;
std::cout << "lastname: " << user.lastname << std::endl;
std::cout << "phone: " << user.phone << std::endl;
std::cout << "email: " << user.email << std::endl;
std::cout << "username: " << user.username << std::endl;
std::cout << "password: " << user.password<< std::endl;
}
void UserManager::registerUser(model::User& user)
{
printUser(user); printUser(user);
utility::PasswordEncryption passEnc; utility::PasswordEncryption passEnc;
@@ -66,22 +31,46 @@ void UserManager::registerUser(model::User& user)
hashed.userId = usr.id; hashed.userId = usr.id;
usrRepo.saveUserSalt(hashed); usrRepo.saveUserSalt(hashed);
result.registered = true;
result.message = "successfully registered";
printUser(usr); printUser(usr);
return result;
} }
model::User UserManager::userDtoConv(dto::UserDto::ObjectWrapper& userDto) bool UserManager::doesUserExist(const model::User& user) {
{ database::UserRepository userRepo(m_bConf);
model::User user;
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);
} }
void UserManager::printUser(const model::User& user) {
std::cout << "\nuser info" << std::endl;
std::cout << "id: " << user.id << std::endl;
std::cout << "firstname: " << user.firstname << std::endl;
std::cout << "lastname: " << user.lastname << std::endl;
std::cout << "phone: " << user.phone << std::endl;
std::cout << "email: " << user.email << std::endl;
std::cout << "username: " << user.username << std::endl;
std::cout << "password: " << user.password<< std::endl;
}
} }