#!/usr/bin/env python # # kpasswd.py # # Set password for kofa instances. # # (c) 2024 WAeUP Germany # Licensed under AGPL 3.0 or newer # # This script sets the username and password for a principal of a `kofa` # instance. # # $ python kpasswd.py -l foo -c parts/etc/site.zcml not-secret # # sets the credentials for the superuser principal (`zope.manager`) to `foo` # (username) and `not-scret` (password). The given site.zcml will be updated # accordingly. # # Not giving a site.zcml path will print the hashed form of the given password: # # $ python kpasswd.py also-not-secret # {SSHA}EkqqJb8opEe1QxhETB57Ps2O62HS63Vu # from __future__ import print_function import argparse import os import sys import xml.etree.ElementTree as ET from zope.password.password import managers as ZOPE_PW_MANAGERS def parse_args(argv): p = argparse.ArgumentParser(description=( "Set kofa password. If path to local `site.zcml` is given, also " "updates this file. If not, the hashed password is displayed and " "can be set manually in `site.zcml`")) p.add_argument("password", nargs=1, help="Password to set") p.add_argument( "-c", "--config", help=( "path to the site.zcml configuration file (optional). If " "given, the respective file will be modified. Otherwise " "the hashed password is displayed on stdout." ), ) p.add_argument( "--id", help=( "Principal id found in site.zcml. `zope.manager? by default. " "Only considered if a config file was given." ), default="zope.manager", ) p.add_argument( "-l", "--login", help=( "Principal login name. `grok` by default. Only considered if a " "config file was given." ), default="grok", ) options = p.parse_args(argv[1:]) return options def main(argv=None): if argv is None: argv = sys.argv try: options = parse_args(argv) app = Application(options) app.run() except SystemExit as e: if e.code: return 2 return 0 class Application(object): def __init__(self, options): self.options = options def run(self): hashed = self.get_hashed_password(self.options.password[0]).decode("utf-8") if self.options.config is None: print(hashed) return path = os.path.abspath(self.options.config) xmltree = ET.parse(path).getroot() doc = open(path).read() for entry in xmltree.iter("{http://namespaces.zope.org/zope}principal"): if entry.get("id") != self.options.id: continue # Replace hashed password with new value doc = doc.replace(entry.get("password"), hashed) # Replace old login (username) with new one doc = doc.replace( 'login="%s"' % entry.get("login"), 'login="%s"' % self.options.login ) # Create a backup of the file we are going to modify os.rename(path, path + ".bak") with open(path, "w") as fd: fd.write(doc) print("Updated: %s" % path) def get_hashed_password(self, password, salt=None): manager = dict(ZOPE_PW_MANAGERS)["SSHA"] return manager.encodePassword(password, salt)