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