Login from txt file with attempt limits - c++

i'm totally new to C++ and this forum. I tried searching the codes and found a piece of code but it doesn't work as what I wanted. I want a login that check every line of the txt file and grant access to the system if the username and password is correct.
string line = " ";
ifstream readfile("Login.txt");
string username, password, adminname, adminpass;
cout << "\nEnter Username: ";
cin >> username;
cout << "\nEnter Password: ";
cin >> password;
while (getline(readfile, line))
{
stringstream iss(line);
iss >> adminname >> adminpass;
//Login Success Function
if (username == adminname && password == adminpass)
{
cout << "\nLOGIN SUCCESSFUL!";
}
}
//Login Fail Function
{
int fail = 5;
while (fail > 0)
{
cout << "Error! Invalid Username and Password. Please reenter.\n";
cout << "You have " << fail << " tries left.\n";
cout << "\nEnter Username: ";
cin >> username;
cout << "\nEnter Password: ";
cin >> password;
fail--;
}
cout << "\nACCESS DENIED!";
}
The txt file consists of 1st line (admin123 password123), 2nd line (admin admin).
The login worked fine if I entered correctly however, if I enter wrong username or password I just stuck in the while loop until it shows access denied even if I enter correct username and password for the second try.
Can anyone help me to fix this? If possible please include the comments(the //) so that I am able to learn from it. Thanks in advance.

Since you mention in the comments that this is an assignment, I'm going to keep this pretty general.
In your program, you prompt once for username and password, then read through the file to see if there is a match.
If there is no match, you then prompt for username and password again in a loop, but do not check to see if they are valid. This is what the problem is. Each time you get a new username and password, check to see if they are valid.
There are (at least) two possible approaches:
Include the "fail" and re-prompt logic around the code that reads the file. So you get a username and password, then read the file checking for a match. If no match, do it again. In this case, you would be reading the file each time. For a large data-set, this could get slow. For this problem, it should be fine.
Read the file once and save the values (Have you studied arrays, vectors, or other data structures? You need at least some of those things to do this). I would use a std::map here because it is direct access and would be the smallest amount of code, but there are many other ways to do this as well.
Here is a possible way to do it with re-reading the file. Note that this mostly just re-organizing the code you already have:
bool success = false; //use this as part of our loop condition
int fail = 5;
while (!success && fail > 0) //loop until we succeed or failed too much
{
//get the username and password
cout << "\nEnter Username: ";
cin >> username;
cout << "\nEnter Password: ";
cin >> password;
//open the file and see if we have a match
ifstream readfile("Login.txt");
while (getline(readfile, line))
{
stringstream iss(line);
iss >> adminname >> adminpass;
//Login Success Function
if (username == adminname && password == adminpass)
{
//we have a match, so set success to true so we exit the loop
success = true;
}
}
if (!success) //we did not find a match in the file
{
//so we output the message
cout << "Error! Invalid Username and Password. Please reenter.\n";
cout << "You have " << fail << " tries left.\n";
fail--;
}
}
//now we know if we had success or not, so report it
if (success)
{
cout << "\nLOGIN SUCCESSFUL!";
}
else
{
cout << "\nACCESS DENIED!";
}

Related

A decision condition is triggered without input

I am currently working on a very simple project and I found a problem in the testing phase when I tried to enter his name for the new employee and the decision condition was suddenly triggered, I am not sure why this happened. Based on my limited coding experience, in general, a statement in an output judgment statement needs to fulfil a judgment condition, but why would a judgment condition be triggered if I didn't do any input? Thank you all for your help.
Here is a part of the code.
void Management::Add_Staff() {
std::cout << "Please enter the number of staffs you want to add: " << std::endl;
int addNum = 0; // saves the amount entered by the user
std::cin >> addNum;
while (addNum <= 0 || addNum >= 50) {
std::cout << "Invaild number. Please try again" << std::endl;
std::cout << "Please enter the number of staffs you want to add: " << std::endl;
std::cin.clear(); // clear error enter
std::cin.ignore(INT_MAX, '\n'); // INT_MAX means an extremely large number,'\n' means empty space
std::cin >> addNum;
}
int new_Size = this->_StaffNumber + addNum; // The number of existing employees plus
// the number of new employees
Person** new_Space = new Person*[new_Size]; // Open up new space
if (this->_StaffArray !=
NULL) // if the data of the original pointer is not null
{
for (int i = 0; i < this->_StaffNumber;
i++) // data of the original pointer is added to the new pointer
{
new_Space[i] = this->_StaffArray[i];
}
}
for (int i = 0; i < addNum; i++) {
int ID; // create an variable nameed id to store the staff number entered
// from users
std::cout << "Please enter pure and positive number as the staff number of " << i + 1 << " staff: " << std::endl;
std::cin >> ID;
while (ID <= 0) {
std::cout << "Invalid staff number, please enter again: " << std::endl;
std::cin.clear();
std::cin.ignore(INT_MAX, '\n');
std::cin >> ID;
}
std::string NAME; // create an variable nameed id to store the staff
// number entered from users
std::cout << "Please enter the name: " << std::endl;
// std::cin >> NAME;
while (std::getline(std::cin, NAME)) {
if (NAME.length() == 0)
{
std::cout << "Your input is not correct. Please re-enter your name" <<
std::endl;
}
// This will check if the NAME contains only characters.
else if (std::all_of(NAME.begin(), NAME.end(), isalpha)) // isalpha: The function returns a non-zero value if the argument is an alphabetic character, or zero otherwise.
{
break;
}
else {
std::cout << "Only characters are allowed:" << std::endl;
}
}
That is my test case.
*********************************************************
********Welcome to the employee management system********
***********0.Exit the management page********************
***********1.Add the employee information****************
***********2.Display the employee information************
***********3.Delete the employee information*************
***********4.Modify the employee information************
***********5.Search the employee information************
***********6.Sort by number*****************************
Please enter the numbers 0 through 6 as your next step
1
Please enter the number of staffs you want to add:
1
Please enter pure and positive number as the staff number of 1 staff:
12
Please enter the name:
Your input is not correct. Please re-enter your name
After I entered the employee number, the judgment condition was triggered before I entered the name, but I didn't enter a space, I didn't even have time to enter something, and the judgment condition was triggered.
When you get input form the user using std::cin the input from the user does not go directly into the program. Instead that input sits in a buffer, which temperately stores that user entered data so you can later tie that data to a variable or perform some other task with that data. However, if that buffer does not get cleared and you use std::getline then std::getline will read the buffer instead of the new user input that you actually wanted. This is why its important to make use of the std::cin.ignore() function, which will clear the buffer of unwanted int and characters. If you want a more en-depth overview of std::cin.ignore() check out this link .
The Fix:
Looking at your code you do make use of cin.ignore() to clear the buffer but only the user enters something other then a number which will drop them into that while loop.
This is what you currently have:
while (ID <= 0) {
std::cout << "Invalid staff number, please enter again: " << std::endl;
std::cin.clear();
std::cin.ignore(INT_MAX, '\n');
std::cin >> ID;
}
std::string NAME; // create an variable named id to store the staff
// number entered from users
std::cout << "Please enter the name: " << std::endl;
To correct this you will need that std::cin.ignore() call out side of the while loop so that it always happens whether there is an error or not. I have a comment that says NEW CODE LINE for where I made the change.
while (ID <= 0) {
std::cout << "Invalid staff number, please enter again: " << std::endl;
std::cin.clear();
std::cin.ignore(INT_MAX, '\n');
std::cin >> ID;
}
std::cin.ignore(INT_MAX, '\n');//NEW CODE LINE
std::string NAME; // create an variable named id to store the staff
//number entered from users
std::cout << "Please enter the name: " << std::endl;

validate string input with while loop

I wanted to validate a string that will be inputted by the user which follows two conditions. The condition would be whether the string is empty or the string has a space char. My current problem is that I can validate the string but it would require me to press enter once more time to reiterate my question "2. Enter Product Name: ".
while (true) {
cout << "2. Enter Product Name: ";
if(getline(cin, newNode->product_name)) {
if ((newNode->product_name).empty() || (newNode->product_name) == " ") {
cout << "Please enter a valid product name!\n";
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
}
else {
break;
}
}
}
Being inside the if statement if(getline(cin, newNode->product_name)) { means that the reading of a line succeeded. Therefore, you don't need the lines
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
They will request an extra line to ignore, so remove that lines.

How to prompt the user to input the correct format of email address in C++?

Basically, I wish to limit the user to input in the correct format of email address with the code below.
cout << "Donor's Email: ";
cin >> email;
while (email != "#" && email != "."){
cout << "Please enter correct email format." << endl;
cout << "Donor's Email: ";
cin >> email;
}
Somehow, the results are, even I input the correct format of email address but it keeps on looping for me to input again.
Somebody please help me. Thanks.
Don't you want the negation of your condition: i.e.
while (!(email != "#" && email != "."))
which, by application of De Morgan's law simplifies to
while (email == "#" || email == ".")
But this seems to me to be an insufficient check for validity (surely "##" is invalid too, for example). Consider using the regular expression library std::regex &c. from C++11, having Googled "regular expression for a valid email address".
Didn't try but if you're using C++11 you could use std::regex.
From gonjay's answer your code could be something like:
#include <regex>
using namespace std;
const regex pattern("(\\w+)(\\.|_)?(\\w*)#(\\w+)(\\.(\\w+))+");
cout << "Donor's Email: ";
cin >> email;
while (!regex_match(email, pattern)){
cout << "Please enter correct email format." << endl;
cout << "Donor's Email: ";
cin >> email;
}

C++ function runs more than intended

When I run my program the user can log in, but if the enter in a wrong user name, it runs a check username loop again, saying that they did not enter in a valid user name. This work perfectly fine except for one thing. Say they attempt to log in in three times and the third attempt is correct and the get prompted for a password. Once they input it it ask for a second password and then a third. It seems like it is completing the function for the other attempts. I can not think of a way to check for this. Any ideas.
If you look at it you can see that I am calling UserCheck inside of getNameIndex. I am almost positive this is where the error is occurring.
The Function that checks the users:
void User_Psw::UserCheck()
{
// read from the database
ifstream fin("dataBase.txt", ios::in);
if( !fin.good() )
{
cout << "Failed to open database file." << endl;
return;
}
while (fin >> username >> password)
{
Usernames.push_back(username);
Password.push_back(password);
++sizeOfDatabase; // This may or may not be needed elsewhere.
}
// rest of the program
cout << "Username: ";
cin >> username;
getNameIndex();
cout << "Password: ";
cin >> password;
if(!PasswordMatches())
{
cout << "Access denied";
}
else
{
cout << "Success! You have logged in.";
}
}
This is the username check function
void User_Psw::getNameIndex()
{
userThere = false;
for(int i=0; i < sizeOfDatabase; i++)
{
if (Usernames[i] == username)
{
index = i;
userThere = true;
}
}
if (userThere == false)
{
cout << "\nThat user name does not exsist. \n";
cout << "Please try again. \n\n";
UserCheck();
}
}
The structure of your program is wrong.
Instead of getNameIndex calling UserCheck() again, you should have getNameIndex return a bool - true on success, false on failure. Run it inside of a loop, something like this:
bool success = false;
while (!success)
{
cout << "Username: ";
cin >> username;
success = getNameIndex();
}
Also, instead of having global variables, you should pass them to the function. Something like:
success = getNameIndex(username);
and getNameIndex() shouldn't do any I/O - the function that calls getNameIndex() should also be responsible for printing the error message. Imagine if you used getNameIndex() in a different context, such as when when the program is being run by another program or in an automated way - then printing to the console would be meaningless.

Reading in Encrypted Username/Password

I am in the process of developing a console application that acts as a Diary. At this stage I am developing the login authentication and have hit a bit of a wall! As I will be dealing with text files for both my login and diary storage, I would like to encrypt these text files from prying eyes.
Now, the problem is I do not know how to go about the decrypt >> check user&&pass >> encrypt again.
Would it be along these lines?:
Program Loads
Decrypt passwords.txt
If at any point the program closes, encryptFile() is ran.
Validate user entry
Encrypt passwords.txt
If I am along the right lines how do I go about implementing this? I searched for encryption tutorials for text files using c++ and they were not very helpful.
Here is my butchered beginner password.txt code, where shall I go from here? If there is an encryption tutorial/article you recommend that I missed please post it!
void checkPasswordFile() {
string username;
string password;
string passwordAgain;
string userIn;
string passIn;
string line;
ifstream passwordFile("passwords.txt");
istringstream instream;
if (passwordFile.good()) {
cout << "\t==================================" << endl;
cout << "\t----------------------------------" << endl;
cout << "\tYou are a returning user, please fill in your details" << endl;
while(!passwordFile.eof()) {
getline(passwordFile, line);
instream.clear();
instream.str(line);
username = line;
getline(passwordFile, line);
instream.clear();
instream.str(line);
password = line;
}
do {
cout << "Username: " << endl;
cin >> userIn;
cout << "Password: " << endl;
cin >> passIn;
if (userIn == username && passIn == password) {
displayMenu();
} else {
cout << "Username and Password Do Not Match, Try Again" << endl;
}
} while(userIn != username && passIn != password);
} else {
cout << "file no exist";
ofstream passwordFile;
passwordFile.open ("passwords.txt", ios:: in | ios::app);
cout << "\t==================================" << endl;
cout << "\t----------------------------------" << endl;
cout << "\tThis is your first run, please enter a username and password" << endl;
cout << "\tUsername: " << endl;
cin >> username;
cout << "\tPassword: " << endl;
cin >> password;
/*
Do Loop:
Prompts Re-Entry if PasswordAgain is not equal to Password
*/
do {
cout << "Re-Type Password: ";
cin >> passwordAgain;
if(password != passwordAgain) {
cout << "Passwords Do Not Match! Try Again" << endl;
}
} while(password != passwordAgain);
passwordFile << username << "\n";
passwordFile << password;
}
}
Thank you very much for your time.
p.s for the life of me I cannot find out how to do:
Username:[cin>>username] on the same console line, sorry for doubling up but didn't deem it a big enough question for its own post! Thanks.
EDIT:
I have succesfully been able to decrypt the username and pass when created and stored in the text file. Then when the user comes back, what they entered is encrypted and compared with the file.
Problem being this only works for short words, user pass works, but username and password does not... any ideas why? Here is my encryption code:
char encryptKey = 'h';
cout << "\tUsername: ";
cin >> userIn;
cout << "\tPassword: ";
cin >> passIn;
for (int i = 0; i < userIn.size(); i++) {
userIn[i] ^= encryptKey;
}
for (int x = 0; x < passIn.size(); x++) {
passIn[x] ^= encryptKey;
}
if (userIn == username && passIn == password) {
displayMenu();
} else {
cout << "\tUsername and Password Do Not Match, Try Again" << endl;
}
The right thing to is not to encrypt the passwords file - the issue is that the encryption key for the file would need to be stored somewhere that the program can access it, which would make it relatively easy to find and abuse.
Instead, you should be using password hashing (using a strong hash algorithm like SHA1). A hash algorithm is a algorithm that deterministically maps a piece of text onto a large number (called its hash), and is designed to be irreversible without great effort. The basic concept is that you take the password, use it to compute its hash, and then store that hash. Later, when the user enters the password to log in, you compute its hash again and compare the resulting hash to the stored hash. Even if someone gains access to the hashes, they do not obtain the password, which is important, because people often share passwords between applications. Don't implement your own SHA1 hash - see "What is the best encryption library in C/C++?" for a list of libraries.
You must also use salting and key stretching to defend against common brute force attacks.