101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
|
#!/usr/bin/python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
$Id$
|
||
|
|
||
|
A python module for managing htpasswd files.
|
||
|
|
||
|
The code is based on http://trac.edgewall.org/export/9825/trunk/contrib/htpasswd.py
|
||
|
Original author: Eli Carter
|
||
|
|
||
|
Copyright 2010 Lars Kruse <devel@sumpfralle.de>
|
||
|
Copyright before 2010 Eli Carter
|
||
|
|
||
|
This module is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This module is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with This module. If not, see <http://www.gnu.org/licenses/>.
|
||
|
"""
|
||
|
|
||
|
import crypt
|
||
|
import shutil
|
||
|
import tempfile
|
||
|
import random
|
||
|
import os
|
||
|
|
||
|
|
||
|
def _get_random_salt():
|
||
|
"""Returns a string of 2 random 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:
|
||
|
self.load()
|
||
|
|
||
|
def load(self):
|
||
|
"""Read the htpasswd file into memory.
|
||
|
This function may raise an IOError exception.
|
||
|
"""
|
||
|
self.entries = {}
|
||
|
for line in open(self.filename, 'r').readlines():
|
||
|
username, pwhash = line.split(':', 1)
|
||
|
self.entries[username] = pwhash
|
||
|
|
||
|
def save(self):
|
||
|
"""Write the htpasswd file to disk
|
||
|
The file is written in a safe manner: first a temporary file with the
|
||
|
resulting content is created in the directory of the original file.
|
||
|
Afterwards it is moved to the original filename.
|
||
|
This function may raise an IOError exception.
|
||
|
"""
|
||
|
# create a temporary file besides the original file
|
||
|
temp_file, temp_filename = tempfile.mkstemp(
|
||
|
dir=os.path.dirname(self.filename), text=True)
|
||
|
try:
|
||
|
if os.path.isfile(self.filename):
|
||
|
# copy the original file mode (mod, timestamps)
|
||
|
shutil.copystat(self.filename, temp_filename)
|
||
|
for name, pwhash in self.entries.items():
|
||
|
os.write(temp_file, "%s:%s%s" % (name, pwhash, os.linesep))
|
||
|
os.close(temp_file)
|
||
|
except IOError:
|
||
|
try:
|
||
|
os.remove(temp_filename)
|
||
|
except IOError:
|
||
|
# ignore errors during failure handling
|
||
|
pass
|
||
|
else:
|
||
|
# move the temporary file to the original filename
|
||
|
os.rename(temp_filename, self.filename)
|
||
|
|
||
|
def update(self, username, password):
|
||
|
"""Replace the entry for the given user, or add it if new."""
|
||
|
pwhash = crypt.crypt(password, _get_random_salt())
|
||
|
self.entries[username] = pwhash
|
||
|
|
||
|
def delete(self, username):
|
||
|
"""Remove the entry for the given user."""
|
||
|
if username in self.entries:
|
||
|
self.entries.pop(username)
|
||
|
|
||
|
def get_usernames(self):
|
||
|
return self.entries.keys()
|
||
|
|