Imported sources.
This commit is contained in:
132
scripts/accounts_editor.py
Normal file
132
scripts/accounts_editor.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
|
||||
|
||||
APP_TITLE = "OpenLDAP Accounts CSV Editor"
|
||||
|
||||
|
||||
FILES = {
|
||||
"Users": {
|
||||
"filename": "users.csv",
|
||||
"keys": ["uid", "gn", "sn", "mail"],
|
||||
"labels": {
|
||||
"uid": "UID",
|
||||
"gn": "Given Name",
|
||||
"sn": "Surname",
|
||||
"mail": "Email",
|
||||
},
|
||||
},
|
||||
"Admins": {
|
||||
"filename": "admins.csv",
|
||||
"keys": ["uid", "gn", "sn", "mail"],
|
||||
"labels": {
|
||||
"uid": "UID",
|
||||
"gn": "Given Name",
|
||||
"sn": "Surname",
|
||||
"mail": "Email",
|
||||
},
|
||||
},
|
||||
"POSIX Users": {
|
||||
"filename": "posix-users.csv",
|
||||
"keys": ["uid", "gn", "sn", "mail", "uidNumber", "gidNumber"],
|
||||
"labels": {
|
||||
"uid": "UID",
|
||||
"gn": "Given Name",
|
||||
"sn": "Surname",
|
||||
"mail": "Email",
|
||||
"uidNumber": "POSIX UID",
|
||||
"gidNumber": "POSIX GID",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def read_csv(path: Path, keys: list[str]) -> pd.DataFrame:
|
||||
if not path.exists():
|
||||
return pd.DataFrame(columns=keys)
|
||||
|
||||
df = pd.read_csv(
|
||||
path,
|
||||
header=None,
|
||||
names=keys,
|
||||
comment="#",
|
||||
skip_blank_lines=True,
|
||||
dtype=str,
|
||||
keep_default_na=False,
|
||||
usecols=range(len(keys)),
|
||||
)
|
||||
return df.apply(lambda col: col.str.strip())
|
||||
|
||||
|
||||
def write_csv(path: Path, keys: list[str], df: pd.DataFrame) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
cleaned = df.fillna("").astype(str)
|
||||
cleaned = cleaned.apply(lambda col: col.str.strip())
|
||||
cleaned = cleaned[cleaned.ne("").any(axis=1)]
|
||||
|
||||
with path.open("w", newline="", encoding="utf-8") as f:
|
||||
f.write(f"# {','.join(keys)}\n")
|
||||
cleaned.to_csv(f, index=False, header=False)
|
||||
|
||||
|
||||
def render_page(accounts_dir: Path, page_name: str) -> None:
|
||||
spec = FILES[page_name]
|
||||
csv_path = accounts_dir / spec["filename"]
|
||||
|
||||
st.subheader(page_name)
|
||||
st.caption(f"File: {csv_path}")
|
||||
|
||||
df = read_csv(csv_path, spec["keys"])
|
||||
|
||||
column_config = {
|
||||
key: st.column_config.TextColumn(label=spec["labels"].get(key, key), width="medium")
|
||||
for key in spec["keys"]
|
||||
}
|
||||
|
||||
edited = st.data_editor(
|
||||
df,
|
||||
width="stretch",
|
||||
num_rows="dynamic",
|
||||
column_config=column_config,
|
||||
hide_index=True,
|
||||
key=f"editor-{page_name}",
|
||||
)
|
||||
|
||||
with st.container(horizontal=True):
|
||||
if st.button("Save", type="primary", width=120, key=f"save-{page_name}"):
|
||||
write_csv(csv_path, spec["keys"], edited)
|
||||
st.success(f"Saved {spec['filename']}")
|
||||
|
||||
if st.button("Reload", width=120, key=f"reload-{page_name}"):
|
||||
st.rerun()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
st.set_page_config(page_title=APP_TITLE, layout="wide")
|
||||
st.title(APP_TITLE)
|
||||
|
||||
default_dir = os.environ.get("OPENLDAP_ACCOUNTS_DIR", "~/app-data/openldap/accounts")
|
||||
|
||||
with st.sidebar:
|
||||
st.header("Settings")
|
||||
accounts_dir_raw = st.text_input("Accounts directory", value=default_dir)
|
||||
page_name = st.radio("Page", list(FILES.keys()))
|
||||
|
||||
accounts_dir = Path(accounts_dir_raw).expanduser()
|
||||
st.caption("This editor manages the CSV files used by OpenLDAP bootstrap.")
|
||||
|
||||
if not accounts_dir.exists():
|
||||
st.warning(f"Directory does not exist yet: {accounts_dir}")
|
||||
|
||||
render_page(accounts_dir, page_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user