initial commit
commit
756f6cbe66
|
@ -0,0 +1,2 @@
|
||||||
|
Command line tool for managing user accounts at Warsaw Hackerspace. For now only
|
||||||
|
password reset is implemented.
|
|
@ -0,0 +1,3 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.python3Packages.callPackage (import ./default.nix) {}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{ buildPythonPackage, setuptools, jinja2, ldap3, apg, callPackage }:
|
||||||
|
|
||||||
|
let
|
||||||
|
python-kadmin = callPackage (import ./python-kadmin.nix) {};
|
||||||
|
in buildPythonPackage rec {
|
||||||
|
pname = "hs-admin-${version}";
|
||||||
|
version = "0.0.1";
|
||||||
|
propagatedBuildInputs = [
|
||||||
|
python-kadmin
|
||||||
|
setuptools
|
||||||
|
jinja2
|
||||||
|
ldap3
|
||||||
|
apg
|
||||||
|
];
|
||||||
|
src = ./.;
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
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)
|
|
@ -0,0 +1,13 @@
|
||||||
|
[libdefaults]
|
||||||
|
default_realm = HACKERSPACE.PL
|
||||||
|
[realms]
|
||||||
|
HACKERSPACE.PL = {
|
||||||
|
admin_server = hackerspace.pl
|
||||||
|
kdc = kerberos.hackerspace.pl
|
||||||
|
default_domain = hackerspace.pl
|
||||||
|
sasl-realm = HACKERSPACE.PL
|
||||||
|
}
|
||||||
|
|
||||||
|
[domain_realm]
|
||||||
|
.hackerspace.pl = HACKERSPACE.PL
|
||||||
|
hackerspace.pl = HACKERSPACE.PL
|
|
@ -0,0 +1,8 @@
|
||||||
|
Hi,
|
||||||
|
|
||||||
|
Password for your Warsaw Hackerspace account has been resetted.
|
||||||
|
|
||||||
|
user: {{ user }}
|
||||||
|
password: {{ password }}
|
||||||
|
|
||||||
|
You can change it here: https://profile.hackerspace.pl
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ buildPythonPackage, bison, krb5, fetchFromGitHub }:
|
||||||
|
|
||||||
|
buildPythonPackage rec {
|
||||||
|
pname = "python-kadmin-${version}";
|
||||||
|
version = "0.0.2";
|
||||||
|
nativeBuildInputs = [ bison ];
|
||||||
|
buildInputs = [ krb5 ];
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "nightfly19";
|
||||||
|
repo = "python-kadmin";
|
||||||
|
#rev = "c1acec9d197b79e3f51928aad6df0f99e86283c2";
|
||||||
|
#sha256 = "0jb0k998624scy204im068kwhnwa2l6vag69qi3hnmlpr1q3wh0z";
|
||||||
|
rev = "31d25f734b926b71e15d4c2f3a2e68decf8a465b";
|
||||||
|
sha256 = "1vnmrd9sz08sr3nsg1n1rgwrqai802hba8gqybgabblbza4kg4x1";
|
||||||
|
};
|
||||||
|
preConfigure = ''
|
||||||
|
substituteInPlace setup.py --replace '["/usr/include/", "/usr/include/et/"]' '["${krb5.dev}/include"]'
|
||||||
|
'';
|
||||||
|
postInstall = ''
|
||||||
|
ln -s $src $out
|
||||||
|
'';
|
||||||
|
#doCheck=false;
|
||||||
|
doCheck=true;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="hsadmin",
|
||||||
|
version="0.1",
|
||||||
|
description="Warsaw Hackerspace account managment cli",
|
||||||
|
author="vuko",
|
||||||
|
author_email="vuko@hackerspace.pl",
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"License :: OSI Approved :: zlib/libpng License",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
],
|
||||||
|
packages=["hsadmin"],
|
||||||
|
python_requires=">=3.8,",
|
||||||
|
install_requires=["kadmin", "setuptools", "jinja2", "ldap3"],
|
||||||
|
package_data={"hsadmin": ["krb5.conf", "password_reset.jinja2"]},
|
||||||
|
entry_points={
|
||||||
|
"console_scripts": [
|
||||||
|
"hs-admin=hsadmin.cmd:main",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
Loading…
Reference in New Issue