0

I'm wondering how I would transfer a username over to another file. But without having the whole script with it, I would just like to be able to import that variable.

So far, the user has to setup a username that they wish to use and then they have to setup a password for that as well. How do you import that username variable but have the data inputted stored inside it when it is imported over?

The username code is:

set_username = input("First enter a username you wish to use: ")
set_password = input("Now enter a password you wish to use: ")

Then, on another file, the user is required to login with the username and password that they setup in another file. How am I meant to import over the username and password variables that the user has setup and the data is still stored inside those variables.

CDspace
  • 2,639
  • 18
  • 30
  • 36
Jake
  • 3
  • 2
  • You can write a list of users/passwords into a file and read that list from there, but that's [not how it's supposed to be done](https://www.youtube.com/watch?v=8ZtInClXe1Q). The list of users should be kept, along with a pointer to witch file/folder belongs to that user. The contents of that file/folder should be encrypted with the password given in the first case, and if the correct password is provided the decryption should be successful. – berna1111 Oct 13 '17 at 20:37
  • By the way, I believe the encryption module is [hashlib](https://docs.python.org/2/library/hashlib.html#module-hashlib) - I've never used it, but as far as I understand from a first glance read it is used to hash the given key (not files or folders as I said in previous comment). You keep the resulting hash of the password and you use the same hashing function on any password that one attempts to use to login. – berna1111 Oct 13 '17 at 23:05
  • Ok thanks for the help, might have sounded like a silly question but I’m just getting into programming. But thanks again for replying – Jake Oct 15 '17 at 08:20
  • It's not a silly question, managing logins is a common exercise to handle files. The answer I gave you is probably a bit convoluted to what you want, but if you have the time give it a read and try to understand what it does. If you feel like something's strange, comment on it and I'll try and answer. – berna1111 Oct 16 '17 at 03:16

3 Answers3

1

You can create a function in your original file that gets/returns the username and password. You can then import this function in your other file. For example,

def get_username():
    return set_username

def get_password():
    return set_password

Then in your other file you can say,

from filename import get_username, get_password
peachykeen
  • 4,143
  • 4
  • 30
  • 49
1

Similar to @agaidis answer, you can also import the variables without using a get function:

from filename import set_username, set_password
DJDMorrison
  • 1,302
  • 2
  • 17
  • 34
0

Got interested and made a convoluted example: defined a LoginManager class to handle both creating and logging users using a list on an external document. This class should be kept in it's own file, which would then be imported on both the code file where the new users are created and on the one where they are verified (see examples on the code).

The class initiator gets a path to a file as an argument, as long as it points to the same file in both code files it should be ok.

I used argon2 as the hashing algorithm, although I must admit I'm new to cryptography and so you should look upon my choice with a grain of salt.

import argon2
from pathlib import Path
import re


class LoginManager(object):
    """
    Parameters
    ----------
    database : str
        File (and path to it) that holds the users and corresponding hashed
        passwords.

    Examples
    --------
    Simple `LoginManager`:

    >>> temp_database = 'temp_database.txt'
    >>> Login1 = LoginManager(temp_database)
    >>> Login1.add_user('super_user', 's3kr3tp4ssw0rd')
    >>> Login1.login('super_user', 's3kr3tp4ssw0rd')
    True
    >>> Login1.login('super_user', 't0t411ywr0ng')  # wrong password
    False
    >>> Login1.login('LameUser', 's3kr3tp4ssw0rd')  # not existing user
    False

    New `LoginManager` with the same database:

    >>> Login2 = LoginManager(temp_database)
    >>> Login2.login('super_user', 's3kr3tp4ssw0rd')  # was on Login1
    True
    >>> Login2.login('super_user', 't0t411ywr0ng')  # wrong password
    False
    >>> Login2.login('LameUser', 't0t411ywr0ng')  # not existing user
    False

    Clean `temp_database` if testing with `doctest`:

    >>> import os
    >>> os.remove(temp_database)


    Notes
    -----
    * Notice that adding new users to either LoginManager in the example will
      not share the new ones between them! Only the users existing at the time
      of the creation will be in the LoginManager, so colisions must be must be
      avoided by not having two pointing to the same file at the same.
    * See more details on `argon2` here:
      https://argon2-cffi.readthedocs.io/en/stable/api.html#argon2.PasswordHasher
      (also where I saw 's3kr3tp4ssw0rd' and 't0t411ywr0ng')
    """
    def __init__(self, database=None):
        """
        Create new `LoginManager`.

        Notes
        -----
        Should have better verification whether given path points to a file or
        a folder!
        """
        if database is None:
            database = 'database.txt'
        self.database = Path(database)
        # create file if it is not there:
        if not self.database.is_file():
            self.database.touch()

        self._reload_database()

        self.hasher = argon2.PasswordHasher(time_cost=2,
                                            memory_cost=512,
                                            parallelism=2,
                                            hash_len=16,
                                            salt_len=16,
                                            encoding='utf-8')
        self._hash_example = self.hasher.hash('default')
        self._max_name_size = 10

    def add_user(self, user, password):
        """
        Add new user to the `LoginManager` and update `database` file.
        Raises `ValueError` if the user already exists or is invalid.

        Examples
        --------
        >>> temp_database = 'temp_database_user.txt'
        >>> Login1 = LoginManager(temp_database)
        >>> Login1.add_user('super*user', 's3kr3tp4ssw0rd')
        Traceback (most recent call last):
        ValueError: Invalid characters in user name!
        Use only A-Z, a-z, 0-9 and `.`, `-` or `_`.
        >>> Login1.add_user('UltraLongUserName', 's3kr3tp4ssw0rd')
        Traceback (most recent call last):
        ValueError: User name "UltraLongUs..." too long!

        Clean `temp_database` if testing with `doctest`:

        >>> import os
        >>> os.remove(temp_database)

        Notes
        -----
        Does not check users added to the databse file after the `LoginManager`
        was created!
        """
        self._is_username_valid(user)  # raises ValueError if it's not
        hashed = self.hasher.hash(password)
        self.users[user] = hashed
        with self.database.open('ba') as database:
            database.write('\t'.join([user.ljust(self._max_name_size),
                                      hashed,
                                      '\r\n']))
        # The '\r\n' at the end ensures there's always a new empty line
        # after the lattest password, and eases the split of user name and
        # password afterwards using '\t' when importing the list.
        # The .ljust(self._max_name_size) pads the username with spaces.
        # The binary mode is used to ensure future changes are valid (namely
        # to allow the use of seek with negative values whithin the file.

    def login(self, user, password):
        """
        Return `True` if the user/password pair is valid, `False` otherwise.
        """
        try:
            return self.hasher.verify(self.users.get(user, self._hash_example),
                                      password)
        except argon2.exceptions.VerifyMismatchError:
            return False

    def change_password(self, user, old_password, new_password):
        """
        Change password of existing user.

        Examples
        --------
        >>> temp_database = 'temp_database_change_pass.txt'
        >>> Login1 = LoginManager(temp_database)
        >>> Login1.add_user('super_user', 's3kr3tp4ssw0rd')
        >>> Login1.add_user('LameUser', 't0t411ywr0ng')
        >>> Login1.add_user('banana', '1234567890')

        Test changes to first user:

        >>> Login1.login('super_user', 's3kr3tp4ssw0rd')
        True
        >>> Login1.change_password('super_user',
        ...                        's3kr3tp4ssw0rd',
        ...                        'n3ws3kr3tp4ssw0rd')
        >>> Login1.login('super_user', 's3kr3tp4ssw0rd')
        False
        >>> Login1.login('super_user', 'n3ws3kr3tp4ssw0rd')
        True

        Test changes to last user:

        >>> Login1.change_password('banana',
        ...                        '1234567890',
        ...                        'n3ws3kr3tp4ssw0rd')
        >>> Login1.login('banana', '1234567890')
        False
        >>> Login1.login('banana', 'n3ws3kr3tp4ssw0rd')
        True

        Test changes to a middle user:

        >>> Login1.change_password('LameUser',
        ...                        't0t411ywr0ng',
        ...                        'n3ws3kr3tp4ssw0rd')
        >>> Login1.login('banana', 't0t411ywr0ng')
        False
        >>> Login1.login('banana', 'n3ws3kr3tp4ssw0rd')
        True


        Make sure changes to file are valid:

        >>> Login2 = LoginManager(temp_database)
        >>> Login2.login('super_user', 'n3ws3kr3tp4ssw0rd')
        True

        Clean `temp_database` if testing with `doctest`:

        >>> import os
        >>> os.remove(temp_database)

        Notes
        -----
        If a contact is available to reach the user, it should be used to warn
        him of an attempt to change the password.
        """
        if self.login(user, old_password):
            with self.database.open('br+') as database:
                # find user name
                line = ' '
                while line != '':
                    line = database.readline()
                    if line[:self._max_name_size].rstrip() == user:
                        new_hash = self.hasher.hash(new_password)
                        database.seek(-len(self._hash_example)-3, 1)
                        database.write('\t'.join([new_hash, '\r\n']))
                        self.users[user] = new_hash
                        break

    def _reload_database(self):
        self.users = {}  # could use a more intuitive name
        with self.database.open('r') as database:
            lines = database.readlines()
            for user_pass in lines:
                user, pass_hash, new_line = user_pass.split('\t')
                self.users[user.rstrip()] = pass_hash

    def _is_username_valid(self, user_name):
        """
        Check if user name is valid, raise `ValueError` if it's not.
        """
        if user_name in self.users.keys():
            raise ValueError('User "%s" already exists!' % (user_name,))

        if len(user_name) > self._max_name_size:
            raise ValueError('User name "%s..." too long!' %
                             (user_name[:self._max_name_size+1],))

        # From https://stackoverflow.com/questions/1323364/
        search = re.compile(r'[^A-Za-z0-9._-]').search
        if bool(search(user_name)):
            raise ValueError('Invalid characters in user name!\n'
                             'Use only A-Z, a-z, 0-9 and `.`, `-` or `_`.')


def _test():
    """
    Test functions using the doctext examples.
    """
    import doctest
    doctest.testmod(verbose=False)


if __name__ == "__main__":
    # use the examples in documentation to test the code.
    _test()

You (and anyone) are free to use and alter at leisure, but don't take this as a good practice example - just an amateur's opinion on the subject.

berna1111
  • 1,811
  • 1
  • 18
  • 23