146 lines
4.5 KiB
Python
146 lines
4.5 KiB
Python
import os
|
|
from pkg_resources import resource_filename, cleanup_resources, resource_string
|
|
import atexit
|
|
from pathlib import Path
|
|
|
|
atexit.register(cleanup_resources, force=True)
|
|
os.environ["KRB5_CONFIG"] = str(
|
|
Path(resource_filename(__name__, "krb5.conf")).resolve()
|
|
)
|
|
|
|
import kadmin
|
|
import getpass
|
|
import argparse
|
|
import secrets
|
|
import string
|
|
import shutil
|
|
import logging
|
|
|
|
from ldap3 import Server, Connection, LEVEL
|
|
from ldap3.utils.conv import escape_filter_chars
|
|
from ldap3.utils.dn import escape_rdn
|
|
|
|
import subprocess
|
|
|
|
import smtplib
|
|
from jinja2 import Template
|
|
from email.message import EmailMessage
|
|
|
|
FROM_LDAP = object()
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--admin", default=getpass.getuser())
|
|
parser.add_argument("--verbose", action="store_true")
|
|
subparsers = parser.add_subparsers(dest="cmd", help="command")
|
|
reset_password = subparsers.add_parser(
|
|
"reset_password",
|
|
help="change user password to newly generated one and send it to his email address from LDAP",
|
|
)
|
|
reset_password.add_argument("user")
|
|
reset_password.add_argument(
|
|
"--show-password", action="store_true", help="print generated password"
|
|
)
|
|
reset_password.add_argument("email_address", default=FROM_LDAP, nargs="?")
|
|
|
|
APG_CMD = shutil.which("apg")
|
|
|
|
|
|
def generage_password(length=15):
|
|
if APG_CMD is None:
|
|
logging.warning("apg command not found. Using built in password generator")
|
|
pool = string.ascii_lowercase + string.ascii_uppercase + string.digits
|
|
password = "".join([secrets.choice(pool) for _ in range(length)])
|
|
else:
|
|
password = (
|
|
subprocess.run(
|
|
[APG_CMD, "-m", str(length), "-n", "1", "-M", "NCL"],
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
.stdout.decode()
|
|
.strip()
|
|
)
|
|
|
|
if len(password) != length:
|
|
raise Exception("Password generation failed")
|
|
return password
|
|
|
|
|
|
def main():
|
|
args = parser.parse_args()
|
|
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
|
|
if args.cmd == "reset_password":
|
|
admin_pass = getpass.getpass(f"{args.admin}@HACKERSPACE.PL password: ")
|
|
logging.debug("initializing kadmin")
|
|
k = kadmin.init_with_password(f"{args.admin}/admin", admin_pass)
|
|
logging.debug("kadmin initialized")
|
|
user = args.user
|
|
if user is None:
|
|
user = input("User: ")
|
|
p = k.get_principal(user)
|
|
|
|
password = generage_password()
|
|
if args.email_address is not FROM_LDAP:
|
|
address = args.email_address
|
|
else:
|
|
address = get_email_address(args.admin, admin_pass, user)
|
|
|
|
if args.show_password:
|
|
print(f'password: "{password}"')
|
|
|
|
i = input(
|
|
f"Type yes to reset {user}'s password and send email to {address}\n"
|
|
).strip()
|
|
if i != "yes":
|
|
print("Aborted")
|
|
return
|
|
|
|
p.change_password(password)
|
|
print("password changed")
|
|
|
|
send_mail(args.admin, admin_pass, password, user, address)
|
|
print("email sent")
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
def get_email_address(admin, admin_pass, uid):
|
|
logging.debug("fetching email address from LDAP")
|
|
s = Server("ldap.hackerspace.pl", use_ssl=True)
|
|
with Connection(
|
|
s,
|
|
user=f"uid={escape_rdn(admin)},ou=People,dc=hackerspace,dc=pl",
|
|
password=admin_pass,
|
|
raise_exceptions=True,
|
|
) as c:
|
|
logging.debug("connected to LDAP server")
|
|
c.search(
|
|
search_base="ou=People,dc=hackerspace,dc=pl",
|
|
search_filter=f"(uid={escape_filter_chars(uid)})",
|
|
search_scope=LEVEL,
|
|
attributes=["mailRoutingAddress"],
|
|
)
|
|
if not c.entries:
|
|
raise Exception("empty response")
|
|
if len(c.entries) > 1:
|
|
raise Exception("too many responses")
|
|
address = c.entries[0]["mailRoutingAddress"]
|
|
logging.debug(f"got mail address from LDAP: {address}")
|
|
return address
|
|
|
|
|
|
def send_mail(admin, admin_password, password, user, address):
|
|
mail_template = Template(
|
|
resource_string(__name__, "password_reset.jinja2").decode()
|
|
)
|
|
msg = EmailMessage()
|
|
config = {"password": password, "user": user, "admin": admin}
|
|
msg.set_content(mail_template.render(config))
|
|
msg["Subject"] = f"Password reset for {user}@hackerspace.pl"
|
|
msg["From"] = f"{admin}@hackerspace.pl"
|
|
msg["To"] = address
|
|
|
|
with smtplib.SMTP_SSL("mail.hackerspace.pl") as s:
|
|
s.login(admin, admin_password)
|
|
s.send_message(msg)
|