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