- Python 92.3%
- Makefile 4.4%
- Shell 2.3%
- Dockerfile 1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
CI / syntax / lint / tests / staged install (push) Failing after 3s
The repo had no CI and no licence file, and the license metadata contradicted itself: debian/copyright and the RPM spec said MIT-0 while embedding the plain-MIT text with the attribution condition intact. - .forgejo/workflows/ci.yml: one job, six steps, each calling a Makefile target, so `make check` locally and CI run identical commands. - .forgejo/workflows/packages.yml: builds the .deb in ubuntu:24.04 and the .rpm in rockylinux:9 as job containers (no docker-in-docker), inspects each, uploads both as artifacts. Gated on a version/tag check first. - Makefile: add check, syntax, lint, shellcheck, version-check, stage-check. A gate fails with an install hint when its tool is missing rather than passing quietly. dist-deb and dist-rpm now assert the artifact exists instead of swallowing the move with `|| true`. - tools/check-version.py: the version is written four times (Makefile, useradmin.py __version__, debian/changelog, spec) with nothing deriving one from another. It globs the built .deb by the Makefile's value, so drift silently leaves the package outside dist/. - LICENSE: MIT-0, and the attribution clause removed from debian/copyright to match the label the spec and changelog already used. - ruff.toml: E4/E5/E7/E9/F/B. E741 and B904 are ignored with their sites listed - pre-existing debt, documented in CONTRIBUTING.md. F841 stays on: the one violation (a dead backend.groups() read in cli_list) is silenced inline with a TODO rather than by switching the rule off tree-wide. - .gitignore/.gitattributes: ignore build, cache and local agent state; force LF, since a CRLF useradmin.py breaks the shebang in the shipped binary. .pi/remote-pi/config.json holds a machine-specific agent name, so it is untracked. - packaging/rhel/useradmin.spec: ship LICENSE as %license. Correct the changelog dates (debian/changelog had "Sat, 05 Oct 2026", which is a Monday; the spec said 2024). Verified: make check green; RPM built on rockylinux:9 and DEB on ubuntu:24.04 with the exact CI step bodies (the DEB needed build-essential added to dpkg-checkbuilddeps, which is now in the workflow). |
||
| .forgejo/workflows | ||
| debian | ||
| packaging/rhel | ||
| tests | ||
| tools | ||
| .gitattributes | ||
| .gitignore | ||
| CONTRIBUTING.md | ||
| DESIGN-useradmin-v3.md | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| ruff.toml | ||
| useradmin.desktop | ||
| useradmin.py | ||
| useradmin.svg | ||
useradmin
A graphical tool for managing local user accounts and groups on a single Linux machine.
Purpose
Designed for a technical lead who manages ~12 local accounts on an airgapped Linux box and is uncomfortable in a terminal. The tool creates, modifies, and deletes accounts; sets passwords; manages group membership; and handles expiry.
All mutations use shadow-utils (useradd, usermod, userdel, groupadd, groupdel, gpasswd, chage, passwd). State is read through getent. Passwords are set via passwd under a pty so PAM policy is enforced.
Target environments
- Ubuntu 22.04+, Rocky 8/9, RHEL 8/9
- Python 3.6+ (stdlib only — no PyPI packages)
- Tkinter + ttk (
python3-tkon Ubuntu,python3-tkinteron RHEL/Rocky)
Installation
From RPM (RHEL/Rocky)
sudo dnf install useradmin-*.rpm
From DEB (Ubuntu)
sudo apt install ./useradmin_*.deb
Manual install
sudo cp useradmin.py /usr/bin/useradmin
sudo chmod +x /usr/bin/useradmin
sudo cp useradmin.desktop /usr/share/applications/
sudo cp useradmin.svg /usr/share/icons/hicolor/scalable/apps/
Usage
GUI mode
sudo useradmin
Or launch from the desktop menu (System → User Admin).
CLI mode
sudo useradmin --no-gui list
Dry-run mode
sudo useradmin --dry-run
Shows the commands that would be executed without running them.
Features
- Account list: table with name, UID, full name, status, expiry; sortable and filterable
- Create account: username, full name, primary group, supplementary groups, password, expiry
- Modify account: full name, shell, groups, password, expiry, aging
- Delete account: with confirmation and optional home directory removal
- Group management: create/delete groups, add/remove members
- Password management: set password via
passwdunder pty (PAM-enforced) - Dry-run mode: preview commands before executing
Safety
- Verify after apply: every mutation re-reads state and shows before/after
- Input validation: usernames, shells, GECOS fields validated before execution
- No blank-password unlock: refuses to unlock accounts with no password (W3 fix)
- No fail-open reads: unreadable shadow → status "unknown", never a guess (W4 fix)
- Flags preserved on relaunch:
--dry-runsurvives sudo relaunch (W1 fix) - GECOS round-trip: commas in "Doe, John" preserved, not mangled (W9 fix)
- Aging from shadow fields: uses shadow indices, not
chage -ltext (W5 fix) - All columns sortable: column IDs and sort keys defined together (W6 fix)
- argparse for CLI:
--helpprints help, doesn't launch GUI (W17 fix)
Logging
All mutations logged to syslog (facility LOCAL6). Passwords are never logged.
Configuration
/etc/useradmin/config.json:
{
"managed_groups": ["devs", "ops"],
"default_shell": "/bin/bash",
"create_home": true,
"home_base": "/home",
"min_uid": 1000
}
managed_groups: if set, only accounts in these groups are showndefault_shell: default for new accountsmin_uid: hide system accounts below this UID
Testing
All gates
make check
Runs syntax, lint, shellcheck, version consistency, unit tests and a staged install - the same steps CI runs. See CONTRIBUTING.md for tool setup and the release flow.
Container smoke tests
./tests/smoke-test.sh
Builds Docker images for Ubuntu 22.04, Ubuntu 24.04, Rocky 8, Rocky 9, and runs smoke tests in each.
Security
- GUI runs as root (
sudo useradmin) - All reads via
getent(NSS-correct, no direct file manipulation) - All mutations via shadow-utils (no
shell=True, no option injection) - Passwords only exist transiently in memory, never in argv or logs
- All subprocess calls have timeouts (10s default, 30s for
useradd -m)
Wayland
On Wayland, root GUI cannot draw on the user's session. The desktop icon uses sudo -A with SUDO_ASKPASS to work around this. Alternatively, launch from a terminal with sudo useradmin.
License
MIT-0 (MIT No Attribution) - see LICENSE. Use at your own risk; this tool runs as root and edits account databases.