[17820] | 1 | #!/usr/bin/env python |
---|
| 2 | # |
---|
| 3 | # kpasswd.py |
---|
| 4 | # |
---|
| 5 | # Set password for kofa instances. |
---|
| 6 | # |
---|
| 7 | # (c) 2024 WAeUP Germany |
---|
| 8 | # Licensed under AGPL 3.0 or newer |
---|
| 9 | # |
---|
| 10 | # This script sets the username and password for a principal of a `kofa` |
---|
| 11 | # instance. |
---|
| 12 | # |
---|
| 13 | # $ python kpasswd.py -l foo -c parts/etc/site.zcml not-secret |
---|
| 14 | # |
---|
| 15 | # sets the credentials for the superuser principal (`zope.manager`) to `foo` |
---|
| 16 | # (username) and `not-scret` (password). The given site.zcml will be updated |
---|
| 17 | # accordingly. |
---|
| 18 | # |
---|
| 19 | # Not giving a site.zcml path will print the hashed form of the given password: |
---|
| 20 | # |
---|
| 21 | # $ python kpasswd.py also-not-secret |
---|
| 22 | # {SSHA}EkqqJb8opEe1QxhETB57Ps2O62HS63Vu |
---|
| 23 | # |
---|
| 24 | from __future__ import print_function |
---|
| 25 | import argparse |
---|
| 26 | import os |
---|
| 27 | import sys |
---|
| 28 | import xml.etree.ElementTree as ET |
---|
| 29 | from zope.password.password import managers as ZOPE_PW_MANAGERS |
---|
| 30 | |
---|
| 31 | |
---|
| 32 | def parse_args(argv): |
---|
| 33 | p = argparse.ArgumentParser(description=( |
---|
| 34 | "Set kofa password. If path to local `site.zcml` is given, also " |
---|
| 35 | "updates this file. If not, the hashed password is displayed and " |
---|
| 36 | "can be set manually in `site.zcml`")) |
---|
| 37 | p.add_argument("password", nargs=1, help="Password to set") |
---|
| 38 | p.add_argument( |
---|
| 39 | "-c", |
---|
| 40 | "--config", |
---|
| 41 | help=( |
---|
| 42 | "path to the site.zcml configuration file (optional). If " |
---|
| 43 | "given, the respective file will be modified. Otherwise " |
---|
| 44 | "the hashed password is displayed on stdout." |
---|
| 45 | ), |
---|
| 46 | ) |
---|
| 47 | p.add_argument( |
---|
| 48 | "--id", |
---|
| 49 | help=( |
---|
| 50 | "Principal id found in site.zcml. `zope.manager? by default. " |
---|
| 51 | "Only considered if a config file was given." |
---|
| 52 | ), |
---|
| 53 | default="zope.manager", |
---|
| 54 | ) |
---|
| 55 | p.add_argument( |
---|
| 56 | "-l", |
---|
| 57 | "--login", |
---|
| 58 | help=( |
---|
| 59 | "Principal login name. `grok` by default. Only considered if a " |
---|
| 60 | "config file was given." |
---|
| 61 | ), |
---|
| 62 | default="grok", |
---|
| 63 | ) |
---|
| 64 | options = p.parse_args(argv[1:]) |
---|
| 65 | return options |
---|
| 66 | |
---|
| 67 | |
---|
| 68 | def main(argv=None): |
---|
| 69 | if argv is None: |
---|
| 70 | argv = sys.argv |
---|
| 71 | try: |
---|
| 72 | options = parse_args(argv) |
---|
| 73 | app = Application(options) |
---|
| 74 | app.run() |
---|
| 75 | except SystemExit as e: |
---|
| 76 | if e.code: |
---|
| 77 | return 2 |
---|
| 78 | return 0 |
---|
| 79 | |
---|
| 80 | |
---|
| 81 | class Application(object): |
---|
| 82 | def __init__(self, options): |
---|
| 83 | self.options = options |
---|
| 84 | |
---|
| 85 | def run(self): |
---|
| 86 | hashed = self.get_hashed_password(self.options.password[0]).decode("utf-8") |
---|
| 87 | if self.options.config is None: |
---|
| 88 | print(hashed) |
---|
| 89 | return |
---|
| 90 | path = os.path.abspath(self.options.config) |
---|
| 91 | xmltree = ET.parse(path).getroot() |
---|
| 92 | doc = open(path).read() |
---|
| 93 | for entry in xmltree.iter("{http://namespaces.zope.org/zope}principal"): |
---|
| 94 | if entry.get("id") != self.options.id: |
---|
| 95 | continue |
---|
| 96 | # Replace hashed password with new value |
---|
| 97 | doc = doc.replace(entry.get("password"), hashed) |
---|
| 98 | # Replace old login (username) with new one |
---|
| 99 | doc = doc.replace( |
---|
| 100 | 'login="%s"' % entry.get("login"), 'login="%s"' % self.options.login |
---|
| 101 | ) |
---|
| 102 | # Create a backup of the file we are going to modify |
---|
| 103 | os.rename(path, path + ".bak") |
---|
| 104 | with open(path, "w") as fd: |
---|
| 105 | fd.write(doc) |
---|
| 106 | print("Updated: %s" % path) |
---|
| 107 | |
---|
| 108 | def get_hashed_password(self, password, salt=None): |
---|
| 109 | manager = dict(ZOPE_PW_MANAGERS)["SSHA"] |
---|
| 110 | return manager.encodePassword(password, salt) |
---|