Skip to content

Instantly share code, notes, and snippets.

@parhammmm
Created September 24, 2012 10:01
Show Gist options
  • Select an option

  • Save parhammmm/3775241 to your computer and use it in GitHub Desktop.

Select an option

Save parhammmm/3775241 to your computer and use it in GitHub Desktop.

Revisions

  1. parhammmm created this gist Sep 24, 2012.
    123 changes: 123 additions & 0 deletions htpasswd.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,123 @@
    #!/usr/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:
    """A class for manipulating htpasswd files."""

    def __init__(self, filename, create=False):
    self.entries = []
    self.filename = filename
    if not create:
    if os.path.exists(self.filename):
    self.load()
    else:
    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):
    """Replace the entry for the given user, or add it if new."""
    pwhash = 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"""
    # For now, we only care about the use cases that affect tests/functional.py
    parser = OptionParser(usage=main.__doc__)
    parser.add_option('-b', action='store_true', dest='batch', default=False,
    help='Batch mode; password is passed on the command line IN THE CLEAR.'
    )
    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)

    if not options.batch:
    syntax_error("Only batch mode is supported\n")

    # 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)

    passwdfile.save()


    if __name__ == '__main__':
    main()