Edgewall Software

source: tags/trac-0.11/contrib/htpasswd.py

Last change on this file was 6651, checked in by ecarter, 4 years ago

Slight code cleanup in htpasswd.py

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 3.9 KB
Line 
1#!/usr/bin/python
2"""Replacement for htpasswd"""
3# Original author: Eli Carter
4
5import os
6import sys
7import random
8from optparse import OptionParser
9
10# We need a crypt module, but Windows doesn't have one by default.  Try to find
11# one, and tell the user if we can't.
12try:
13    import crypt
14except ImportError:
15    try:
16        import fcrypt as crypt
17    except ImportError:
18        sys.stderr.write("Cannot find a crypt module.  "
19                         "Possibly http://carey.geek.nz/code/python-fcrypt/\n")
20        sys.exit(1)
21
22
23def salt():
24    """Returns a string of 2 randome letters"""
25    letters = 'abcdefghijklmnopqrstuvwxyz' \
26              'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
27              '0123456789/.'
28    return random.choice(letters) + random.choice(letters)
29
30
31class HtpasswdFile:
32    """A class for manipulating htpasswd files."""
33
34    def __init__(self, filename, create=False):
35        self.entries = []
36        self.filename = filename
37        if not create:
38            if os.path.exists(self.filename):
39                self.load()
40            else:
41                raise Exception("%s does not exist" % self.filename)
42
43    def load(self):
44        """Read the htpasswd file into memory."""
45        lines = open(self.filename, 'r').readlines()
46        self.entries = []
47        for line in lines:
48            username, pwhash = line.split(':')
49            entry = [username, pwhash.rstrip()]
50            self.entries.append(entry)
51
52    def save(self):
53        """Write the htpasswd file to disk"""
54        open(self.filename, 'w').writelines(["%s:%s\n" % (entry[0], entry[1])
55                                             for entry in self.entries])
56
57    def update(self, username, password):
58        """Replace the entry for the given user, or add it if new."""
59        pwhash = crypt.crypt(password, salt())
60        matching_entries = [entry for entry in self.entries
61                            if entry[0] == username]
62        if matching_entries:
63            matching_entries[0][1] = pwhash
64        else:
65            self.entries.append([username, pwhash])
66
67    def delete(self, username):
68        """Remove the entry for the given user."""
69        self.entries = [entry for entry in self.entries
70                        if entry[0] != username]
71
72
73def main():
74    """%prog [-c] -b filename username password
75    Create or update an htpasswd file"""
76    # For now, we only care about the use cases that affect tests/functional.py
77    parser = OptionParser(usage=main.__doc__)
78    parser.add_option('-b', action='store_true', dest='batch', default=False,
79        help='Batch mode; password is passed on the command line IN THE CLEAR.'
80        )
81    parser.add_option('-c', action='store_true', dest='create', default=False,
82        help='Create a new htpasswd file, overwriting any existing file.')
83    parser.add_option('-D', action='store_true', dest='delete_user',
84        default=False, help='Remove the given user from the password file.')
85
86    options, args = parser.parse_args()
87
88    def syntax_error(msg):
89        """Utility function for displaying fatal error messages with usage
90        help.
91        """
92        sys.stderr.write("Syntax error: " + msg)
93        sys.stderr.write(parser.get_usage())
94        sys.exit(1)
95
96    if not options.batch:
97        syntax_error("Only batch mode is supported\n")
98
99    # Non-option arguments
100    if len(args) < 2:
101        syntax_error("Insufficient number of arguments.\n")
102    filename, username = args[:2]
103    if options.delete_user:
104        if len(args) != 2:
105            syntax_error("Incorrect number of arguments.\n")
106        password = None
107    else:
108        if len(args) != 3:
109            syntax_error("Incorrect number of arguments.\n")
110        password = args[2]
111
112    passwdfile = HtpasswdFile(filename, create=options.create)
113
114    if options.delete_user:
115        passwdfile.delete(username)
116    else:
117        passwdfile.update(username, password)
118
119    passwdfile.save()
120
121
122if __name__ == '__main__':
123    main()
Note: See TracBrowser for help on using the repository browser.