github.com/enmand/kubernetes@v1.2.0-alpha.0/third_party/htpasswd/htpasswd.py (about) 1 #!/usr/bin/python 2 # -*- coding: utf-8 -*- 3 # 4 # Copyright (C) 2008-2013 Edgewall Software 5 # Copyright (C) 2008 Eli Carter 6 # All rights reserved. 7 # 8 # This software is licensed as described in the file COPYING, which 9 # you should have received as part of this distribution. The terms 10 # are also available at http://trac.edgewall.com/license.html. 11 # 12 # This software consists of voluntary contributions made by many 13 # individuals. For the exact contribution history, see the revision 14 # history and logs, available at http://trac.edgewall.org/. 15 16 """Replacement for htpasswd""" 17 18 import os 19 import sys 20 import random 21 from optparse import OptionParser 22 23 # We need a crypt module, but Windows doesn't have one by default. Try to find 24 # one, and tell the user if we can't. 25 try: 26 import crypt 27 except ImportError: 28 try: 29 import fcrypt as crypt 30 except ImportError: 31 sys.stderr.write("Cannot find a crypt module. " 32 "Possibly http://carey.geek.nz/code/python-fcrypt/\n") 33 sys.exit(1) 34 35 36 def wait_for_file_mtime_change(filename): 37 """This function is typically called before a file save operation, 38 waiting if necessary for the file modification time to change. The 39 purpose is to avoid successive file updates going undetected by the 40 caching mechanism that depends on a change in the file modification 41 time to know when the file should be reparsed.""" 42 try: 43 mtime = os.stat(filename).st_mtime 44 os.utime(filename, None) 45 while mtime == os.stat(filename).st_mtime: 46 time.sleep(1e-3) 47 os.utime(filename, None) 48 except OSError: 49 pass # file doesn't exist (yet) 50 51 def salt(): 52 """Returns a string of 2 randome letters""" 53 letters = 'abcdefghijklmnopqrstuvwxyz' \ 54 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ 55 '0123456789/.' 56 return random.choice(letters) + random.choice(letters) 57 58 59 class HtpasswdFile: 60 """A class for manipulating htpasswd files.""" 61 62 def __init__(self, filename, create=False): 63 self.entries = [] 64 self.filename = filename 65 if not create: 66 if os.path.exists(self.filename): 67 self.load() 68 else: 69 raise Exception("%s does not exist" % self.filename) 70 71 def load(self): 72 """Read the htpasswd file into memory.""" 73 lines = open(self.filename, 'r').readlines() 74 self.entries = [] 75 for line in lines: 76 username, pwhash = line.split(':') 77 entry = [username, pwhash.rstrip()] 78 self.entries.append(entry) 79 80 def save(self): 81 """Write the htpasswd file to disk""" 82 wait_for_file_mtime_change(self.filename) 83 open(self.filename, 'w').writelines(["%s:%s\n" % (entry[0], entry[1]) 84 for entry in self.entries]) 85 86 def update(self, username, password): 87 """Replace the entry for the given user, or add it if new.""" 88 pwhash = crypt.crypt(password, salt()) 89 matching_entries = [entry for entry in self.entries 90 if entry[0] == username] 91 if matching_entries: 92 matching_entries[0][1] = pwhash 93 else: 94 self.entries.append([username, pwhash]) 95 96 def delete(self, username): 97 """Remove the entry for the given user.""" 98 self.entries = [entry for entry in self.entries 99 if entry[0] != username] 100 101 102 def main(): 103 """ 104 %prog -b[c] filename username password 105 %prog -D filename username""" 106 # For now, we only care about the use cases that affect tests/functional.py 107 parser = OptionParser(usage=main.__doc__) 108 parser.add_option('-b', action='store_true', dest='batch', default=False, 109 help='Batch mode; password is passed on the command line IN THE CLEAR.' 110 ) 111 parser.add_option('-c', action='store_true', dest='create', default=False, 112 help='Create a new htpasswd file, overwriting any existing file.') 113 parser.add_option('-D', action='store_true', dest='delete_user', 114 default=False, help='Remove the given user from the password file.') 115 116 options, args = parser.parse_args() 117 118 def syntax_error(msg): 119 """Utility function for displaying fatal error messages with usage 120 help. 121 """ 122 sys.stderr.write("Syntax error: " + msg) 123 sys.stderr.write(parser.get_usage()) 124 sys.exit(1) 125 126 if not (options.batch or options.delete_user): 127 syntax_error("Only batch and delete modes are supported\n") 128 129 # Non-option arguments 130 if len(args) < 2: 131 syntax_error("Insufficient number of arguments.\n") 132 filename, username = args[:2] 133 if options.delete_user: 134 if len(args) != 2: 135 syntax_error("Incorrect number of arguments.\n") 136 password = None 137 else: 138 if len(args) != 3: 139 syntax_error("Incorrect number of arguments.\n") 140 password = args[2] 141 142 passwdfile = HtpasswdFile(filename, create=options.create) 143 144 if options.delete_user: 145 passwdfile.delete(username) 146 else: 147 passwdfile.update(username, password) 148 149 passwdfile.save() 150 151 152 if __name__ == '__main__': 153 main()