views:

44

answers:

1

Hello:

A series of applications I'm writing require that the user be able to read from a filesystem with KLOG authentication. Some functions require the user to have KLOG tokens (i.e., be authenticated) and others don't. I wrote a small Python decorator so that I can refactor the "you must be KLOGed" functionality within my modules:

# this decorator is defined in ``mymodule.utils.decorators``
def require_klog(method):
    def require_klog_wrapper(*args, **kwargs):
        # run the ``tokens`` program to see if we have KLOG tokens
        out = subprocess.Popen('tokens', stdout=subprocess.PIPE)
        # the tokens (if any) are located in lines 4:n-1
        tokens_list = out.stdout.readlines()[3:-1]
        if tokens_list == []:
            # this is where I launch KLOG (if the user is not authenticated)
            subprocess.Popen('klog')
        out = method(*args, **kwargs)
        return out
    return require_klog_wrapper

# once the decorator is defined, any function can use it as follows:
from mymodule.utils.decorators import require_klog
@require_klog
def my_function():
 # do something (if not KLOGed, it SHUOLD ask for the password... but it does not!)

It is all very simple. Except when I tried to apply the following logic: "if the user is not KLOGed, run KLOG and ask for the password".

I do this using subprocess.Popen('klog') and the password: prompt does come up to the terminal. However, when I write the password it actually is echoed back to the terminal and, worse, nothing happens upon hitting return.

Edit:

After Alex's fast and correct response I solved the problem as follows:

  • I erased all the *.pyc files from my module's directory (yes - this made a difference)
  • I used getpass.getpass() to store the password in a local variable
  • I called the KLOG command with the -pipe option
  • I passed the locally-stored password to the pipe via the pipe's write method

Below is the corrected decorator:

def require_klog(method):
    def require_klog_wrapper(*args, **kwargs):
        # run the ``tokens`` program to see if we have KLOG tokens
        out = subprocess.Popen('tokens', stdout=subprocess.PIPE)
        # the tokens (if any) are located in lines 4:n-1
        tokens_list = out.stdout.readlines()[3:-1]
        if tokens_list == []:
            args = ['klog', '-pipe']
            # this is the custom pwd prompt 
            pwd = getpass.getpass('Type in your AFS password: ') 
            pipe = subprocess.Popen(args, stdin=subprocess.PIPE)
            # here is where the password is sent to the program
            pipe.stdin.write(pwd) 
        return method(*args, **kwargs)
    return require_klog_wrapper
+2  A: 

Apparently, your script (or something else it's spawning later) and the subprocess running klog are "competing" for the /dev/tty -- and the subprocess is losing (after all, you're not calling the wait method of the object returned from subprocess.Popen, to ensure you wait until it terminates before continuing, so a race condition of some kind would be hardly surprising).

If a wait does not suffice, I would work around this by putting

pwd = getpass.getpass('password:')

in the Python code (with an import getpass at the top of course), then running klog with the -pipe argument and stdin=subprocess.PIPE, and writing pwd to that pipe (with a call to the communicate method of the object returned from subprocess.Popen).

Alex Martelli
Thanks Alex - the `-pipe` + `getpass()` solution worked great.
Arrieta
@Arrieta, you're welcome!
Alex Martelli