Skip to content

3.2.1.3

Latest

Choose a tag to compare

@nilsteampassnet nilsteampassnet released this 29 Jul 20:08
e440662

What's Changed

This is a maintenance release on the 3.2.1 line. It fixes an Active Directory group mapping regression introduced in 3.2.1.2, tightens the Security Posture authorization boundary, stops malformed legacy passwords from aborting security scans, and adds administrator management of authentication lockouts, a redesigned operational statistics dashboard, a KeePass 2.x XML export and a native PowerShell API client.

This release is code only: no ALTER TABLE, no schema change and no new data migration. UPGRADE_MIN_DATE is deliberately left at its 3.2.1.2 value, so installations already running 3.2.1.2 are not sent back through the upgrade wizard.

Upgrading is recommended for all installations, and required for anyone mapping Active Directory groups to roles.

🔒 Security fixes

  • Security Posture: a sharekey is not an access grant - every posture query (dashboard cards, flagged-item list, per-folder badges, personal score, health nudges and the persisted item_health rows) was scoped by "this user holds a sharekey for this item", plus an exclusion of other users' personal folders. A sharekey is cryptographic material, not an authorization: an account still holding a key on an item it can no longer read - a revoked role, a folder moved out of scope, a denial in the forbidden-folders list, a per-item user or role restriction - kept seeing that item's label and path in the posture lists. Every posture query now applies the user's real folder grants, denials, own personal tree and per-item restrictions. The predicate is resolved once per request and memoised instead of being re-evaluated per row, which also removes six correlated EXISTS (two of them nested_tree self-joins) from three hot paths: the score computed on every page load, the folder flags on every folder open, and the item_health prune on every password save. The decision logic moved to a DB-free module covered by 46 behavioural test cases (denial beats every grant, a shared grant can never reach a personal folder, another user's personal tree stays out even for their manager, restricted_to must not match on an id substring).
  • Role assignment scoped to the caller's own roles (PR #5312, @joeshubaccount) - 3.2.1.1 closed the unscoped grant in add_one_role_to_user, but the user edit form, the rights propagation and the two account-creation paths still listed and accepted every role, so a manager could still grant a role outside their own scope through those screens. A non-administrator now only sees and grants the roles they hold or created. Roles the edited user holds outside that scope are returned locked and preserved server-side, so saving the form can no longer silently revoke them. The set logic is centralised in callerGrantableRoleIds() / reconcileManualUserRoles() and unit tested.

✨ New features

  • Administrator management of authentication lockouts (PR #5307, @guerricv) - a new Authentication lockouts tab in Utilities → Logs lists the active anti-bruteforce lockouts with their scope (account or IP address), locked value, failure count, first and last failure and remaining time, with a live countdown. An administrator can inspect the related failed authentications and release a lockout without waiting for its expiry or touching the database; the action is recorded in the audit trail. Options → Security links straight to the screen.
  • KeePass 2.x XML export (PR #5300, @danfossi) - a new export format producing a KeePassFile document that KeePass 2.x and its derivatives import natively, including tags and email. Like every other export path it enforces the caller's folder access list and the per-item restrictions, decrypts through the caller's own sharekey with v1→v3 migration support, and writes one at_export audit entry per exported item.
  • Native PowerShell API client (PR #5305, @danfossi) - app/scripts/teampass-cli.ps1, the PowerShell counterpart of the Bash teampass-cli.sh shipped in 3.2.1.2, for Windows environments. Requirements, configuration paths, command examples and the execution-policy notes are documented in the API documentation.
  • "Not assessed" password health state - an item whose password metadata was never computed (a legacy item, or one the background calculation has not reached yet) used to be counted as not weak, which read as healthy. It is now reported explicitly as Not assessed, with its own indicator card on the Security Posture dashboard and a matching state on the item card, the item-list badge and the compliance reports.
  • Configurable minimum password length - the weak-password rule now combines the complexity score and the password length. The threshold is the new admin setting security_dashboard_min_password_length (Options → Security → Security posture), defaulting to 12 characters.

🛠️ Improvements

  • Operational statistics dashboard redesigned (PR #5310, @guerricv) - the admin statistics page is split into Overview, Security, Activity and Users tabs, with an overall status summarising the prioritised findings, HIBP coverage indicators and item-creation statistics per source. Password compliance is computed with the canonical Security Posture helpers instead of hard-coded thresholds, so the page finally agrees with the item card, the posture dashboard and the compliance reports. Service accounts are excluded from the user and role metrics, and the period, ranking and complexity logic moved to a DB-free module covered by behavioural tests.
  • Security scans no longer abort on a malformed legacy password (PR #5311, @guerricv) - zxcvbn expects valid UTF-8 and throws on malformed legacy bytes. A single such password aborted the whole scan, the background metadata calculation or the API complexity check. Every server-side evaluation now goes through one safe adapter, evaluatePasswordStrengthSafely(), which validates the encoding, catches any evaluator failure and rejects a malformed result. The password itself is never converted or normalised - doing so would change the credential - and the item is reported as unassessable while the run continues. The dashboard scan reports the number of skipped items and surfaces the server-side reason instead of a generic failure.
  • Unassessable passwords no longer saturate the background queue - an item whose password could not be evaluated kept its complexity level unset, so the background calculation selected it again on every run: the same rows were decrypted and re-evaluated forever and, past 100 such items, the batch window was fully consumed and no other item was ever processed. Those items are now marked with a dedicated sentinel that still reads as Not assessed everywhere but takes them out of the batch selection.
  • Item card password health measured from the live value - the item card is the only health surface that already holds the plaintext, yet it classified from the stored metadata and reported Not assessed on items it could assess for free. It now measures the actual password, repairs the stored length and complexity when they diverge, and classifies from the repaired values, so the card, the item list and the posture dashboard converge. The repair is self-limiting: once written, the item is no longer legacy.
  • Folder management capabilities exposed by the API (PR #5309, @guerricv) - GET /api/v1/folder/writableFolders now returns is_personal, is_personal_root, can_create_subfolder, can_rename_folder, can_move_folder and can_delete_folder. These describe the folder itself, not its contents, and must not be conflated with the existing item rights (can_create / can_edit / can_delete): a folder in ND mode is a folder whose items cannot be deleted while the folder itself can be. The flags are UI hints only, computed from the same evaluator the mutation routes use, and every folder mutation re-runs the full check server-side. The endpoint documentation, the OpenAPI contract and the teampass_version fields of both authentication routes are documented accordingly, with sentinel tests keeping the specification and the controllers in sync.
  • Audit trail completed on per-user unlock - releasing a lockout from the user management screen now writes the same admin_action event as the new lockouts screen, so the trail is complete whichever screen the administrator used.
  • Password-health shields readable on highlighted rows - the shield badges were washed out on favourite rows in the item list.

🐛 Bug fixes

  • Active Directory primary group not resolved at login, breaking group ↔ role mapping (issue #5313) - 3.2.1.2 replaced LdapRecord's User::groups() relation by a raw filter on the group member attribute. Active Directory never lists primary group membership there, nor in the user's memberOf, and LDAP_MATCHING_RULE_IN_CHAIN does not expand it either: it is carried by the user's primaryGroupID alone. Mapping Domain Users (or any group used as a primary group) to a role therefore stopped working in 3.2.1.2, and the login then stripped every AD role from the user. The primary group is now resolved through LdapRecord's HasOnePrimaryGroup relation, and an unresolvable primary group is treated as a partial answer instead of a failed lookup, so the memberships read from member survive it. The same resolution is applied to the allowed-login-group check, in both membership modes.
  • Offline HTML export unable to decrypt AES v2 passwords - the offline HTML export path selected the password and its length but not the pw_iv metadata, which carries the IV and the salt of the hardened AES-256-GCM format introduced in 3.2.1.1. Exports made from an installation with the v2 format enabled therefore contained passwords that could not be decrypted from the exported file. The metadata is restored on that path.
  • CSV imports counted as web creations in the statistics - only the KeePass import path tagged its items with the at_import reason, so items created through the CSV importers were reported as created from the web interface.
  • Statistics charts built with the wrong Chart.js API - the page used Chart.js v3 option names while the application bundles v2.9.4.
  • "Unknown (-1)" complexity bucket in the statistics - PHP normalises numeric array keys to integers, so the string comparison identifying the unassessed bucket never matched and the bucket was rendered with its raw score. The minimum complexity is now shown as a label rather than a raw score.
  • IP lockouts that could be listed but never released - getClientIpServer() falls back to the literal UNKNOWN, and that value does get locked. The row was listed but every unlock attempt was rejected by the IP-format check. A target failing that check is now accepted when it exactly matches a stored lockout.
  • Forgeable API marker on lockout rows - the authentication channel displayed on the lockouts screen was derived from the tp_src=api marker, which on the web path sits in a field holding the submitted login, so anyone could make a row look like an API failure by typing that string in the login form. The channel is now derived from the log label, and the marker only discriminates the one event both channels write.

⬆️ Upgrade notes

  • No schema change, no data migration. UPGRADE_MIN_DATE is unchanged, so an installation already running 3.2.1.2 is not sent back through the upgrade wizard. Installations coming from 3.2.1.1 or earlier still run the idempotent upgrade_run_3.2.1.php as before.
  • Security Posture scope for administrators. The posture authorization fix resolves folders from the user's actual grants: direct folder rights, role-granted folders, minus denials, plus their own personal tree. Being an administrator is not a grant here - an administrator holding no explicit folder right now sees only their own personal tree in the Security Posture dashboard, the personal score and the nudges. This is deliberate: administrators get browsing rights but not item details, so listing those items would disclose labels and paths they cannot open. Grant the folders explicitly to an administrator who needs a wider posture view.
  • Weak-password counts will change. The weak rule now combines the complexity score with a minimum length (12 characters by default, configurable in Options → Security → Security posture), and items whose password was never assessed are moved out of the implicit "healthy" bucket into the new Not assessed state. Expect the dashboard counters to shift on the first scan after the upgrade.
  • The new setting needs no upgrade script. security_dashboard_min_password_length is seeded at 12 on fresh installs only; on an existing installation the row is simply absent, the resolver falls back to the built-in default and the row is written the first time an administrator changes the value.
  • Active Directory users: re-check your group ↔ role mappings. If a mapping stopped working after upgrading to 3.2.1.2, it should work again without any change; confirm that each group, primary groups included, resolves to the intended role.
  • Back up your database before upgrading, as always.

Full Changelog: 3.2.1.2...3.2.1.3

Important

  • Requires at least PHP 8.2

Languages

Please join Teampass v3 translation project on Poeditor and translate it for your language.

Installation

Follow instructions from Documentation.

Download TeamPass