Edgewall Software

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

Last change on this file was 6651, checked in by Eli Carter, 16 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.