Cache user remote credentials for command line tool - python

I'm designing a Python command line tool that will essentially be a wrapper for a few REST APIs. The general idea is so that the user can make remote changes without having to leave the terminal.
The only detail I'm not really sure about is how to cache the user credentials so that they don't have to enter username and password every time they make a remote call. I'm worried about leaving user credentials exposed for long periods of time when they're not using the tool. Is there a typical way to do this without writing a file and making a thread that destroys the file after a certain time has passed?

I recommend using the keyring package from pypi, then you can wrap it up with some utility functions:
SERVICE_NAME = 'confluence_api'
def get_login_cli(username = None, prompt = False):
'''
Get the password for the username out of the keyring. If the password
isn't found in the keyring, ask for it from the command line.
'''
disp_username = False
if username is None or prompt:
username = getpass.getuser()
disp_username = True
passwd = keyring.get_password(SERVICE_NAME, username)
if passwd is None or prompt:
if disp_username:
print 'login: %s' % username
passwd = getpass.getpass()
set_password(username, passwd)
return (username, passwd)
def set_password(username, passwd):
'''
Writes the password to the keyring.
'''
keyring.set_password(SERVICE_NAME, username, passwd)
And then your run-time script can call it like so:
username, passwd = get_login_cli(username, **kwargs)
print("username = %s" % (username))
So after logging in, the password is cached and you won't be prompted the second time. keyring uses the native platform's keyring to store the credentials, and I believe after N time has passed, you will be prompted again, but you'll have to read the docs on keyring to know what N is.

Related

Get local variable after function call in python

I have a function in python that change my account password. That is something like follows :
def f(account):
new_password = # Some random string generation
# Do something and send password change request to remote
account.password = new_password
account.save()
In the second line of above function I send a post request to change my password and then do some processing and finally save my new password. In the processing phase some error has occurred, but unfortunately my request has been sent to server. I have called the function in python shell and now my password has changed but I don't have it. Is it possible to get new_password variable from my function in python shell?
First of all, that is not valid Python code. Functions are not defined with function, but with def. (It seems you updated your question after reading this).
If indeed the function is called with an account object, then the caller will have access to it after the call. So account.password should have the password, unless account.save() wiped it out.
For instance, this could work:
def f(account):
new_password = # Some random string generation
# Do something and send password change request to remote
account.password = new_password
account.save()
account = Account() # do here whatever you do to get an account object
try: # Trap errors
f(account)
except:
print("Something went wrong...")
finally: # Whether an exception occurred or not, print the password
print("The password is:", account.password)
Again, this can only provide the password if account.save() did not destroy the value assigned to account.password.
If you can change the f function to your needs, then include the error handling inside that function and have it return the password:
def f(account):
new_password = # Some random string generation
# Do something and send password change request to remote
account.password = new_password
try: # Trap errors
account.save()
except:
print("Something went wrong...")
return new_password
account = Account() # do here whatever you do to get an account object
new_password = f(account) # now you will have the password returned
print("The password is:", new_password)
If however you already called the function and the program ended, then it is of course too late now. You must take the necessary precautions before running the code, and only run it when you are sure you can recover the password whatever happens.

Mock prompts in Python 2.x.x for username and password credentials

I am trying to write a unit test in python that tests a username and password entry. I am using prompts for user input for both the username and password.
I have two separate functions that prompt the user for username and password. Both functions then check length. The password function also checks for exceptions, that were made by us, if the password length is zero. Are their ways to mock these exceptions?
I am struggling because I can get passed the username prompt but the pytest script holds after the username input. Is there a way to mock both username and password?
I am fairly new to python and I have looked through documentation but everything I have seen is usually for Python 3.x.x which we currently aren't implementing at this time. Any help would be much appreciated.
I think the init function is actually causing most of the headaches I am having. Is there a way around this?
class Login(object):
def __init__(self, args, env="test"):
created_token = self.login()
def login(self):
username = self.args.username
if len(username) == 0:
username = self._get_username()
password = self.args.password
if len(password) == 0:
password = self._get_password(username)
def _get_username(self):
return prompt('%s Username (%s): ' % (self.domain)
def _get_password(self):
if password is None:
password = prompt_sensitive('%s Password: ' % self.domain)
try:
except ParseError,e:
print "ParseError message"
except ClientError,e:
print "ClientError message"
You can use dependency injection to inject the prompt.
def password_function(input_func=None):
input_func = input_func or input
...
def test_password_function(self, ...):
fake_input = Mock()
...
result = password_function(fake_input)
This won't break existing code since the previous call signature is valid. e.g. password_function(). However if you don't have access to change the code then you can patch the input function.
import __builtin__
#mock.patch(__builtins__, 'input', return_value=...)
def test_password_function(self, mock_input):

Changing Active Directory user password in Python 3.x

I am trying to make a Python script that will open an LDAP connection to a server running AD, take a search entry (in this case a name), search for that entry and change that users password to a randomly generated password (as well as set the option to change password on logon) and then send them an automated secure email containing the new temporary password.
So far I have been able to connect to the server, and search for a single DN which returns. The temporary password is being generated, and an email is being sent (although the password is not hashed, and the email is not secure yet). However, I cannot find any information on where to go from here.
I have found Change windows user password with python however I see that this does not play well with AD, and the other LDAP in Python documentation I have been finding seems to be outdated from 2.x and no longer works. The documentation for ldap3 (https://media.readthedocs.org/pdf/ldap3/stable/ldap3.pdf) also doesnt seem to really mention anything for it, and exhaustive Googling has been fruitless. I am new to this kind of programming having only low level or academic knowledge previously, so this has been a bit frustrating but Python is my strongest language.
----------------EDITED CODE TO CURRENT STATUS-----------------------
#Takes input for name which will be used for search criterion
zid = input("ZID: ")
zid = str(zid).lower()
print(zid)
#Binds session to the server and opens a connection
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com', password = "<something>", auto_bind=True) %zid
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
sys.exit(0)
#Searches and prints LDAP entries
try:
base_dn = 'DC=<something>,DC=<something>,DC=<something>,DC=<something>,DC=com'
zid_filter = '(sAMAccountName=%s)' %zid
conn.search(base_dn, zid_filter, attributes=['mail'])
#i.e. "DN: CN=<First Last>,OU=<something>, DC= <something>
user_dn = str(conn.entries)
#i.e. "CN=<First Last>"
front = user_dn.find('C')
back = user_dn.find(',')
user_cn = user_dn[front:back]
#i.e. "<First Last>"
display_name = user_cn[3:]
#i.e. "first.last#<something>.com"
raw_email = str(conn.entries)
front = raw_email.find('mail: ')
back = raw_email.find('#<something>.com')
user_email = raw_email[front + 6:back] + '#<something>.com'
except:
print("Could not search entries")
#Generates random 12 digit alpha-numeric password
try:
new_password = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12))
print(new_password)
print("New password successfully generated")
except:
print("New password could not be generated")
#Set and replace AD Password
try:
conn.extend.microsoft.modify_password(user_dn, None, new_password)
print ("Active Directory password was set successfully!")
except:
print('Error setting AD password')
sys.exit(0)
Any suggestions on how to get/set the user password and hash the password for security purposes during this whole ordeal? For the email I imagine I can force it to use HTTPS and that would be sufficient, but the connection to the server passing the new_password to I would like to secure.
ldap3 contains a specific method for changing AD password, just add the following after you generated a new password:
dn = conn.entries[0].entry_get_dn() # supposing you got back a single entry
conn.extend.microsoft.modify_password(dn, None, new_password)
This should properly encode the password and store it in AD.
This code is working with Windows 2012 R2 AD:
First, install latest ldap3:
sudo pip3 install ldap
#!/usr/bin/python3
import ldap3
SERVER='127.0.0.1'
BASEDN="DC=domain,DC=com"
USER="user_domain_login_name#domain.com"
CURREENTPWD="current_password"
NEWPWD="new_password"
SEARCHFILTER='(&(userPrincipalName='+USER+')(objectClass=person))'
USER_DN=""
USER_CN=""
ldap_server = ldap3.Server(SERVER, get_info=ldap3.ALL)
conn = ldap3.Connection(ldap_server, USER, CURREENTPWD, auto_bind=True)
conn.start_tls()
print(conn)
conn.search(search_base = BASEDN,
search_filter = SEARCHFILTER,
search_scope = ldap3.SUBTREE,
attributes = ['cn', 'givenName', 'userPrincipalName'],
paged_size = 5)
for entry in conn.response:
if entry.get("dn") and entry.get("attributes"):
if entry.get("attributes").get("userPrincipalName"):
if entry.get("attributes").get("userPrincipalName") == USER:
USER_DN=entry.get("dn")
USER_CN=entry.get("attributes").get("cn")
print("Found user:", USER_CN)
if USER_DN:
print(USER_DN)
print(ldap3.extend.microsoft.modifyPassword.ad_modify_password(conn, USER_DN, NEWPWD, CURREENTPWD, controls=None))
else:
print("User DN is missing!")

twisted cred with http digest authentication

I'm trying to implement twisted cred with HTTP Digest Authentication, and I'm having some difficulty. I was able to get it to work with checkPassword, but I don't want to store my passwords in the clear in the database, obviously.
I'm storing the MD5 of username:realm:password in my password database, and I'm calculating it using:
from twisted.cred._digest import calcHA1
def calc_ha1(self, password, username=None):
if username is None:
username = self.avatarId
realm = self.digest_factory.digest.authenticationRealm
return calcHA1('md5', username, realm, password, None, None)
My password checker looks like this:
def requestAvatarId(self, credentials):
username = credentials.username
try:
user = self.session.query(models.User).filter_by(username=username).one()
except NoResultFound as e:
return defer.fail(credError.UnauthorizedLogin("No such administrator"))
if credentials.checkHash(user.password_hash):
return defer.succeed(username)
else:
return defer.fail(credError.UnauthorizedLogin("Bad password"))
However, when checkHash computes HA2 and combines it with the HA1 that I have in the DB, it does not match what the browser is sending. I stepped through the checkHash code with a debugger and everything is operating as I would expect. Does anyone have ideas?
Thanks
-s

Python Enter Password And Compare to Shadowed Password Database

Good Day.
Im trying to write a python script that will take a captured password then compare it
to the system shadowed password.
Im using Ubuntu 12.10 for this test. and running the script as sudo.
def login(user, password):
"Check if user would be able to login using password"
try:
pw1 = spwd.getspnam(user)[1]
allus = spwd.getspall()
print pw1
# pw2 = crypt.crypt(password, pw1[:2])
pw2 = crypt.crypt(password, '\$6\$SALTsalt\$')
print pw2
return pw1 == pw2
except KeyError:
return 0 # no such user
Now the above returns
2 diferent passwords but i do get the one from the shadowed.
So my question is how do i encrypt the supplied password so i can compare it to the
one retreived. Any Help would be awsome
Edit addon
def login(user, password):
"Check if user would be able to login using password"
try:
pw1 = spwd.getspnam(user)[1]
allus = spwd.getspall()
# print allus
print pw1
# pw2 = crypt.crypt(password, pw1[:2])
# pw2 = crypt.crypt(password, '\$6\$SALTsalt\$')
pw2 =hashlib.new()
pw2.update(password)
pw2.digest()
print pw2
return pw1 == pw2
except KeyError:
return 0 # no such user
That also did not work
How does one impliment the haslib to get the hash to match system password
I've made an example on how to authenticate using shadowed passwords. I added some comments to let the code speak for itself.
Some extra info:
https://en.wikipedia.org/wiki/Passwd#Shadow_file
http://docs.python.org/2/library/spwd.html
http://docs.python.org/2/library/crypt.html
Also note (from the crypt module docs):
This module implements an interface to the crypt(3) routine, which is a one-way hash function based upon a modified DES algorithm; see the Unix man page for further details. Possible uses include allowing Python scripts to accept typed passwords from the user, or attempting to crack Unix passwords with a dictionary.
Notice that the behavior of this module depends on the actual implementation of the crypt(3) routine in the running system. Therefore, any extensions available on the current implementation will also be available on this module.
This is also why you cannot use hashlib without problems.
import crypt # Interface to crypt(3), to encrypt passwords.
import getpass # To get a password from user input.
import spwd # Shadow password database (to read /etc/shadow).
def login(user, password):
"""Tries to authenticate a user.
Returns True if the authentication succeeds, else the reason
(string) is returned."""
try:
enc_pwd = spwd.getspnam(user)[1]
if enc_pwd in ["NP", "!", "", None]:
return "user '%s' has no password set" % user
if enc_pwd in ["LK", "*"]:
return "account is locked"
if enc_pwd == "!!":
return "password has expired"
# Encryption happens here, the hash is stripped from the
# enc_pwd and the algorithm id and salt are used to encrypt
# the password.
if crypt.crypt(password, enc_pwd) == enc_pwd:
return True
else:
return "incorrect password"
except KeyError:
return "user '%s' not found" % user
return "unknown error"
if __name__ == "__main__":
username = raw_input("Username:")
password = getpass.getpass()
status = login(username, password)
if status == True:
print("Logged in!")
else:
print("Login failed, %s." % status)

Categories

Resources