Repository navigation
Releases: chriswayneh/crowdkeeper
Releases · chriswayneh/crowdkeeper
Release list
v0.2.3
Changed
- Visible behaviour changes:
- Leavers:
jmlplansdeactivate-userbefore theremove-from-groupsteps. If deactivation
fails, the run stops with exit 1 and no group is removed. - Exit codes: a redirect, any non-2xx or non-JSON answer, a malformed Crowd response, and the
directory changing between the confirmed preview and the apply all exit 1 (nothing is applied
on drift). An unknown JML column now exits 2. - Stricter Crowd validation: a user without a true/false
activeflag, a non-string name field
or an out-of-range timestamp is now an error (exit 1) instead of being read with a default.
- Leavers:
Fixed
- Crowd millisecond timestamps convert exactly (integer arithmetic), so the year-9999 limit and
sub-millisecond values no longer drift from float rounding. jmlleavers are now deactivated before their group memberships are removed, so a failed removal
can no longer leave the account active (the plan order changed:deactivate-usercomes first).- The Crowd client no longer follows redirects and treats every non-2xx answer (including a 3xx to a
login page) as an error, so such a change is logged as failed instead of applied. --applyre-reads the directory and re-plans right before applying; if the plan differs from the
confirmed preview, it exits 1 and changes nothing.fixnow recomputes the audit for that check.- Deactivation sends only the identity fields and
active: false, not the whole fetched user, and
refuses to write if Crowd's answer isn't the requested user or lacks any profile field (Crowd's
PUT /useroverwrites them, so a missing one is never sent as blank). - If the re-plan before apply fails because the directory changed (for example a planned user was
deleted), it is reported as drift: exit 1 and nothing changed. - Crowd fields that drive decisions are validated:
activemust be true/false (a missing flag no
longer reads as active),display-nameandemailmust be strings, andlastAuthenticatedand
created-datemust be whole-number millisecond timestamps in range (lastAuthenticatedneeds a
non-emptyvalueslist; oversized digit strings are rejected before parsing). A Crowd error during
the re-check is reported as a Crowd error, not as drift. A 2xx answer that isn't JSON
(such as an HTML login page) is an error naming the request. Each exits 1. - Users read from Crowd are sorted by name, so the re-check before apply doesn't abort just because
Crowd returned the same users in a different order. - Malformed Crowd responses (missing or non-list
users/groups/attributes, entries without a
name) raise an error instead of reading as empty results (a falsely clean audit or zero removals). jmlCSV headers are matched after stripping spaces, sogroupsis no longer silently ignored,
and unknown columns are rejected with exit 2.reportreads the directory once, so all sections describe the same state.
Documentation
- README states that membership is direct-only (nested groups aren't read or changed) and documents
the re-check before apply.
v0.2.2
Changed
- Visible output change: in the plain table, CSV, Markdown report and CLI error messages, invisible
characters (tab, newline, ESC, bidi, zero-width and similar) now print as Python-style escapes
such as\t,\n,\u001band\u202e, and a literal backslash prints doubled (\\).
Use-o jsonfor exact names. Bad JML input now always exits 2.
Fixed
jmlparses the CSV strictly. Duplicate or blank column headers (for example twogroups
columns, which could plan removal of all of a user's groups) and broken quoting (an unclosed
quote, or text after a closing quote, which could still plan a removal or deactivation) are now
rejected with a line-numbered error, exit 2 (the usage-error code), and nothing is planned or
changed.- The Markdown
reportescapes[ ] ( ) ! *and HTML (&,<,>) in user and group names,
so a name can't render as a link, image, emphasis or HTML. Finding subjects shown in code
spans are left unescaped apart from backticks and newlines, since Markdown and HTML aren't
interpreted there. jmlreads the CSV as UTF-8 with an optional BOM. These also stop the run with a line-numbered
error and exit 2, before anything is planned: an empty file (no header), a missing required
header (action,username; was exit 1), a row with more or fewer values than the header
(short rows were previously accepted), bytes that aren't UTF-8, and any control, format or
separator character in a field (NUL, tab, ESC, C1, U+202E, U+200B, U+2028, line breaks inside
quotes).- The Markdown
reportalso escapes_and~and breaks autolinks (://,www.,@), and in
both escaped text and code spans it shows control, bidi, zero-width and line/paragraph separator
characters as visible escapes (\u001b), so report output can't inject terminal escapes (ESC, OSC 8)
or reorder text. - Row validation errors in
jml(unknown action or group, missing username, a mover or leaver who
doesn't exist) now exit 2 like other input errors (was 1), and their line numbers are the real
file lines, so blank lines no longer shift them. Through the Python API they raise
core.JmlInputError, aValueErrorsubclass;core.jml()takes an optionallineslist. - The plain table and CSV outputs and CLI error messages show control, bidi, zero-width and
separator characters as visible escapes (sharedcore.term_safehelper). JSON is unchanged: it
already writes them as\uXXXXescapes and keeps the exact value. term_safewrites those characters as Python-style escapes (\u001b,\t,\n) and doubles a
literal backslash, so a name containing the textU+001Bor\u001bcan't be mistaken for a
real control character.- The Markdown
reportescapes every.and puts each run of@in a code span, so bare domains
(evil.com,xn--...),//hostpaths and e-mail ormailto:addresses no longer autolink in
cmark-gfm (GitHub) or markdown-it linkify.
v0.2.1
Fixed
jmlnow checks every row up front, before any change, including whencore.jmlis called
directly withExecutor(apply=True): unknown action, empty username, and movers or leavers that
don't exist (users created by earlier joiner rows count). Previously a valid row before a bad one
could already have been applied through the API. All bad lines are reported together.- The
--applyconfirmation prompt refuses when stdin is not a terminal and-yisn't passed, so
piped input such asecho yes |can't confirm a change, and exits 2 (the usage-error code) so cron and CI
see a failure. Previously only end-of-input counted as no. Declining at an interactive prompt
still exits 0.
v0.2.0
Changed
- Breaking: new audit-log format. Each applied change now writes an
intentline before the
Crowd call and anappliedline after it; on error afailedline is written. Every line carries
seq(restarts at 1 each run) andrun_id(random per run), so(run_id, seq)identifies a change.
The change's own fields sit under a nesteddetailobject.failedlines addstage
(intent_log,change,applied_log),change_applied(true,false, ornullmeaning
unknown: Ctrl-C, timeout, lost response or 5xx during the Crowd call;falseonly for a 4xx
rejection or a failedintentwrite),errorandapplied_before_failure.
Readers of the old one-line-per-change log must be updated. auditlog.recordrejects reserved top-level field names (ts;event,actionanddetailare positional parameters and cannot be passed as extra fields).- Breaking:
CROWD_URLmust behttps://and must not contain credentials; the client refuses
to start otherwise (was a warning). Plainhttp://, localhost included, needs the new global
--insecureflag, which logs a warning. - Breaking:
jmlmovers: a blankgroupscell (empty, spaces, or only;) now leaves
memberships alone. Removing every group requires the explicit marker-ornone; mixing it
with group names is rejected with the line number. A single-quoted token ('none','-') is a
literal group name, so real groups with those names can still be listed alongside others. - Requires
requests>=2.32.4(CVE-2024-35195 was fixed in 2.32.0, CVE-2024-47081 in 2.32.4). - Error text masks an app password of 8+ characters anywhere, and a shorter one only where no letter
or digit touches it, so short passwords no longer mangle unrelated words. - Breaking: users with no recorded login are now
activity-unknown(review) instead of
inactive(deactivate), and are only flagged once the account is older than--days. --daysmust be a positive whole number (audit,fix,report, and the core API).jmlchecks the whole CSV first (requiredaction/usernamecolumns, extra values, unknown
groups, with line numbers) and makes no change if anything is wrong.- Prompts and status messages go to stderr; stdout carries only the selected output format.
Fixed
fix --applycould deactivate brand-new accounts that had not logged in yet.jml: several rows for one user are applied in order against the updated state (mover and back,
repeated joiner no longer creates twice).jml: a missinggroupscolumn, truncated row or blankgroupscell no longer removes every
mover membership; only an explicit-ornonedoes.- A Ctrl-C while writing a change's
appliedline now logs afailedentry with
stage: applied_logandchange_applied: truebefore the interrupt propagates.
Added
SECURITY.md(supported versions, private reporting, scope) and a[tool.ruff]config.--applyexecutes exactly the confirmed plan instead of re-planning after the prompt.- Membership reads now paginate past Crowd's 1,000-result page.
- CLI error messages mask the app password and URL credentials.
groups diffrejects malformed snapshots with a clear error instead of a traceback.fixandjmllog every change already applied plus the failure when they stop partway, and
exit with status 1 (including on Ctrl-C, without a traceback).- If the failure entry cannot be written, the original error is still printed to stderr.
- Secrets (app password, URL credentials) are masked in logged error text.