Account recovery requires SMTP. When mail is off, treat the organization
reset-password auto-enroll policy as inactive so invite/accept flows are
not forced to supply a reset-password key.
Fixes#7459
`src/api/core/ciphers.rs:170` comment said "similar to the the
userDecryptionOptions" -> "similar to the userDecryptionOptions".
Comment-only.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* Include user email in successful login logs
* Modified disable account log to display Email instead of Display Name
---------
Co-authored-by: Alejandro Olmos <aolmos@trevenque.es>
* Update GHA and pre-commit
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update admin diagnostics
Added a check if the templates are overridden and return which specific folder, `admin`, `email` or `scss`.
This way we could more quickly point users to possible outdated templates which they are using.
Also updated the Support String to use some emojis so we should be able to quicker see if there is something wrong.
Just checking `true` or `false` could be difficult sometimes, and sometimes what we had as `false` wasn't bad either.
Also adjusted the eslint comments so it will work with the latest version of eslint.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix updating collections for a cipher
The newer clients expect a `cipherDetails` response on the `collections-admin` endpoints.
Without it, the client will cause an error and stops handling the update correctly.
This will fix this by returning the cipher json.
Fixes#7545Fixes#7546
Signed-off-by: BlackDex <black.dex@gmail.com>
* Cache CSS file in a different way
Currently we set a cache ttl of 24 hours, and users need to do a force refresh if there is anything changed to the CSS file.
In the past we have had several issue reported which were related to a still cached CSS file.
This commit will change the caching and also cache the generated CSS file in memory.
Instead of letting the browser cache it for 24 hours we generate an ETag, this is just a hash of the contents.
This ETag is returned by the browser during a request, and we can match this, and if so, just return a `304` `Not Modified`.
If the ETag is not known, we return the new content.
This should make simple refreshes by clients get updated settings or a new version of Vaultwarden which has other CSS entries get updated instantly.
If a user does a hard refresh, we will not receive the ETag and the content will be served.
The same goes if someone has the `reload_templates` feature enabled, since then we should not cache anyway.
If someone adjust settings via the `/admin` interface, the cache will be invalidated and a new CSS will be generated.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix showing events for a specific user
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update crates and adjust code.
- Updated opendal and adjusted code where needed.
- Updated yubico_ng and adjusted code where needed.
This version now supports using an own HttpClient and it pulls in no reqwest dependency anymore.
Now it will use our own client which uses custom hickory DNS and other features.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update web-vault to v2026.7.0
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix hadolint warnings
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
Dropping an emergency access hits two people, and only one of them is the
member the organization acts upon. The other one is the grantor of a grant the
member merely held, who can be a complete outsider to that organization and
would otherwise silently lose a part of its account setup.
Every grantor now gets a single mail listing all of the contacts it lost, which
is how Bitwarden notifies as well. The mail deliberately does not name a reason:
a recipient can be outside of the organization which triggered this and has no
business learning about the memberships of others.
The rows are gone by the time the mail goes out, so a failing send is logged
instead of failing the request, which would report a rollback that did not
happen.
Confirming a member into an organization which confirms automatically now drops
the emergency access of that member. Enabling the policy only cleaned up the
members present at that time, so anybody who joined afterwards kept a grantee
which could take over the account of a member nobody ever vetted and reach the
organization vault through it. Bitwarden drops these on every confirmation,
manual ones included, so this does the same.
Enabling the policy no longer touches invited members. An invitation is created
by an admin alone, without any consent of the invited user, so it must not be
able to delete the emergency access of an account that never joined the
organization.
The validation and the cleanup now only run on the step from disabled to
enabled. The web vault saves a policy on every edit and re-running the cleanup
kept wiping emergency access that members created in the meantime. The cleanup
also moved behind the save of the policy so that a failed save can no longer
destroy data for nothing.
While here: only notify members which may actually confirm, `AdminHeaders`
rejects the managers that `find_confirmed_and_manage_all_by_org` also returns,
and stop unwrapping the client supplied id in the manual bulk confirm.
The referenced Bitwarden sources were pinned to a commit which predates the
feature and returned 404, they now point at one which contains them.
The bundled web vault (2026.6.4) requires seven query parameters in the
accept-organization URL and rejects the invite client-side when any of them is
null, showing only "Unable to accept invitation" without sending a request to
the server.
send_invite() never appended initOrganization, and appended
orgUserHasExistingUser only for users who already had an account, so every
organization invitation e-mail produced a link that could not be accepted.
Web vault 2026.4.1 (shipped with 1.36.0) read these parameters null-safely,
which is why this only appeared in 1.37.0.
Fixes#7481
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Two issues found while reviewing the previous commits:
The bulk endpoint unwrapped the client supplied member id, so a single entry
without an id took the whole request down with a 500 and silently dropped the
confirmations of every other entry in the same call. Skip such an entry instead.
The manual `bulk_confirm_invite` has the same unwrap, that one is left alone
here.
`send_invite` announced the pending member right after saving the membership,
which is before the collections and groups of the invite are attached. An admin
client acts on that notification immediately, so it could confirm the member
while its access was still incomplete, and a failing collection or group
assignment would leave a confirmed member behind. The notification now happens
once the invite is fully applied.
Web vaults up to 2026.4.x do not show the automatic user confirmation policy
based on the organization flag alone, they additionally require the server side
feature flag `pm-19934-auto-confirm-organization-users`:
display$(org, config) =>
config.getFeatureFlag$(FeatureFlag.AutoConfirm)
.pipe(map(f => f && org.useAutomaticUserConfirmation))
Without it the policy is simply missing from Settings -> Policies. Newer clients
dropped the flag again and look at `useAutomaticUserConfirmation` alone, so
sending it stays harmless there. It follows `ORG_AUTO_CONFIRM_ENABLED`, which
keeps it off unless the server really supports the feature.
Implements the server side of Bitwarden's "Automatic Confirmation", which
automates the confirm step of the invite -> accept -> confirm flow.
The server can never confirm a member itself: confirming means encrypting the
organization key with the public key of the new member, and the server does not
have that key. Bitwarden solves this by letting the unlocked browser extension
of an admin do the work in the background, so what is added here is everything
that extension needs:
- `ORG_AUTO_CONFIRM_ENABLED` (off by default) and the matching
`useAutomaticUserConfirmation` flag in both organization JSONs. This mirrors
Bitwarden, where the feature is enabled per organization on request.
- Policy type 18. Enabling it requires the Single Org policy, and every member
has to be in this organization only. Contrary to the Single Org policy the
non compliant members are not revoked, because this policy applies to owners
and admins as well and revoking those could lock the organization out.
Enabling it also drops the emergency access grants of all members, and new
ones are refused while it is on.
- `GET /organizations/<id>/users/pending-auto-confirm`
`POST /organizations/<id>/users/<member>/auto-confirm`
`POST /organizations/<id>/users/bulk-auto-confirm`
Only members which accepted their invitation and hold the plain User role are
ever confirmed this way, an elevated role always needs a human, and an owner
can not lift that restriction like it can for the manual confirmation.
- Notification type 26 (AutoConfirmMember) to everybody who can confirm, sent
wherever a membership reaches the accepted state. WebSocket only, the
extension is the only consumer.
- Updated API response to more closely match v2026.6.0+ server versions.
- Updated all the crates
- Updated Rust to v1.97.1
- Updated the web-vault to v2026.6.4
- Updated GitHub Actions
Signed-off-by: BlackDex <black.dex@gmail.com>
* Misc updates and fixes
- Updated Rust to v1.96.1
- Updated all the crates
- Updated GitHub Actions
- Updated the web-vault to v2026.6.2
- Updated Alpine to v3.24
- Fixed several clippy lints
- The `send` UUID wrappers didn't need the special namespace anymore since an updated crate, so removed this extra mod.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update MSRV to v1.94.1
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* ci: remove dead BASE_TAGS reference in release bake step
steps.determine-version doesn't exist in docker-build; the expression
resolves to empty string. The HCL default (testing) would have
applied, but it's moot - the bake uses push-by-digest=true so tags are
only set in merge-manifests. Dead code.
* ci: replace unsecured curl hadolint download with an official action
hadolint/hadolint-action uses a Docker-based runner with hadolint
pre-bundled in ghcr.io/hadolint/hadolint:v2.14.0-debian,so no binary
downloaded at runtime. Pinning the action to a commit SHA covers the
Dockerfile that specifies the image version, closing the supply-chain
gap from the previous unverified curl | sudo install.
Split {debian,alpine}: the action takes a single dockerfile argument,
so debian and alpine are linted separately.
* ci: pin ubuntu-latest to ubuntu-24.04 in merge-manifests and zizmor
ubuntu-latest is a moving target that can silently change the runner OS
on the next GitHub-side update. All other jobs in this repo already pin
to ubuntu-24.04; this makes merge-manifests and zizmor consistent.
* ci: return BASE_TAGS - it's needed for bake step
The /api/sync response serialized a Send hide_email field directly from
Option<bool>, so a NULL value in the sends table (the column is
Nullable<Bool> with no default) produced "hideEmail": null.
The Bitwarden Android client deserializes SyncResponseJson.Send.hideEmail
as a non-null Kotlin Boolean and aborts the entire sync with a
JsonDecodingException when it encounters null. Web, desktop and CLI
clients coerce null to false, so only accounts with at least one Send
are affected and only on Android.
Default None to false at the serialization boundary, matching the
official Bitwarden server where hideEmail is non-nullable. This needs no
database migration and fixes both legacy NULL rows and any future NULLs.
The hide_email field stays Option<bool> internally.
- Updated Rust to v1.96.0
- Updated all the crates, and adjusted code where needed
- Fixed some nightly reported clippy lints
- Updated all the GitHub actions
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update to Rust 2024 Edition
Updated to the Rust 2024 Edition and added and fixed several lint checks.
This is a large change which, because of the extra lints, added some possible fixes for issues.
Signed-off-by: BlackDex <black.dex@gmail.com>
* Reorder and merge imports
Signed-off-by: BlackDex <black.dex@gmail.com>
* Remove "db_run!" macro calls where possible
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update crates and gha
Updated all the crates
Updated GitHub Actions
Signed-off-by: BlackDex <black.dex@gmail.com>
* Fix restoring revoked user
A new endpoint is used to restore a revoked user.
This commit fixes that.
Fixes#7224
Signed-off-by: BlackDex <black.dex@gmail.com>
* Update datatables
Signed-off-by: BlackDex <black.dex@gmail.com>
---------
Signed-off-by: BlackDex <black.dex@gmail.com>
* Panic on unrecognised DATABASE_URL instead of silent SQLite fallback
Previously, any DATABASE_URL that did not match the mysql: or postgresql:
prefix was silently treated as a SQLite file path. This caused data loss
in containerised environments when the URL was misconfigured (typos,
quoting issues), as vaultwarden would create an ephemeral SQLite database
that was wiped on restart.
Now, an explicit sqlite:// prefix is supported and used as the default.
Bare paths without a recognised scheme are still accepted for backwards
compatibility, but only if the database file already exists. If not, the
process panics with a clear error message.
Relates to #2835, #1910, #860.
* Use err!() instead of panic!() for unrecognised DATABASE_URL
Follow the established codebase convention where configuration
validation errors use err!() to propagate gracefully, rather than
panic!(). The error propagates through from_config() and is caught
by create_db_pool() which logs and calls exit(1).
* Use 'scheme' instead of 'prefix' in DATABASE_URL messages
Per review feedback, 'scheme' is the more accurate term for the
sqlite:// portion of the URL.