0

This is my code:

users = []
users.append([username, password])

usersFile = open("users.txt","w+")
for users in users:
  usersFile.write("%s" % users)
usersFile.close()


def loginFunction():
    userEntry = ""

    foundName = 0

    while userEntry == "":
        userEntry = raw_input("Enter your username: ")
        usersFile = open("users.txt", "r")
        lines = usersFile.readlines() 
        usersLen = len(users)
        for index in range (0, usersLen-1):
            if userEntry == users[index, 0]:
                foundName = 1
                passwordEntry = raw_input("Enter your password: ")
                if passwordEntry == users[index, 1]:
                    print "Username and passwords are correct"
                else:
                    print "incorrect"
                    userEntry = ""
        if foundName == 0:
            print "Username not recognised"
            userEntry = ""

I have produced a program that registers the username and password of a user. I have checked the files and it saves the username and password successfully but when I try to login using the correct username that I know is in the file as a list, it still always comes up "Username not recognised". Any ideas as to why this might be?

qwe
  • 93
  • 1
  • 1
  • 10
  • 2
    Your `for` loop stops after it's tested the second-last entry in `users`. But you should break out of that loop as soon as you've found a match: it's silly to keep looking after you've found what you're looking for. Also, `users[index, 0]` won't work, as mentioned in comments to [your previous question](https://stackoverflow.com/questions/48010959/code-not-working-using-python-tried-converting-from-pseudo-code-to-python-a). – PM 2Ring Dec 28 '17 at 18:44
  • @PM 2 Ring Whoops thought I changed it. Clearly not haha. So how should I change the for loop? Just type 'break' after the print "username and passwords are correct" ? – qwe Dec 28 '17 at 18:49
  • Yes, you can just put `break` on the line after the `print` call. But there are a few other problems with this code. You read "users.txt" into `lines`, but then you ignore `lines` and search in the original `users` list. You really should be searching for the username & password in `lines`. And to do that you first need to split each row of `lines` into a list containing the username & password. – PM 2Ring Dec 28 '17 at 18:58
  • @PM 2Ring Sorry I'm a bit confused. How do I split the rows of lines? – qwe Dec 28 '17 at 19:20

2 Answers2

0

It looks like you're writing out the user/passwords, as lists, as single lines in the file. Then when you read it back, each pair is read back as one string. readlines() doesn't automatically convert text back to a list object. If you want to do that, you might use Pickle instead. Pickle should let you save the entire users list of lists object all at once.

Also to be pythonic your for loop should iterate over the list directly. If you need the current index, use for i, user in enumerate(users). Using range(len(users)) is suboptimal in python. You can then use user[0] to get the username and user[1] for the password.

TheAtomicOption
  • 1,456
  • 1
  • 12
  • 21
  • How would I go about using a Pickle in my program? Sorry, I'm quite new to Python. – qwe Dec 28 '17 at 18:50
  • 1
    Pickle is a module that lets you convert python objects to bytes and back. The best way to learn it is to read through the documentation for it: https://docs.python.org/3.6/library/pickle.html – TheAtomicOption Dec 28 '17 at 18:53
0

I've made a few changes to your program. Firstly, I've changed it to use the modern print function instead of the print statement. The print function is available in Python 2.6 and later. It's more powerful than the old print statement, and IMHO it's a good idea to start using it in preparation for Python 3 (which doesn't have the print statement).

To simplify reading & writing the username & password data we can use the standard csv module. It's not strictly necessary for this simple task, but it means we don't have to worry about the messy details of parsing the name and password strings. Eg, if the strings contain spaces or quotes, the csv will handle them correctly. Note that in Python 2 CSV files must be opened in binary mode, but in Python 3 they must be opened in text mode. This is rather annoying when you're trying to write code that runs correctly on both versions.

The easy way to look up a password given the username is to use a dictionary with the username as the key and the password as the value. This is much more efficient than scanning through a list row by row looking for a match.

Of course, in a real program we would never store passwords as plain text. That's extremely insecure! The usual procedure is to store a hashed version of the password, using a strong cryptographic hash function applied a very large number of times to make it a time-consuming operation. For further info please see PBKDF2, scrypt, and bcrypt.

Also, it's bad practice to let a potential attacker know that a username is valid but that the password they submitted is invalid. That allows them to easily build a list of valid usernames. Instead, you should always ask for the password, even if the username is invalid.

from __future__ import print_function
import csv

users = [
    ['Alice', 'aardvark'],
    ['Bob', 'bobcat'],
    ['Steve', 'swordfish'],
]

# Save the users list to a CSV file
users_filename = "users.txt"
with open(users_filename, "wb") as f:
    writer = csv.writer(f)
    writer.writerows(users)

def login_function():
    # Load the usernames & passwords into a dictionary
    with open(users_filename, "rb") as f:
        users = dict(csv.reader(f))

    # Give the user 3 chances to login
    for i in range(2, -1, -1):
        user_entry = raw_input("Enter your username: ")
        password_entry = raw_input("Enter your password: ")
        if user_entry in users and password_entry == users[user_entry]:
            print("Username and password are correct")
            return True
        else:
            print("Username and password are invalid")
            print(i, "login attempts remaining")

    print("Login failed")
    return False

print(login_function())

demo

Enter your username: Alan
Enter your password: runner
Username and password are invalid
2 login attempts remaining
Enter your username: Alice
Enter your password: aardvark
Username and password are correct
True
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182