0
import getpass
from passlib.hash import sha256_crypt

def register():
    username = str(input('username '))
    password = str(getpass.getpass('password ',stream=None))
    exec('global '+username)
    exec(username+'=user('+"'"+username+"'"+','+"'"+password+"'"+')')

def hashPassword(password):
    Passhash = sha256_crypt.hash(password)
    return Passhash

def verifyPassword(password,hashpass):
    return sha256_crypt.verify(password,hashpass)

class user(object):
    users=[]
    def __init__(self, username, password):
        password = str(password)
        if len(password) <= 20:
            self.username = username
            user.users.append(username)
            self.password = hashPassword(password)
        else:
            print("No more than 20 characters in the password")  

def login(username, passsword):
    if username in user.users:
        if verifyPassword(password,exec(username+'.password'))==True:
            print('logged in.')
        else:
            print('wrong password')
    else:
       print('unknown user.')   

I am trying to make a text based login/register system since I am fairly new to coding. For some reason something with the register() function doesn't correctly register a user because when I go to login verifypassword() it says

if verifyPassword(password,exec(username+'.password'))==True:
  File "<string>", line 1, in <module>
NameError: name 'test' is not defined
>>> 

if someone could tell me what is happening. I think it it something with global variables but I don't know how to fix it

user24343
  • 892
  • 10
  • 19
TheToll
  • 13
  • 2

2 Answers2

1

global in exec doesn't work.

Use globals()[var_name] = var_value to set dynamic variable names in global scope.

exec is gererally a (very) bad idea if called with user-supplied input. It also has (more or less) unexpected bahaviour in functions, see this example:

def f():
    exec('a=3')
    print(a)

>>> f()
Traceback [...]
NameError: name 'a' is not defined

(This has something to do with local scope being known at compile-time, see here or here)

Also, you might consider storing the actual user objects in user.users -- this prevents users picking names that you actually use inside your code and prevents unexpected behavior

Edit: Elaboration on the "local scope known at compile-time"

Since the compiler knows what local variables you are using, access is by the bytecode STORE_FAST and LOAD_FAST instructions, which store and load to and from a kind of array (you can look at local variable names via f.__code__.co_varnames), you can't just add stuff dynamically.

Why is this relevant for global?

Well, as said above, the STORE_FAST and LOAD_FAST instructions are used (you guessed it, for speed), the bytecode for following function will be:

>>> def f():
    exec('global x')
    x = 3

>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (exec)
              2 LOAD_CONST               1 ('global x')
              4 CALL_FUNCTION            1
              6 POP_TOP

  3           8 LOAD_CONST               2 (3)
             10 STORE_FAST               0 (x)
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE

The first part deals with calling exec. The second part is the assignment. STORE_FAST assigns to a local variable x, no matter what exec just did.

This is also the reason creating new local variables in exec doesn't work: there just isn't space for them. Note this isn't valid for local variables set in exec but also "normally", they will have space assigned nevertheless.

user24343
  • 892
  • 10
  • 19
0

In python, there is a dictionary of global-variables, that you can access by calling globals(). I'm not sure why exec(...) doesn't work, but I would consider a different solution than storing users as variables, named by username. But to solve your problem, keeping this design-choice, you can set the global user like this: globals()[username] = user(username, password) and when loggin in, do verifyPassword(password, globals()[username].password).

But since globals is really just a dictionary, it might be better practice to maintain a dictionary of users yourself. A user could potentially overwrite a global variable, by choosing a username that clashes with another global variable. You could then also eliminate the static users list on your user class. It could look like this, where repository is your dictionary with users:

import hashlib
import base64
import uuid
import getpass
from passlib.hash import sha256_crypt

repository = {}

def register():
    username = str(input('username '))
    password = str(getpass.getpass('password ',stream=None))
    repository[username] = user(username, password)

def hashPassword(password):
    Passhash = sha256_crypt.hash(password)
    return Passhash

def verifyPassword(password,hashpass):
    return sha256_crypt.verify(password,hashpass)

class user(object):
    def __init__(self,username,password):
        password = str(password)
        if len(password) <= 20:
            self.username = username
            self.password = hashPassword(password)
        else:
            print("No more than 20 characters in the password")

def loginUser(username):
    if username == 'exit':
        start()
    if username in repository:
        if loginPass(username)==True:
            print('success')
        else:
            print('passfail')        
    else:
        print('incorrect login USERNAME NOT IN USER.USERS LIST')

def loginPass(username):
    password = getpass.getpass('password ',stream=None)
    if verifyPassword(password, repository[username].password) == True:
        return True
    else:
        return False

def start():
    while 1:
        key1=input('login or register (l/r)')
        if key1=='register':
            del key1
            register()
        elif key1=='r':
            del key1
            register()
        elif key1=='l':
            del key1
            loginUser(input('username or exit to got to l/r screen '))

        else:
            print('ERROR string not reconised among ifs') 

start()
Jeppe
  • 1,830
  • 3
  • 24
  • 33