From 953cb2f9355be24229f4c01545c05b1fa70ce98f Mon Sep 17 00:00:00 2001 From: lars Date: Tue, 9 Feb 2021 21:12:49 +0000 Subject: [PATCH] htman: switch from bobo to bottle bobo does not support python3 --- htman/htman.py | 121 +++++++++++++++++++++++++++------------------- htman/htpasswd.py | 18 +++---- 2 files changed, 78 insertions(+), 61 deletions(-) diff --git a/htman/htman.py b/htman/htman.py index 881309f..0314d08 100644 --- a/htman/htman.py +++ b/htman/htman.py @@ -1,8 +1,5 @@ -#!/usr/bin/python2.5 -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 """ -$Id$ - A web interface for managing htpasswd files. Copyright 2010 Lars Kruse @@ -21,21 +18,23 @@ You should have received a copy of the GNU General Public License along with This module. If not, see . """ -import sys +import configparser +import re import os -# add the current path to the python path - for "htpasswd" -sys.path.insert(0, os.path.dirname(__file__)) -# necessary for etch -sys.path.insert(1, '/usr/share/pyshared') -import htpasswd -import bobo +import sys + +import bottle import genshi.template import genshi.filters -import ConfigParser -import re + +import htpasswd + BASE_DIR = os.path.dirname(__file__) -CONFIG_FILE_LOCATIONS = [os.path.join(BASE_DIR, "htman.conf"), '/etc/htman/htman.conf'] +CONFIG_FILE_LOCATIONS = [ + os.path.join(BASE_DIR, "htman.conf"), + '/etc/htman/htman.conf', +] TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates') REGEX = { "username": r"[a-zA-Z0-9_\-\.]+$", @@ -48,20 +47,28 @@ if "HTMAN_CONFIG" in os.environ: CONFIG_FILE_LOCATIONS = [os.environ["HTMAN_CONFIG"]] -@bobo.query('') +@bottle.route('') def redirect_frontpage(): - return bobo.redirect(web_defaults["base_url"]) + return bottle.redirect(web_defaults["base_url"]) -@bobo.query('/') +@bottle.route('/') def show_frontpage(): values = web_defaults.copy() return render("frontpage.html", **values) -@bobo.query('/password') -def change_password(zone=None, username=None, old_password=None, - new_password=None, new_password2=None): +@bottle.route('/password') +def change_password_get(zone=None): + return change_password(zone) + + +@bottle.route('/password', method='POST') +def change_password(zone=None): + username = bottle.request.forms.get('username') + old_password = bottle.request.forms.get('old_password') + new_password = bottle.request.forms.get('new_password') + new_password2 = bottle.request.forms.get('new_password2') if zone: zone = zone.strip() if username: @@ -94,33 +101,43 @@ def change_password(zone=None, username=None, old_password=None, htdb.save() values["success"] = "Password changed successfully." else: - values["error"] = "Authentication error: zone, username or password is invalid." + values["error"] = ( + "Authentication error: zone, username or password is invalid.") return render("password_change.html", input_data=input_data, **values) -@bobo.query('/admin') +@bottle.route('/admin') def show_files(): values = web_defaults.copy() # The template expects a list of tuples: (mapping name, admin-url). # We assume, that the admin-url is just below the main admin URL. Thus # there is no need for generating a specific URL. - all_zones = get_mapping().keys() - all_zones.sort() - values["mapping"] = [(zone_name, "%s%s/%s" % (web_defaults["base_url"], "admin/manage", zone_name)) for zone_name in all_zones] + all_zones = sorted(get_mapping().keys()) + values["mapping"] = [ + (zone_name, "%s%s/%s" % ( + web_defaults["base_url"], "admin/manage", zone_name)) + for zone_name in all_zones + ] return render("list_mappings.html", **values) def is_zone_valid(zone): - return zone and (zone in get_mapping()) and re.match(REGEX["mapping"], zone) + return ( + zone + and (zone in get_mapping()) + and re.match(REGEX["mapping"], zone) + ) -@bobo.query('/manage') +@bottle.route('/manage') def show_htpasswd(zone=None): if not is_zone_valid(zone): - return bobo.redirect(web_defaults["base_url"]) + return bottle.redirect(web_defaults["base_url"]) else: - # do a redirect: this allows the webserver to check the permissions for this URL - return bobo.redirect("%smanage/%s" % (web_defaults["base_url"], str(zone))) + # do a redirect: this allows the webserver to check the permissions + # for this URL + return bottle.redirect( + "%smanage/%s" % (web_defaults["base_url"], str(zone))) def get_htpasswd(zone, auto_create=False): @@ -132,16 +149,25 @@ def get_htpasswd(zone, auto_create=False): return None +@bottle.route('/admin/manage/:zone') +@bottle.route('/manage/:zone') +def mange_htpasswd_get(zone=None): + return manage_htpasswd(zone) + + # the alternative "/admin" URL allows to define super-user htaccess rules via # Apache's "Location" directive -@bobo.query('/admin/manage/:zone') -@bobo.query('/manage/:zone') -def manage_htpasswd(zone=None, action=None, username=None, password=None): +@bottle.route('/admin/manage/:zone', method='POST') +@bottle.route('/manage/:zone', method='POST') +def manage_htpasswd(zone=None): + action = bottle.request.forms.get('action') + username = bottle.request.forms.get('username') + password = bottle.request.forms.get('password') values = web_defaults.copy() values["error"] = None values["success"] = None if not is_zone_valid(zone): - return bobo.redirect(web_defaults["base_url"]) + return bottle.redirect(web_defaults["base_url"]) values["zone"] = zone htdb = get_htpasswd(zone, auto_create=True) if not htdb: @@ -174,7 +200,7 @@ def manage_htpasswd(zone=None, action=None, username=None, password=None): else: values["error"] = "The user does not exist!" elif action == "new": - if not username in htdb.get_usernames(): + if username not in htdb.get_usernames(): htdb.update(username, password) htdb.save() values["success"] = "User added" @@ -188,7 +214,7 @@ def manage_htpasswd(zone=None, action=None, username=None, password=None): def get_config(): - config = ConfigParser.SafeConfigParser() + config = configparser.ConfigParser() for filename in CONFIG_FILE_LOCATIONS: if os.path.isfile(filename): try: @@ -198,14 +224,17 @@ def get_config(): pass else: return config - print >>sys.stderr, "Failed to load config file from %s" % str(CONFIG_FILE_LOCATIONS) + print( + "Failed to load config file from %s" % str(CONFIG_FILE_LOCATIONS), + file=sys.stderr + ) sys.exit(1) def get_mapping(): try: mapping_file = config.get("Locations", "mapping") - except ConfigParser.NoOptionError: + except configparser.NoOptionError: print >>sys.stderr, "The location of the mapping file is not " \ "defined in the config file: [Locations] -> mapping" sys.exit(2) @@ -241,16 +270,16 @@ def get_htpasswd_filename(zone): try: htpasswd_directory = config.get("Locations", "htpasswd_directory") return os.path.join(htpasswd_directory, basename) - except ConfigParser.NoOptionError: + except configparser.NoOptionError: return os.path.abspath(basename) else: return basename - + def get_templates_dir(config): try: templates_dir = config.get("Locations", "templates") - except ConfigParser.NoOptionError: + except configparser.NoOptionError: # use the default templates_dir = TEMPLATES_DIR if not os.path.isdir(templates_dir): @@ -261,7 +290,7 @@ def get_templates_dir(config): def render(filename, input_data=None, **values): stream = loader.load(filename).generate(**values) - if not input_data is None: + if input_data is not None: stream |= genshi.filters.HTMLFormFiller(data=input_data) return stream.render("html", doctype="html") @@ -275,11 +304,3 @@ web_defaults = dict(config.items("Web")) if not web_defaults["base_url"].endswith("/"): web_defaults["base_url"] += "/" loader = genshi.template.TemplateLoader([templates_dir], auto_reload=False) - - -# this line allows to use mod_wsgi -# see: http://groups.google.com/group/bobo-web/msg/2ba55fc381658cd1 -# see: http://blog.dscpl.com.au/2009/08/using-bobo-on-top-of-modwsgi.html -if __name__.startswith("_mod_wsgi_"): - application = bobo.Application(bobo_resources=__name__) - diff --git a/htman/htpasswd.py b/htman/htpasswd.py index 454302b..6ce72ce 100644 --- a/htman/htpasswd.py +++ b/htman/htpasswd.py @@ -1,11 +1,9 @@ -#!/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 +The code is based on +http://trac.edgewall.org/export/9825/trunk/contrib/htpasswd.py Original author: Eli Carter Copyright 2010 Lars Kruse @@ -72,15 +70,15 @@ class HtpasswdFile: """ # create a temporary file besides the original file temp_file, temp_filename = tempfile.mkstemp( - dir=os.path.dirname(self.filename), text=True) + dir=os.path.dirname(self.filename), text=False) try: if os.path.isfile(self.filename): # copy the original file mode (mod, timestamps) shutil.copystat(self.filename, temp_filename) - sorted_names = self.entries.keys() - sorted_names.sort() + sorted_names = sorted(self.entries.keys()) for name in sorted_names: - os.write(temp_file, "%s:%s%s" % (name, self.entries[name], os.linesep)) + line = "%s:%s%s" % (name, self.entries[name], os.linesep) + os.write(temp_file, line.encode()) os.close(temp_file) except IOError: try: @@ -111,7 +109,5 @@ class HtpasswdFile: return False def get_usernames(self): - names = self.entries.keys() - names.sort() + names = sorted(self.entries.keys()) return names -