Last active
March 17, 2017 06:31
-
-
Save meson10/ff28b41c0fa7303841cce53eebb51c05 to your computer and use it in GitHub Desktop.
Ship this utility instead of installing htpasswd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python | |
"""Replacement for htpasswd""" | |
# Original author: Eli Carter | |
import os | |
import sys | |
import random | |
from optparse import OptionParser | |
# We need a crypt module, but Windows doesn't have one by default. Try to find | |
# one, and tell the user if we can't. | |
try: | |
import crypt | |
except ImportError: | |
try: | |
import fcrypt as crypt | |
except ImportError: | |
sys.stderr.write("Cannot find a crypt module. " | |
"Possibly http://carey.geek.nz/code/python-fcrypt/\n") | |
sys.exit(1) | |
def salt(): | |
"""Returns a string of 2 randome letters""" | |
letters = 'abcdefghijklmnopqrstuvwxyz' \ | |
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ | |
'0123456789/.' | |
return random.choice(letters) + random.choice(letters) | |
class HtpasswdFile(object): | |
"""A class for manipulating htpasswd files.""" | |
def __init__(self, filename, create=False): | |
self.entries = [] | |
self.filename = filename | |
if os.path.exists(self.filename): | |
self.load() | |
elif not create: | |
raise Exception("%s does not exist" % self.filename) | |
def load(self): | |
"""Read the htpasswd file into memory.""" | |
lines = open(self.filename, 'r').readlines() | |
self.entries = [] | |
for line in lines: | |
username, pwhash = line.split(':') | |
entry = [username, pwhash.rstrip()] | |
self.entries.append(entry) | |
def save(self): | |
"""Write the htpasswd file to disk""" | |
open(self.filename, 'w').writelines(["%s:%s\n" % (entry[0], entry[1]) | |
for entry in self.entries]) | |
def update(self, username, password, cleartext=False): | |
"""Replace the entry for the given user, or add it if new.""" | |
pwhash = password if cleartext is True else \ | |
crypt.crypt(password, salt()) | |
matching_entries = [entry for entry in self.entries | |
if entry[0] == username] | |
if matching_entries: | |
matching_entries[0][1] = pwhash | |
else: | |
self.entries.append([username, pwhash]) | |
def delete(self, username): | |
"""Remove the entry for the given user.""" | |
self.entries = [entry for entry in self.entries | |
if entry[0] != username] | |
def main(): | |
"""%prog [-c] -b filename username password | |
Create or update an htpasswd file""" | |
parser = OptionParser(usage=main.__doc__) | |
parser.add_option( | |
'--cleartext', action='store_true', dest='cleartext', | |
default=False, help='Use cleartext passwords?' | |
) | |
parser.add_option( | |
'-c', action='store_true', dest='create', default=False, | |
help='Create a new htpasswd file, overwriting any existing file.' | |
) | |
parser.add_option( | |
'-D', action='store_true', dest='delete_user', | |
default=False, help='Remove the given user from the password file.' | |
) | |
options, args = parser.parse_args() | |
def syntax_error(msg): | |
"""Utility function for displaying fatal error messages with usage | |
help. | |
""" | |
sys.stderr.write("Syntax error: " + msg) | |
sys.stderr.write(parser.get_usage()) | |
sys.exit(1) | |
# Non-option arguments | |
if len(args) < 2: | |
syntax_error("Insufficient number of arguments.\n") | |
filename, username = args[:2] | |
if options.delete_user: | |
if len(args) != 2: | |
syntax_error("Incorrect number of arguments.\n") | |
password = None | |
else: | |
if len(args) != 3: | |
syntax_error("Incorrect number of arguments.\n") | |
password = args[2] | |
passwdfile = HtpasswdFile(filename, create=options.create) | |
if options.delete_user: | |
passwdfile.delete(username) | |
else: | |
passwdfile.update(username, password, options.cleartext) | |
passwdfile.save() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage