From b8efd56418bd2d0ed2c6afe6c6ce36b0b8f213ab Mon Sep 17 00:00:00 2001 From: Cristian-Adrian Frasineanu Date: Sat, 26 Nov 2016 18:19:24 +0200 Subject: [PATCH] Working login module. --- app/Console.cpp | 14 ++----- app/Controller.cpp | 35 ++++++++++------ app/Controller.h | 12 +++--- app/Helpers.h | 27 ++++++++++--- app/RepositoryInterface.h | 8 ---- app/UserModel.cpp | 82 +++++++++++++++++++++++++++++++------- app/UserModel.h | 39 ++++++++++++------ app/UserRepository.cpp | 69 ++++++++++++++++++++++---------- app/UserRepository.h | 5 +-- app/View.cpp | 14 +++++-- database/users.store | Bin 0 -> 664 bytes views/dashboard.view | 16 ++++++++ views/logout.view | 11 +++++ 13 files changed, 235 insertions(+), 97 deletions(-) create mode 100644 views/dashboard.view create mode 100644 views/logout.view diff --git a/app/Console.cpp b/app/Console.cpp index 1ff4570..1f6fb68 100644 --- a/app/Console.cpp +++ b/app/Console.cpp @@ -110,7 +110,7 @@ void Console::loadViews(const fs::path &viewsFolder) void Console::loadActions() { // TODO: load actions via config file - vector actions = { 'q', 'b', 'n', 'c' }; + vector actions = { 'q', 'b', 'n' }; this->actions = actions; } @@ -129,14 +129,11 @@ void Console::handleView() buffer << viewFile.rdbuf(); this->currentView.setRawFormat(buffer.str()); - vector emptyErrorBag = {}; - Controller::setErrorsBag(emptyErrorBag); - // If there's any validation error catch it and reload the view. this->theController = Controller(this->currentView.getViewName(), buffer.str(), View::getViewExtension()); - if (!Controller::getErrorsBag().empty()) { - string message = "\nPlease correct the following: "; - printVector(Controller::getErrorsBag(), message); + if (!Controller::getErrorBag().empty()) { + toast(string("There were some issues:"), string("error")); + printVector(Controller::getErrorBag()); sleepAndClearBuffer(3 * this->delay); this->reloadView(); @@ -197,9 +194,6 @@ void Console::takeActionOrNext() case 'n': // TODO: implement pagination for the questions index view break; - case 'c': - // TODO: implement confirmation action for input decisions - break; default: break; } diff --git a/app/Controller.cpp b/app/Controller.cpp index b6262f6..fa90c7a 100644 --- a/app/Controller.cpp +++ b/app/Controller.cpp @@ -2,10 +2,11 @@ //---Controller--- //---------------- -string Controller::viewInputFormat = "@input-"; -string Controller::viewOutputFormat = "@output-"; +string Controller::userInputString = "@input-"; +string Controller::outputString = "@output-"; +string Controller::middlewareString = "@guard-"; -vector Controller::errorsBag = {}; +vector Controller::errorBag = {}; void Controller::justShow() { @@ -40,7 +41,7 @@ void Controller::prepareView() } else if (this->hasInput(copyChunk) || this->hasOutput(copyChunk)) { - if (copyChunk.find(Controller::viewInputFormat) < copyChunk.find(Controller::viewOutputFormat)) + if (copyChunk.find(Controller::userInputString) < copyChunk.find(Controller::outputString)) { this->chopChunkAndGetAlias(copyChunk); } @@ -77,14 +78,21 @@ void Controller::prepareViewInput(const string &subChunk, const string &inputAli this->userInputs[inputAlias] = userInput; } -void Controller::setErrorsBag(vector &errorsBag) +void Controller::pushError(string &error) { - Controller::errorsBag = errorsBag; + if (error == "") + { + Controller::errorBag = vector({}); + } + else + { + Controller::errorBag.push_back(error); + } } -vector Controller::getErrorsBag() +vector Controller::getErrorBag() { - return Controller::errorsBag; + return Controller::errorBag; } Controller::Controller() @@ -123,10 +131,10 @@ char *Controller::getControllerName() void Controller::chopChunkAndGetAlias(string &chunk) { // Splice the alias (take the part after the "-" in the template string). - string inputAlias = chunk.substr(chunk.find(Controller::viewInputFormat) + Controller::viewInputFormat.size(), - chunk.find("\n", chunk.find(Controller::viewInputFormat)) - (chunk.find(Controller::viewInputFormat) + Controller::viewInputFormat.size())); + string inputAlias = chunk.substr(chunk.find(Controller::userInputString) + Controller::userInputString.size(), + chunk.find("\n", chunk.find(Controller::userInputString)) - (chunk.find(Controller::userInputString) + Controller::userInputString.size())); - this->prepareViewInput(chunk.substr(0, chunk.find(Controller::viewInputFormat) - 1), inputAlias); + this->prepareViewInput(chunk.substr(0, chunk.find(Controller::userInputString) - 1), inputAlias); chunk.erase(0, chunk.find(inputAlias) + inputAlias.size() + 2); } @@ -137,18 +145,19 @@ void Controller::operator=(const Controller &controller) delete[] this->controllerName; this->controllerName = new char[strlen(controller.controllerName) + 1]; strcpy(this->controllerName, controller.controllerName); + this->viewChunk = controller.viewChunk; this->controllerAttributions = controller.controllerAttributions; } bool Controller::hasInput(const string &raw) { - return raw.find(Controller::viewInputFormat) != string::npos; + return raw.find(Controller::userInputString) != string::npos; } bool Controller::hasOutput(const string &raw) { - return raw.find(Controller::viewOutputFormat) != string::npos; + return raw.find(Controller::outputString) != string::npos; } Controller::~Controller() diff --git a/app/Controller.h b/app/Controller.h index 57840fb..798e27f 100644 --- a/app/Controller.h +++ b/app/Controller.h @@ -9,9 +9,11 @@ using namespace std; class Controller { private: - static string viewInputFormat; - static string viewOutputFormat; - static vector errorsBag; + static string userInputString; + static string outputString; + static string middlewareString; + + static vector errorBag; char *controllerName; vector controllerAttributions; @@ -24,8 +26,8 @@ class Controller { void prepareViewInput(const string &, const string &); // TODO: take the chunk one by one for output and request output from model for each output variable public: - static void setErrorsBag(vector &); - static vector getErrorsBag(); + static void pushError(string &); + static vector getErrorBag(); Controller(); Controller(char *, string &, string &); diff --git a/app/Helpers.h b/app/Helpers.h index 9ec75ec..fb75337 100644 --- a/app/Helpers.h +++ b/app/Helpers.h @@ -57,19 +57,34 @@ inline bool isInMap(map &haystack, FirstT needle) } template -inline void printVector(vector &v, string &message) +inline void printVector(vector &v) { - if (message != "") + for (vector::iterator it = v.begin(); it != v.end(); it++) { - cout << message + cout << (*it) << endl; } +} - for (vector::iterator it = v.begin(); it != v.end(); it++) +template +inline void toast(T content, string &status) +{ + cout << endl << endl; + + if (status == "success") { - cout << (*it) - << endl; + cout << "++ " << content; + } + else if (status == "error") + { + cout << "!! " << content; } + else + { + cout << "** " << content; + } + + cout << endl; } void toLowerCase(string &); diff --git a/app/RepositoryInterface.h b/app/RepositoryInterface.h index a7bc870..0089e9e 100644 --- a/app/RepositoryInterface.h +++ b/app/RepositoryInterface.h @@ -2,8 +2,6 @@ #include "Helpers.h" -#include "ModelInterface.h" - using namespace std; //---Repository--- @@ -19,14 +17,8 @@ class RepositoryInterface { protected: map ValidationRules; map ValidationErrors; - - ModelInterface *model; - - vector errorBag; public: virtual void validateItems(map &) = 0; - virtual void retrieveItemForActive() = 0; - virtual void retrieveAll() = 0; virtual ~RepositoryInterface(); }; \ No newline at end of file diff --git a/app/UserModel.cpp b/app/UserModel.cpp index 24910ea..87c3f53 100644 --- a/app/UserModel.cpp +++ b/app/UserModel.cpp @@ -1,12 +1,44 @@ #include "UserModel.h" -// Serialize the object when writing a new record. -void UserModel::save() +User UserModel::getAfterUser(string &username) { - //this->io.write(reinterpret_cast(&this->User), sizeof(this->User)); + this->io.seekg(0, this->io.beg); + + do + { + this->io.read(reinterpret_cast(&this->user), sizeof(User)); + + if (this->user.username == username) + { + return this->user; + } + } while (!this->io.eof()); + + throw("Username not found!"); +} + +User UserModel::getAfterId(int id) +{ + this->io.seekg((id - 1) * sizeof(User), this->io.beg); + this->io.read(reinterpret_cast(&this->user), sizeof(User)); + + return this->user; +} + +void UserModel::markAs(string &status, int id) +{ + this->getAfterId(id); + + this->user.active = (status == "active") ? true : false; - cout << "Theoretically wrote the data into the file" - << endl; + this->save(); +} + +// Serialize the object when saving a user. +void UserModel::save() +{ + this->io.seekp((this->user.id - 1) * sizeof(User), this->io.beg); + this->io.write(reinterpret_cast(&this->user), sizeof(User)); } void UserModel::setAttributes(map &cleanInputs) @@ -19,28 +51,30 @@ void UserModel::setAttributes(map &cleanInputs) } else if (it->first == "fullname") { - strcpy(this->User.full_name, it->second.c_str()); + strcpy(this->user.full_name, it->second.c_str()); } else if (it->first == "email") { - strcpy(this->User.email, it->second.c_str()); + strcpy(this->user.email, it->second.c_str()); } else if (it->first == "username") { - strcpy(this->User.username, it->second.c_str()); + strcpy(this->user.username, it->second.c_str()); } else if (it->first == "password") { - strcpy(this->User.password, it->second.c_str()); + strcpy(this->user.password, it->second.c_str()); } } // If there's a new user, assign created_at with the current date. - if (strlen(this->User.full_name) != 0) + if (strlen(this->user.full_name) != 0) { time_t t = time(nullptr); - strftime(this->User.created_at, sizeof(this->User.created_at), "%c", localtime(&t)); + strftime(this->user.created_at, sizeof(this->user.created_at), "%c", localtime(&t)); } + + this->user.id = ++this->lastId; } UserModel::~UserModel() @@ -51,16 +85,36 @@ UserModel::~UserModel() string UserModel::pathToFile = "..\\database\\users.store"; void UserModel::openIOStream() { - this->io.open(UserModel::pathToFile, ios::in | ios::out | ios::binary | ios::app); + this->io.open(UserModel::pathToFile, ios::in | ios::out | ios::binary); + this->io.seekp(0, this->io.end); + this->fileSize = this->io.tellp(); if (!this->io.is_open()) { - cout << "Couldn't open the file stream to " + UserModel::pathToFile; + cout << "Couldn't open the file stream path: " << UserModel::pathToFile; + } +} + +void UserModel::setLastId() +{ + if (this->fileSize == 0) + { + this->lastId = 0; + } + else + { + User lastUser; + + this->io.seekg((this->fileSize / sizeof(User) - 1) * sizeof(User), this->io.beg); + this->io.read(reinterpret_cast(&lastUser), sizeof(lastUser)); + + this->lastId = lastUser.id; } } UserModel::UserModel() { this->openIOStream(); - this->protectedAttributes = { "created_at", "deleted_at", "role", "active" }; + this->protectedAttributes = { "created_at", "deleted_at", "role", "active", "banned" }; + this->setLastId(); } diff --git a/app/UserModel.h b/app/UserModel.h index 7dc22a8..7b9c400 100644 --- a/app/UserModel.h +++ b/app/UserModel.h @@ -7,28 +7,41 @@ #include "ModelInterface.h" +typedef struct { + int id; + + char full_name[255] = ""; + char email[255] = ""; + char username[20] = ""; + char password[17] = ""; + char created_at[50] = ""; + + // Soft deletes + char deleted_at[50] = ""; + char role[10] = "user"; + bool active = 1; + bool banned = 0; +} User; + class UserModel : public ModelInterface { private: static string pathToFile; - - struct { - char full_name[255] = ""; - char email[255] = ""; - char username[20] = ""; - char password[17] = ""; - char created_at[50] = ""; - - // Soft deletes - char deleted_at[50] = ""; - char role[10] = "user"; - bool active = 1; - } User; + + User user; void openIOStream(); + void setLastId(); + + int fileSize; + int lastId; public: // Open the IOStream and assign the protected attributes that shouldn't be changed by the user UserModel(); + //vector getAll(); + User getAfterUser(string &); + User getAfterId(int); + void markAs(string &, int); void save(); void setAttributes(map &); diff --git a/app/UserRepository.cpp b/app/UserRepository.cpp index c4869f6..d69856a 100644 --- a/app/UserRepository.cpp +++ b/app/UserRepository.cpp @@ -10,7 +10,8 @@ void UserRepository::defineValidation() { "fullname", "^(?=.*(?:[\\s\\-])([a-zA-Z]+\\s?)+)([a-zA-Z]+)(?:[\\s\\-]).*$" }, { "email", "^(?!.*[._]{2})[a-z0-9._]+@[a-z0-9]+(\\.[a-z]{1,3}){1,2}$" }, { "username", "^(?=.{7,19}$)(?![^a-zA-Z0-9])(?!.*[!_\\-\\.]{2})[a-zA-Z0-9\\.\\-_]+$" }, - { "password", "^(?=.{5,16}$)(?=.*[A-Z])(?=.*[0-9])(?=.*[!\\-_@.$#])[a-zA-Z0-9!\\-_@.$#]+$" } + { "password", "^(?=.{5,16}$)(?=.*[A-Z])(?=.*[0-9])(?=.*[!\\-_@.$#])[a-zA-Z0-9!\\-_@.$#]+$" }, + { "logout", "^y|Y|n|N$" } }; this->ValidationErrors = { @@ -20,19 +21,59 @@ void UserRepository::defineValidation() { "password", "Password should be 5-16 characters long, and contain at least an uppercase letter, a number and a special character." }, }; - this->errorBag = {}; + Controller::pushError(string("")); } UserRepository::UserRepository() { this->defineValidation(); - this->model = new UserModel(); } void UserRepository::receiveCleanInput(map &cleanInput) { - this->model->setAttributes(cleanInput); - this->model->save(); + this->model.setAttributes(cleanInput); + + // TODO: find a way to log ou the user and change the views without providing further input after confirming. + if (cleanInput.find("logout") != cleanInput.end()) + { + char choice[2]; + strcpy(choice, cleanInput.find("logout")->second.c_str()); + if ((char)tolower(choice[0]) == 'y') + { + //this->model.getActive(); + //this->model.markAs(string("nonactive")) + } + else + { + // Go back + } + } + // If the user tries to login, compare the password with the one from the db. + else if (cleanInput.find("fullname") == cleanInput.end()) + { + try + { + User user = this->model.getAfterUser(cleanInput.find("username")->second); + if (user.password == cleanInput.find("password")->second && user.banned != true && user.deleted_at == "") + { + this->model.markAs(string("active"), user.id); + toast("Login successful! Please press -c- confirm your access to the dashboard.", string("success")); + } + else + { + Controller::pushError(string("Incorrect password!")); + } + } + catch (const invalid_argument &e) + { + Controller::pushError(string(e.what())); + } + } + else + { + this->model.save(); + toast("Account created successfully! Please press -c- confirm your access to the dashboard.", string("success")); + } } string &UserRepository::getAlias() @@ -48,7 +89,7 @@ void UserRepository::validateItems(map &truncatedInput) { if (!regex_match(it->second.c_str(), regex(this->ValidationRules[it->first.c_str()]))) { - this->errorBag.push_back(this->ValidationErrors[it->first.c_str()]); + Controller::pushError(this->ValidationErrors[it->first.c_str()]); } } catch (const regex_error &e) @@ -62,26 +103,12 @@ void UserRepository::validateItems(map &truncatedInput) } // If there are no errors, send it along. - if (this->errorBag.empty()) + if (Controller::getErrorBag().empty()) { this->receiveCleanInput(truncatedInput); } - - // Let the console know about the errors. - Controller::setErrorsBag(this->errorBag); -} - -void UserRepository::retrieveItemForActive() -{ - // Search for the record having active set to true. -} - -void UserRepository::retrieveAll() -{ - // Print everything. } UserRepository::~UserRepository() { - delete this->model; } \ No newline at end of file diff --git a/app/UserRepository.h b/app/UserRepository.h index 677be8e..52edbad 100644 --- a/app/UserRepository.h +++ b/app/UserRepository.h @@ -5,11 +5,12 @@ #include "RepositoryInterface.h" #include "UserModel.h" - class UserRepository : public RepositoryInterface { private: static string alias; + UserModel model; + void defineValidation(); void receiveCleanInput(map &); public: @@ -18,8 +19,6 @@ class UserRepository : public RepositoryInterface { UserRepository(); void validateItems(map &); - void retrieveItemForActive(); - void retrieveAll(); ~UserRepository(); }; \ No newline at end of file diff --git a/app/View.cpp b/app/View.cpp index 5939751..300761b 100644 --- a/app/View.cpp +++ b/app/View.cpp @@ -51,18 +51,24 @@ void View::loadViewsOptions() signupView = "signup.view", browseIndexView = "browse-index.view", faqView = "faq.view", - helpView = "help.view"; + helpView = "help.view", + dashboardView = "dashboard.view", + logoutView = "logout.view"; View::ViewsOptions = { { homeView, { { '1', "login.view" }, { '2', "signup.view" }, { '3', "browse-index.view" }, { '4', "faq.view" }, { '5', "help.view" }, { 'q', "quit" } } }, - { loginView, { { '1', "browse-index.view" }, { 'q', "quit" }, { 'b', "back" } } }, + { browseIndexView, { { 'q', "quit" }, { 'b', "back" } } }, - { signupView, { { 'q', "quit" }, { 'b', "back" } } }, + { loginView, { { 'b', "back" }, { 'c', "dashboard.view" } } }, - { browseIndexView, { { 'q', "quit" }, { 'b', "back" } } }, + { signupView, { { 'b', "back" }, { 'c', "dashboard.view" } } }, + + { dashboardView, { { 'q', "quit" }, { '4', "logout.view" } } }, + { logoutView, { { 'b', "back" } } }, + { faqView, { { 'q', "quit" }, { 'b', "back" } } }, { helpView, { { 'q', "quit" }, { 'b', "back" } } } diff --git a/database/users.store b/database/users.store index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a561790b19fd24deadb04de76b67662b8596199 100644 GIT binary patch literal 664 zcmZQ%U|?`7N-WOIOHIrxRd6oKEH24R%+qyDDFRYJ$>9&u;NEwrOw7p7*GtaN9sa(C z`!KOQ1&9odjNu{-43O9_%}F&>gop+wmMHk;mnj&TDHvK<0g