Aryan
Aryan

Reputation: 103

Python - Help needed with file reading

I am trying to create a simple login system. What I'm doing is storing the login data in a text file called 'accounts.txt'

Now, when user tires to login, it first checks if the username given by the user is in the 'accounts.txt'. If it exists, then it asks for the password and then checks if password matches with the password in 'accounts.txt'

fr = open('accounts.txt', 'r')
while True:
username = input('Enter your username: ')  # Ask for their username

    if username in fr.read():  # Check if username exists
        password = input('Enter password: ')  # Ask for password if username exists

        if username+password in fr.read():
            print('Welcome ' + username)
            break
        else:
            print('Wrong password')

Note, the password save in accounts.txt is in the format of usernamepassword so if username is jack and password is gate, the actual password in the txt file will be jackgate, hence im using username+password to check if password is correct.

The problem occuring is if the user enters correct username, then program moves ahead properly but even if the password entered is right, it still displays 'Wrong password' .When the second time user enters username, it even shows error for wrong username. I tried to play with the code for a long time but couldn't come up with a solution. I guess it has something to do with fr.read(). Can I use that 'fr' object only once?

Upvotes: 1

Views: 219

Answers (3)

Victor Li
Victor Li

Reputation: 113

I would do

if password in fr.read():

instead of

if username+password in fr.read():

This is because for it to get to the if password in fr.read loop it first has to pass the if username in fr.read loop. However, the only problem I find with this is that if they enter a correct username but enter the wrong password for that username but correct password for another username it will still pass.

That is why I think you should use a dictionary not a text file. For example, if the usernames allowed is username and username1 and the password is username and username1, then in a different .py file, you can say.

username_password={'username':'username','username1':'username1'}

that makes a dictionary that has the username and passwords.

let's say you name that file stuff.py. Then in the second file that has to be in the same directory, you can do

from stuff import * #imports all values from stuff.py
while True:
    username = input('Enter your username: ') #gets username
    if username_password.has_key(username):
        password = input('Enter password: ')
        if password== username_password[username]:
            print('Welcome '+username)
            break
        else:
            print('Wrong password')
            break
    else:
        print('Wrong username')

I still don't get why you have a while loop, but if you want it, it is fine. Also, I added an else loop just in case the username is wrong.

Upvotes: 1

Arminius
Arminius

Reputation: 1169

Let me suggest some improvements with my answer to your question. I would read the accounts file in its entirety so you have an in-memory structure. If you do this as a dictionary in the form accounts[USER] -> PASS you can easily check for any account as per the code below.

Regarding my suggestions (they do not exactly only answer your questions, but IMHO the topic of writing login code should be treated with care):

  • I strongly recommend not to store passwords in plain text, regardless of application importance, always use hashes.
  • Do not store just the password hash, always use salting.
  • Do not tell the person trying to log in, if the username or the password was wrong, always just say "that's not the right combination", thus making it harder to break in.
  • Please find information about hashing functions in Python here: https://docs.python.org/3/library/hashlib.html#randomized-hashing
  • This site has a good introduction on salting ans securing passwords: https://crackstation.net/hashing-security.htm
  • Do you users a favor and treat the username as no case-sensitive. That is a totally valid approach, but it annoys me every time I have to use such a site (just like email addr are not case-sensitive)
  • As I am a total layman regarding password security, maybe one of the other Stackoverflow users can jump in with a comment and expand on this topic.

Anyway, here is my answer for your question on how to check for a login. I created a function check_account() that returns True or False, depending on wether the supplied credentials were correct or not.

import hashlib
import os
import binascii

def check_account(usr, pwd):
    # read the accounts file, a simple CSV where
    # username, salt value and password hash are
    # stored in three columns separated by a pipe char
    accounts = {}
    fr = open('/users/armin/temp/test.csv', 'r')
    for line in [x.strip().split("|") for x in fr.readlines()]:
        accounts[line[0].lower()] = (line[1], line[2])
    fr.close()

    # now go looking if we do have the user account
    # in the dictionary
    if usr in accounts:
        credentials = accounts[usr]
        # credentials is a list with salt at index 0
        # and pwd hash at index 1
        # generate the hash form the functions parameters
        # and compare with our account
        h = hashlib.blake2b(salt=binascii.unhexlify(credentials[0]))
        h.update(pwd.encode('utf-8'))
        if credentials[1] == h.hexdigest():
            return True
        else:
            return False
    else:
        return False


def main():
    while True:
        username = input('Enter your username: ')  # Ask for their username
        password = input('Enter password: ')  # Ask for password if username exists
        if check_account(username.lower(), password):
            print("Welcome, {0}".format(username))
        else:
            print('Username or password unknown')


if __name__ == '__main__':
    main()

To create the data for a user account, use may this code.

def create():
    username = input('Enter your username: ').lower()  # Ask for their username
    password = input('Enter password: ')  # Ask for password if username exists
    salt = binascii.hexlify(os.urandom(hashlib.blake2b.SALT_SIZE))
    print("SALT value:", salt)
    h = hashlib.blake2b(salt=binascii.unhexlify(salt))
    h.update(password.encode('utf-8'))
    print("Pwd hash:", h.hexdigest())

Upvotes: 1

Ajax1234
Ajax1234

Reputation: 71451

You can use startswith and endswith:

fr = [i.strip('\n') for i in open('accounts.txt')]

while True:
   username = input()
   if any(i.startswith(username) for i in fr):
       password = input('Enter password: ')
       if any(username+password == i for i in fr):
            print("welcome")
            break
       else:
           print("wrong password")

Upvotes: 1

Related Questions