Compare commits

..

43 Commits

Author SHA1 Message Date
Daniel García
1e5306b820 Remove warning when compiling only with mysql and add compatibility mode with the old docker script names 2021-04-29 16:01:04 +02:00
Daniel García
6890c25ea1 Merge pull request #1636 from rkowalewski/fix-libressl-332
Update openssl crate to support LibreSSL 3.3.2
2021-04-29 16:00:14 +02:00
rkowalewski
48482fece0 Merge branch 'main' into fix-libressl-332 2021-04-29 08:34:10 +02:00
Roger Kowalewski
1dc1d4df72 update openssl crate to support LibreSSL 3.3.2 2021-04-29 10:04:08 +02:00
Daniel García
2b4dd6f137 Fix branch name 2021-04-28 21:46:20 +02:00
Daniel García
3da44a8d30 Fix formatting 2021-04-27 23:39:36 +02:00
Daniel García
34ea10475d Project renaming 2021-04-27 23:18:32 +02:00
Daniel García
ced7f1771a Update dependencies 2021-04-15 18:38:00 +02:00
Daniel García
af2235bf88 Merge branch 'RealOrangeOne-fmt' 2021-04-15 18:30:50 +02:00
Daniel García
305de2e2cd Format the changes from merge to master 2021-04-15 18:30:23 +02:00
Daniel García
8756c5c255 Merge branch 'fmt' of https://github.com/RealOrangeOne/bitwarden_rs into RealOrangeOne-fmt 2021-04-15 18:29:03 +02:00
Daniel García
27609ac4cc Update README.md 2021-04-15 18:27:05 +02:00
Daniel García
95d906bdbb Merge branch 'master' into fmt 2021-04-15 18:24:04 +02:00
Daniel García
4bb0d7bc05 Merge pull request #1587 from RealOrangeOne/request-proxy
Allow outbound requests to go via a proxy
2021-04-15 17:40:39 +02:00
Daniel García
d9599155ae Merge pull request #1602 from jjlin/backup-warning
Warn that the SQLite backup feature doesn't produce a complete backup
2021-04-15 17:38:31 +02:00
Jeremy Lin
244bad3a24 Warn that the SQLite backup feature doesn't produce a complete backup
Also add a link to the wiki page on backups.
2021-04-09 22:30:39 -07:00
Jake Howard
f7056bcaa5 Enable socks feature for reqwest
This allowed HTTP_PROXY be set with a socks5 proxy
2021-04-07 19:25:02 +01:00
Jake Howard
994669fb69 Merge remote-tracking branch 'origin/master' into fmt 2021-04-06 21:55:28 +01:00
Jake Howard
3ab90259f2 Modify rustfmt file 2021-04-06 21:54:42 +01:00
Jake Howard
155109dea1 Extract client creation to a single place 2021-04-06 21:04:37 +01:00
Daniel García
b268c3dd1c Update web vault and add unnoficialserver response 2021-04-06 20:38:22 +02:00
Daniel García
4e64dbdde4 Merge pull request #1579 from jjlin/job-scheduler
Add support for auto-deleting trashed items
2021-04-06 19:48:49 +02:00
Daniel García
a2955daffe Merge pull request #1576 from jjlin/global-domains
Sync global_domains.json
2021-04-06 19:36:11 +02:00
Daniel García
d3921b973b Merge pull request #1583 from BlackDex/icon-updates
Updated icon fetching.
2021-04-06 19:35:51 +02:00
Daniel García
cf6ad3cb15 Merge pull request #1584 from BlackDex/admin-interface
Some admin interface updates.
2021-04-06 19:33:15 +02:00
Jeremy Lin
90e0b7fec6 Offset scheduled jobs by 5 minutes
This is intended to avoid contention with database backups that many users
probably schedule to start at exactly the top of an hour.
2021-04-05 23:20:08 -07:00
Jeremy Lin
d77333576b Add support for auto-deleting trashed items
Upstream will soon auto-delete trashed items after 30 days, but some people
use the trash as an archive folder, so to avoid unexpected data loss, this
implementation requires the user to explicitly enable auto-deletion.
2021-04-05 23:07:25 -07:00
Jeremy Lin
73ff8d79f7 Add a generic job scheduler
Also rewrite deletion of old sends using the job scheduler.
2021-04-05 23:07:15 -07:00
BlackDex
95fc88ae5b Some admin interface updates.
- Fixed bug when web-vault is disabled.
- Updated sql-server version check to be simpler thx to @weiznich ( https://github.com/dani-garcia/bitwarden_rs/pull/1548#discussion_r604767196 )
- Use `VACUUM INTO` to create a SQLite backup instead of using the external sqlite3 application.
  - This also removes the dependancy of having the sqlite3 packages installed on the final image unnecessary, and thus removed it.
- Updated backup filename to also have the current time.
- Add specific bitwarden_rs web-vault version check (to match letter patched versions)
  Will work when https://github.com/dani-garcia/bw_web_builds/pull/33 is build (But still works without it also).
2021-04-05 15:09:16 +02:00
BlackDex
1d0eaac260 Updated icon fetching.
- Added image type checking, and prevent downloading non images.
  We didn't checked this before, which could in turn could allow someone
to download an arbitrary file.
- This also prevents SVG images from being used, while they work on the
  web-vault and desktop client, they didn't on the mobile versions.
- Because of this image type checking we can return a valid file type
  instead of only 'x-icon' (which is still used as a fallback).
- Prevent rel values with `icon-mask`, these are not valid favicons.
2021-04-03 22:51:44 +02:00
Jeremy Lin
3565bfc939 Sync global_domains.json to bitwarden/server@261916d (Stack Exchange) 2021-04-01 21:59:06 -07:00
Mathijs van Veluw
a82c04910f Merge pull request #1575 from RealOrangeOne/linguist-vendored
Just ignore scripts
2021-04-01 21:57:23 +02:00
Jake Howard
233f03ca2b Just ignore scripts
Nothing else in `src/static` is vendored external scripts, so just ignore these.

This also fixes the glob, which previously wasn't matching anything
2021-04-01 20:44:58 +01:00
Jake Howard
93c881a7a9 Reflow some lines manually 2021-03-31 21:45:05 +01:00
Jake Howard
0af3956abd Run cargo fmt on codebase 2021-03-31 21:18:35 +01:00
Jake Howard
15feff3e79 Add fmt to CI 2021-03-31 21:16:57 +01:00
Daniel García
5c5700caa7 Merge pull request #1565 from BlackDex/misc-updates
Misc changes.
2021-03-30 23:31:58 +02:00
Daniel García
3bddc176d6 Updated sponsors 2021-03-30 23:27:55 +02:00
BlackDex
9caf4bf383 Misc changes.
Some small changes in general:
- Moved the SQL Version check struct into the function.
- Updated hadolint to 2.0.0
- Fixed hadolint 2.0.0 warnings
- Updated github workflows
- Added .editorconfig for some general shared editor settings.
2021-03-30 21:45:10 +02:00
Daniel García
9b2234fa0e Merge pull request #1556 from mkilchhofer/docs/update_template
fix(env.template): IP_HEADER defaults to X-Real-IP
2021-03-29 23:35:50 +02:00
Daniel García
1f79fdec4e Merge pull request #1552 from BlackDex/misc-fixes
Icon and SMTP Debug fixes.
2021-03-29 23:35:31 +02:00
Marco Kilchhofer
a56f4c97e4 fix(env.template): IP_HEADER defaults to X-Real-IP
This was wrong in commit 88c56de97b.
2021-03-29 11:16:20 +02:00
BlackDex
3a3390963c Icon and SMTP Debug fixes.
- We need to add some feature to enable smtp debugging again. See: https://github.com/lettre/lettre/pull/584
- Upstream added the fallback icon again, probably because of caching ;). See: https://github.com/bitwarden/server/pull/1149
- Enabled gzip and brotli compression support with reqwest. Some sites seem to force this, or assume that because of the User-Agent string it is supported. This caused some failed icons.

Fixes #1540
2021-03-29 10:27:58 +02:00
92 changed files with 1255 additions and 954 deletions

View File

@@ -4,6 +4,8 @@ target
# Data folder
data
.env
.env.template
.gitattributes
# IDE files
.vscode

23
.editorconfig Normal file
View File

@@ -0,0 +1,23 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
end_of_line = lf
charset = utf-8
[*.{rs,py}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab

View File

@@ -1,4 +1,4 @@
## Bitwarden_RS Configuration File
## Vaultwarden Configuration File
## Uncomment any of the following lines to change the defaults
##
## Be aware that most of these settings will be overridden if they were changed
@@ -36,9 +36,9 @@
## Automatically reload the templates for every request, slow, use only for development
# RELOAD_TEMPLATES=false
## Client IP Header, used to identify the IP of the client, defaults to "X-Client-IP"
## Client IP Header, used to identify the IP of the client, defaults to "X-Real-IP"
## Set to the string "none" (without quotes), to disable any headers and just use the remote IP
# IP_HEADER=X-Client-IP
# IP_HEADER=X-Real-IP
## Cache time-to-live for successfully obtained icons, in seconds (0 is "forever")
# ICON_CACHE_TTL=2592000
@@ -56,6 +56,23 @@
# WEBSOCKET_ADDRESS=0.0.0.0
# WEBSOCKET_PORT=3012
## Job scheduler settings
##
## Job schedules use a cron-like syntax (as parsed by https://crates.io/crates/cron),
## and are always in terms of UTC time (regardless of your local time zone settings).
##
## How often (in ms) the job scheduler thread checks for jobs that need running.
## Set to 0 to globally disable scheduled jobs.
# JOB_POLL_INTERVAL_MS=30000
##
## Cron schedule of the job that checks for Sends past their deletion date.
## Defaults to hourly (5 minutes after the hour). Set blank to disable this job.
# SEND_PURGE_SCHEDULE="0 5 * * * *"
##
## Cron schedule of the job that checks for trashed items to delete permanently.
## Defaults to daily (5 minutes after midnight). Set blank to disable this job.
# TRASH_PURGE_SCHEDULE="0 5 0 * * *"
## Enable extended logging, which shows timestamps and targets in the logs
# EXTENDED_LOGGING=true
@@ -82,7 +99,7 @@
## Enable WAL for the DB
## Set to false to avoid enabling WAL during startup.
## Note that if the DB already has WAL enabled, you will also need to disable WAL in the DB,
## this setting only prevents bitwarden_rs from automatically enabling it on start.
## this setting only prevents vaultwarden from automatically enabling it on start.
## Please read project wiki page about this setting first before changing the value as it can
## cause performance degradation or might render the service unable to start.
# ENABLE_DB_WAL=true
@@ -170,7 +187,7 @@
## Invitations org admins to invite users, even when signups are disabled
# INVITATIONS_ALLOWED=true
## Name shown in the invitation emails that don't come from a specific organization
# INVITATION_ORG_NAME=Bitwarden_RS
# INVITATION_ORG_NAME=Vaultwarden
## Per-organization attachment limit (KB)
## Limit in kilobytes for an organization attachments, once the limit is exceeded it won't be possible to upload more
@@ -242,8 +259,8 @@
## To make sure the email links are pointing to the correct host, set the DOMAIN variable.
## Note: if SMTP_USERNAME is specified, SMTP_PASSWORD is mandatory
# SMTP_HOST=smtp.domain.tld
# SMTP_FROM=bitwarden-rs@domain.tld
# SMTP_FROM_NAME=Bitwarden_RS
# SMTP_FROM=vaultwarden@domain.tld
# SMTP_FROM_NAME=Vaultwarden
# SMTP_PORT=587 # Ports 587 (submission) and 25 (smtp) are standard without encryption and with encryption via STARTTLS (Explicit TLS). Port 465 is outdated and used with Implicit TLS.
# SMTP_SSL=true # (Explicit) - This variable by default configures Explicit STARTTLS, it will upgrade an insecure connection to a secure one. Unless SMTP_EXPLICIT_TLS is set to true. Either port 587 or 25 are default.
# SMTP_EXPLICIT_TLS=true # (Implicit) - N.B. This variable configures Implicit TLS. It's currently mislabelled (see bug #851) - SMTP_SSL Needs to be set to true for this option to work. Usually port 465 is used here.

2
.gitattributes vendored
View File

@@ -1,3 +1,3 @@
# Ignore vendored scripts in GitHub stats
src/static/* linguist-vendored
src/static/scripts/* linguist-vendored

View File

@@ -1,6 +1,6 @@
---
name: Bug report
about: Use this ONLY for bugs in bitwarden_rs itself. Use the Discourse forum (link below) to request features or get help with usage/configuration. If in doubt, use the forum.
about: Use this ONLY for bugs in vaultwarden itself. Use the Discourse forum (link below) to request features or get help with usage/configuration. If in doubt, use the forum.
title: ''
labels: ''
assignees: ''
@@ -8,11 +8,11 @@ assignees: ''
---
<!--
# ###
NOTE: Please update to the latest version of bitwarden_rs before reporting an issue!
NOTE: Please update to the latest version of vaultwarden before reporting an issue!
This saves you and us a lot of time and troubleshooting.
See:
* https://github.com/dani-garcia/bitwarden_rs/issues/1180
* https://github.com/dani-garcia/bitwarden_rs/wiki/Updating-the-bitwarden-image
* https://github.com/dani-garcia/vaultwarden/issues/1180
* https://github.com/dani-garcia/vaultwarden/wiki/Updating-the-vaultwarden-image
# ###
-->
@@ -37,9 +37,9 @@ such as passwords, IP addresses, and DNS names as appropriate.
-->
<!-- The version number, obtained from the logs (at startup) or the admin diagnostics page -->
<!-- This is NOT the version number shown on the web vault, which is versioned separately from bitwarden_rs -->
<!-- This is NOT the version number shown on the web vault, which is versioned separately from vaultwarden -->
<!-- Remember to check if your issue exists on the latest version first! -->
* bitwarden_rs version:
* vaultwarden version:
<!-- How the server was installed: Docker image, OS package, built from source, etc. -->
* Install method:
@@ -54,7 +54,7 @@ such as passwords, IP addresses, and DNS names as appropriate.
### Steps to reproduce
<!-- Tell us how to reproduce this issue. What parameters did you set (differently from the defaults)
and how did you start bitwarden_rs? -->
and how did you start vaultwarden? -->
### Expected behaviour
<!-- Tell us what you expected to happen -->

View File

@@ -3,6 +3,6 @@ contact_links:
- name: Discourse forum for bitwarden_rs
url: https://bitwardenrs.discourse.group/
about: Use this forum to request features or get help with usage/configuration.
- name: GitHub Discussions for bitwarden_rs
url: https://github.com/dani-garcia/bitwarden_rs/discussions
- name: GitHub Discussions for vaultwarden
url: https://github.com/dani-garcia/vaultwarden/discussions
about: An alternative to the Discourse forum, if this is easier for you.

View File

@@ -2,23 +2,21 @@ name: Build
on:
push:
# Ignore when there are only changes done too one of these paths
paths-ignore:
- "**.md"
- "**.txt"
- "azure-pipelines.yml"
- "docker/**"
- "hooks/**"
- "tools/**"
pull_request:
# Ignore when there are only changes done too one of these paths
paths-ignore:
- "**.md"
- "**.txt"
- ".dockerignore"
- ".env.template"
- ".gitattributes"
- ".gitignore"
- "azure-pipelines.yml"
- "docker/**"
- "hooks/**"
- "tools/**"
- ".github/FUNDING.yml"
- ".github/ISSUE_TEMPLATE/**"
jobs:
build:
@@ -91,7 +89,7 @@ jobs:
with:
profile: minimal
target: ${{ matrix.target-triple }}
components: clippy
components: clippy, rustfmt
# End Uses the rust-toolchain file to determine version
@@ -113,6 +111,15 @@ jobs:
# End Run cargo clippy
# Run cargo fmt
- name: '`cargo fmt`'
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
# End Run cargo fmt
# Build the binary
- name: '`cargo build --release --features ${{ matrix.features }} --target ${{ matrix.target-triple }}`'
uses: actions-rs/cargo@v1
@@ -126,8 +133,8 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: bitwarden_rs-${{ matrix.target-triple }}${{ matrix.ext }}
path: target/${{ matrix.target-triple }}/release/bitwarden_rs${{ matrix.ext }}
name: vaultwarden-${{ matrix.target-triple }}${{ matrix.ext }}
path: target/${{ matrix.target-triple }}/release/vaultwarden${{ matrix.ext }}
# End Upload artifact to Github Actions
@@ -138,7 +145,7 @@ jobs:
# uses: Shopify/upload-to-release@1
# if: startsWith(github.ref, 'refs/tags/')
# with:
# name: bitwarden_rs-${{ matrix.target-triple }}${{ matrix.ext }}
# path: target/${{ matrix.target-triple }}/release/bitwarden_rs${{ matrix.ext }}
# name: vaultwarden-${{ matrix.target-triple }}${{ matrix.ext }}
# path: target/${{ matrix.target-triple }}/release/vaultwarden${{ matrix.ext }}
# repo-token: ${{ secrets.GITHUB_TOKEN }}
# End Upload to github actions release

View File

@@ -1,6 +1,7 @@
name: Hadolint
on:
push:
pull_request:
# Ignore when there are only changes done too one of these paths
paths:
@@ -24,7 +25,7 @@ jobs:
sudo curl -L https://github.com/hadolint/hadolint/releases/download/v$HADOLINT_VERSION/hadolint-$(uname -s)-$(uname -m) -o /usr/local/bin/hadolint && \
sudo chmod +x /usr/local/bin/hadolint
env:
HADOLINT_VERSION: 1.19.0
HADOLINT_VERSION: 2.0.0
# End Download hadolint
# Test Dockerfiles

482
Cargo.lock generated
View File

@@ -26,6 +26,35 @@ dependencies = [
"memchr",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192ec435945d87bc2f70992b4d818154b5feede43c09fb7592146374eac90a6"
[[package]]
name = "alloc-stdlib"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "async-compression"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537"
dependencies = [
"brotli",
"flate2",
"futures-core",
"memchr",
"pin-project-lite",
"tokio",
]
[[package]]
name = "atty"
version = "0.2.14"
@@ -115,55 +144,6 @@ dependencies = [
"wyz",
]
[[package]]
name = "bitwarden_rs"
version = "1.0.0"
dependencies = [
"backtrace",
"chashmap",
"chrono",
"chrono-tz",
"data-encoding",
"data-url",
"diesel",
"diesel_migrations",
"dotenv",
"fern",
"handlebars",
"html5ever",
"idna 0.2.2",
"jsonwebtoken",
"lettre",
"libsqlite3-sys",
"log 0.4.14",
"markup5ever_rcdom",
"multipart",
"newline-converter",
"num-derive",
"num-traits",
"oath",
"once_cell",
"openssl",
"parity-ws",
"paste",
"percent-encoding 2.1.0",
"pico-args",
"rand 0.8.3",
"regex",
"reqwest",
"ring",
"rmpv",
"rocket",
"rocket_contrib",
"serde",
"serde_json",
"syslog",
"time 0.2.26",
"u2f",
"uuid",
"yubico",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
@@ -194,6 +174,27 @@ dependencies = [
"byte-tools 0.3.1",
]
[[package]]
name = "brotli"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f29919120f08613aadcd4383764e00526fc9f18b6c0895814faeed0dd78613e"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1052e1c3b8d4d80eb84a8b94f0a1498797b5fb96314c001156a1c761940ef4ec"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "buf_redux"
version = "0.8.4"
@@ -298,9 +299,9 @@ dependencies = [
[[package]]
name = "const_fn"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
[[package]]
name = "constant_time_eq"
@@ -341,6 +342,26 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "cron"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e009ed0b762cf7a967a34dfdc67d5967d3f828f12901d37081432c3dd1668f8f"
dependencies = [
"chrono",
"nom 4.1.1",
"once_cell",
]
[[package]]
name = "crypto-mac"
version = "0.3.0"
@@ -399,9 +420,9 @@ version = "0.3.0"
source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9afc3b6ff10a8aaf02a3e768a8f530089"
dependencies = [
"bitflags",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -427,9 +448,9 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -491,6 +512,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "encoding_rs"
version = "0.8.28"
@@ -525,6 +552,18 @@ dependencies = [
"syslog",
]
[[package]]
name = "flate2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -596,9 +635,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@@ -611,9 +650,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@@ -621,15 +660,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@@ -638,39 +677,39 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
[[package]]
name = "futures-macro"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "futures-sink"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-util"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@@ -770,9 +809,9 @@ dependencies = [
[[package]]
name = "handlebars"
version = "3.5.3"
version = "3.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb0867bbc5a3da37a753e78021d5fcf8a4db00e18dd2dd90fd36e24190e162d"
checksum = "580b6f551b29a3a02436318aed09ba1c58eea177dc49e39beac627ad356730a5"
dependencies = [
"log 0.4.14",
"pest",
@@ -839,16 +878,16 @@ dependencies = [
"log 0.4.14",
"mac",
"markup5ever",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "http"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
dependencies = [
"bytes 1.0.1",
"fnv",
@@ -868,9 +907,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.3.5"
version = "1.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
checksum = "bc35c995b9d93ec174cf9a27d425c7892722101e14993cd227fdb51d70cf9589"
[[package]]
name = "httpdate"
@@ -899,9 +938,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.14.4"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
dependencies = [
"bytes 1.0.1",
"futures-channel",
@@ -940,7 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes 1.0.1",
"hyper 0.14.4",
"hyper 0.14.5",
"native-tls",
"tokio",
"tokio-native-tls",
@@ -1025,11 +1064,21 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "job_scheduler"
version = "1.2.1"
source = "git+https://github.com/jjlin/job_scheduler?rev=ee023418dbba2bfe1e30a5fd7d937f9e33739806#ee023418dbba2bfe1e30a5fd7d937f9e33739806"
dependencies = [
"chrono",
"cron",
"uuid",
]
[[package]]
name = "js-sys"
version = "0.3.49"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
dependencies = [
"wasm-bindgen",
]
@@ -1088,7 +1137,7 @@ dependencies = [
"idna 0.2.2",
"mime 0.3.16",
"native-tls",
"nom",
"nom 6.1.2",
"once_cell",
"quoted_printable",
"rand 0.8.3",
@@ -1100,9 +1149,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.91"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libsqlite3-sys"
@@ -1117,9 +1166,9 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
dependencies = [
"scopeguard",
]
@@ -1223,9 +1272,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
dependencies = [
"migrations_internals",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -1403,6 +1452,15 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a"
dependencies = [
"memchr",
]
[[package]]
name = "nom"
version = "6.1.2"
@@ -1441,9 +1499,9 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -1514,9 +1572,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.33"
version = "0.10.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
@@ -1543,9 +1601,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.61"
version = "0.9.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd"
dependencies = [
"autocfg",
"cc",
@@ -1716,9 +1774,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -1791,9 +1849,9 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -1858,9 +1916,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid 0.2.1",
]
@@ -1892,14 +1950,14 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
]
[[package]]
name = "quoted_printable"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
checksum = "1238256b09923649ec89b08104c4dfe9f6cb2fea734a5db5384e44916d59e9c5"
[[package]]
name = "r2d2"
@@ -2048,9 +2106,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
dependencies = [
"bitflags",
]
@@ -2083,10 +2141,11 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.2"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4"
checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124"
dependencies = [
"async-compression",
"base64 0.13.0",
"bytes 1.0.1",
"encoding_rs",
@@ -2094,7 +2153,7 @@ dependencies = [
"futures-util",
"http",
"http-body",
"hyper 0.14.4",
"hyper 0.14.5",
"hyper-tls",
"ipnet",
"js-sys",
@@ -2109,6 +2168,8 @@ dependencies = [
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tokio-socks",
"tokio-util",
"url 2.2.1",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -2296,9 +2357,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
"ring",
"untrusted",
@@ -2306,9 +2367,9 @@ dependencies = [
[[package]]
name = "security-framework"
version = "2.1.2"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d"
checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84"
dependencies = [
"bitflags",
"core-foundation",
@@ -2319,9 +2380,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.1.1"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d"
checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339"
dependencies = [
"core-foundation-sys",
"libc",
@@ -2357,9 +2418,9 @@ version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -2482,11 +2543,10 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.3.19"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi 0.3.9",
]
@@ -2538,11 +2598,11 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"serde",
"serde_derive",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -2552,13 +2612,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"serde",
"serde_derive",
"serde_json",
"sha1",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
@@ -2588,7 +2648,7 @@ checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
]
@@ -2611,11 +2671,11 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"unicode-xid 0.2.1",
]
@@ -2663,6 +2723,26 @@ dependencies = [
"utf-8",
]
[[package]]
name = "thiserror"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
name = "threadpool"
version = "1.8.1"
@@ -2715,17 +2795,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"standback",
"syn 1.0.64",
"syn 1.0.69",
]
[[package]]
name = "tinyvec"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
dependencies = [
"tinyvec_macros",
]
@@ -2738,9 +2818,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
dependencies = [
"autocfg",
"bytes 1.0.1",
@@ -2762,10 +2842,22 @@ dependencies = [
]
[[package]]
name = "tokio-util"
version = "0.6.5"
name = "tokio-socks"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
dependencies = [
"either",
"futures-util",
"thiserror",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e"
dependencies = [
"bytes 1.0.1",
"futures-core",
@@ -2797,10 +2889,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
dependencies = [
"cfg-if 1.0.0",
"log 0.4.14",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
name = "tracing-core"
version = "0.1.17"
@@ -2886,9 +2991,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
dependencies = [
"matches",
]
@@ -2958,6 +3063,57 @@ dependencies = [
"getrandom 0.2.2",
]
[[package]]
name = "vaultwarden"
version = "1.0.0"
dependencies = [
"backtrace",
"chashmap",
"chrono",
"chrono-tz",
"data-encoding",
"data-url",
"diesel",
"diesel_migrations",
"dotenv",
"fern",
"handlebars",
"html5ever",
"idna 0.2.2",
"job_scheduler",
"jsonwebtoken",
"lettre",
"libsqlite3-sys",
"log 0.4.14",
"markup5ever_rcdom",
"multipart",
"newline-converter",
"num-derive",
"num-traits",
"oath",
"once_cell",
"openssl",
"parity-ws",
"paste",
"percent-encoding 2.1.0",
"pico-args",
"rand 0.8.3",
"regex",
"reqwest",
"ring",
"rmpv",
"rocket",
"rocket_contrib",
"serde",
"serde_json",
"syslog",
"time 0.2.26",
"tracing",
"u2f",
"uuid",
"yubico",
]
[[package]]
name = "vcpkg"
version = "0.2.11"
@@ -3011,9 +3167,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasm-bindgen"
version = "0.2.72"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
dependencies = [
"cfg-if 1.0.0",
"serde",
@@ -3023,24 +3179,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.72"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
dependencies = [
"bumpalo",
"lazy_static",
"log 0.4.14",
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.22"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468"
checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -3050,9 +3206,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.72"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote 1.0.9",
"wasm-bindgen-macro-support",
@@ -3060,28 +3216,28 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.72"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2 1.0.24",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.64",
"syn 1.0.69",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.72"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
[[package]]
name = "web-sys"
version = "0.3.49"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
dependencies = [
"js-sys",
"wasm-bindgen",

View File

@@ -1,10 +1,10 @@
[package]
name = "bitwarden_rs"
name = "vaultwarden"
version = "1.0.0"
authors = ["Daniel García <dani-garcia@users.noreply.github.com>"]
edition = "2018"
repository = "https://github.com/dani-garcia/bitwarden_rs"
repository = "https://github.com/dani-garcia/vaultwarden"
readme = "README.md"
license = "GPL-3.0-only"
publish = false
@@ -32,7 +32,7 @@ rocket = { version = "0.5.0-dev", features = ["tls"], default-features = false }
rocket_contrib = "0.5.0-dev"
# HTTP client
reqwest = { version = "0.11.2", features = ["blocking", "json"] }
reqwest = { version = "0.11.3", features = ["blocking", "json", "gzip", "brotli", "socks"] }
# multipart/form-data support
multipart = { version = "0.17.1", features = ["server"], default-features = false }
@@ -73,6 +73,9 @@ chrono = { version = "0.4.19", features = ["serde"] }
chrono-tz = "0.5.3"
time = "0.2.26"
# Job scheduler
job_scheduler = "1.2.1"
# TOTP library
oath = "0.10.2"
@@ -99,11 +102,12 @@ num-traits = "0.2.14"
num-derive = "0.3.3"
# Email libraries
tracing = { version = "0.1.25", features = ["log"] } # Needed to have lettre trace logging used when SMTP_DEBUG is enabled.
lettre = { version = "0.10.0-beta.3", features = ["smtp-transport", "builder", "serde", "native-tls", "hostname", "tracing"], default-features = false }
newline-converter = "0.2.0"
# Template library
handlebars = { version = "3.5.3", features = ["dir_source"] }
handlebars = { version = "3.5.4", features = ["dir_source"] }
# For favicon extraction from main website
html5ever = "0.25.1"
@@ -112,7 +116,7 @@ regex = { version = "1.4.5", features = ["std", "perf"], default-features = fals
data-url = "0.1.0"
# Used by U2F, JWT and Postgres
openssl = "0.10.33"
openssl = "0.10.34"
# URL encoding library
percent-encoding = "2.1.0"
@@ -135,3 +139,10 @@ rocket_contrib = { git = 'https://github.com/SergioBenitez/Rocket', rev = '263e3
# For favicon extraction from main website
data-url = { git = 'https://github.com/servo/rust-url', package="data-url", rev = '540ede02d0771824c0c80ff9f57fe8eff38b1291' }
# The maintainer of the `job_scheduler` crate doesn't seem to have responded
# to any issues or PRs for almost a year (as of April 2021). This hopefully
# temporary fork updates Cargo.toml to use more up-to-date dependencies.
# In particular, `cron` has since implemented parsing of some common syntax
# that wasn't previously supported (https://github.com/zslayton/cron/pull/64).
job_scheduler = { git = 'https://github.com/jjlin/job_scheduler', rev = 'ee023418dbba2bfe1e30a5fd7d937f9e33739806' }

View File

@@ -1,15 +1,14 @@
### This is a Bitwarden server API implementation written in Rust compatible with [upstream Bitwarden clients](https://bitwarden.com/#download)*, perfect for self-hosted deployment where running the official resource-heavy service might not be ideal.
### Alternative implementation of the Bitwarden server API written in Rust and compatible with [upstream Bitwarden clients](https://bitwarden.com/#download)*, perfect for self-hosted deployment where running the official resource-heavy service might not be ideal.
---
[![Travis Build Status](https://travis-ci.org/dani-garcia/bitwarden_rs.svg?branch=master)](https://travis-ci.org/dani-garcia/bitwarden_rs)
[![Docker Pulls](https://img.shields.io/docker/pulls/bitwardenrs/server.svg)](https://hub.docker.com/r/bitwardenrs/server)
[![Dependency Status](https://deps.rs/repo/github/dani-garcia/bitwarden_rs/status.svg)](https://deps.rs/repo/github/dani-garcia/bitwarden_rs)
[![GitHub Release](https://img.shields.io/github/release/dani-garcia/bitwarden_rs.svg)](https://github.com/dani-garcia/bitwarden_rs/releases/latest)
[![GPL-3.0 Licensed](https://img.shields.io/github/license/dani-garcia/bitwarden_rs.svg)](https://github.com/dani-garcia/bitwarden_rs/blob/master/LICENSE.txt)
[![Matrix Chat](https://img.shields.io/matrix/bitwarden_rs:matrix.org.svg?logo=matrix)](https://matrix.to/#/#bitwarden_rs:matrix.org)
[![Docker Pulls](https://img.shields.io/docker/pulls/bitwardenrs/server.svg)](https://hub.docker.com/r/vaultwarden/server)
[![Dependency Status](https://deps.rs/repo/github/dani-garcia/vaultwarden/status.svg)](https://deps.rs/repo/github/dani-garcia/vaultwarden)
[![GitHub Release](https://img.shields.io/github/release/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/releases/latest)
[![GPL-3.0 Licensed](https://img.shields.io/github/license/dani-garcia/vaultwarden.svg)](https://github.com/dani-garcia/vaultwarden/blob/master/LICENSE.txt)
[![Matrix Chat](https://img.shields.io/matrix/vaultwarden:matrix.org.svg?logo=matrix)](https://matrix.to/#/#vaultwarden:matrix.org)
Image is based on [Rust implementation of Bitwarden API](https://github.com/dani-garcia/bitwarden_rs).
Image is based on [Rust implementation of Bitwarden API](https://github.com/dani-garcia/vaultwarden).
**This project is not associated with the [Bitwarden](https://bitwarden.com/) project nor 8bit Solutions LLC.**
@@ -33,29 +32,57 @@ Basically full implementation of Bitwarden API is provided including:
Pull the docker image and mount a volume from the host for persistent storage:
```sh
docker pull bitwardenrs/server:latest
docker run -d --name bitwarden -v /bw-data/:/data/ -p 80:80 bitwardenrs/server:latest
docker pull vaultwarden/server:latest
docker run -d --name vaultwarden -v /vw-data/:/data/ -p 80:80 vaultwarden/server:latest
```
This will preserve any persistent data under /bw-data/, you can adapt the path to whatever suits you.
**IMPORTANT**: Some web browsers, like Chrome, disallow the use of Web Crypto APIs in insecure contexts. In this case, you might get an error like `Cannot read property 'importKey'`. To solve this problem, you need to access the web vault from HTTPS.
This can be configured in [bitwarden_rs directly](https://github.com/dani-garcia/bitwarden_rs/wiki/Enabling-HTTPS) or using a third-party reverse proxy ([some examples](https://github.com/dani-garcia/bitwarden_rs/wiki/Proxy-examples)).
This can be configured in [vaultwarden directly](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-HTTPS) or using a third-party reverse proxy ([some examples](https://github.com/dani-garcia/vaultwarden/wiki/Proxy-examples)).
If you have an available domain name, you can get HTTPS certificates with [Let's Encrypt](https://letsencrypt.org/), or you can generate self-signed certificates with utilities like [mkcert](https://github.com/FiloSottile/mkcert). Some proxies automatically do this step, like Caddy (see examples linked above).
## Usage
See the [bitwarden_rs wiki](https://github.com/dani-garcia/bitwarden_rs/wiki) for more information on how to configure and run the bitwarden_rs server.
See the [vaultwarden wiki](https://github.com/dani-garcia/vaultwarden/wiki) for more information on how to configure and run the vaultwarden server.
## Get in touch
To ask a question, offer suggestions or new features or to get help configuring or installing the software, please [use the forum](https://bitwardenrs.discourse.group/).
To ask a question, offer suggestions or new features or to get help configuring or installing the software, please [use the forum](https://vaultwarden.discourse.group/).
If you spot any bugs or crashes with bitwarden_rs itself, please [create an issue](https://github.com/dani-garcia/bitwarden_rs/issues/). Make sure there aren't any similar issues open, though!
If you spot any bugs or crashes with vaultwarden itself, please [create an issue](https://github.com/dani-garcia/vaultwarden/issues/). Make sure there aren't any similar issues open, though!
If you prefer to chat, we're usually hanging around at [#bitwarden_rs:matrix.org](https://matrix.to/#/#bitwarden_rs:matrix.org) room on Matrix. Feel free to join us!
If you prefer to chat, we're usually hanging around at [#vaultwarden:matrix.org](https://matrix.to/#/#vaultwarden:matrix.org) room on Matrix. Feel free to join us!
### Sponsors
Thanks for your contribution to the project!
- [@ChonoN](https://github.com/ChonoN)
- [@themightychris](https://github.com/themightychris)
<table>
<tr>
<td align="center">
<a href="https://github.com/netdadaltd">
<img src="https://avatars.githubusercontent.com/u/77323954?s=75&v=4" width="75px;" alt="netdadaltd"/>
<br />
<sub><b>netDada Ltd.</b></sub>
</a>
</td>
</tr>
</table>
<br/>
<table>
<tr>
<td align="center">
<a href="https://github.com/ChonoN" style="width: 75px">
<sub><b>ChonoN</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/themightychris">
<sub><b>themightychris</b></sub>
</a>
</td>
</tr>
</table>

View File

@@ -1,5 +1,5 @@
use std::process::Command;
use std::env;
use std::process::Command;
fn main() {
// This allow using #[cfg(sqlite)] instead of #[cfg(feature = "sqlite")], which helps when trying to add them through macros
@@ -11,7 +11,9 @@ fn main() {
println!("cargo:rustc-cfg=postgresql");
#[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgresql")))]
compile_error!("You need to enable one DB backend. To build with previous defaults do: cargo build --features sqlite");
compile_error!(
"You need to enable one DB backend. To build with previous defaults do: cargo build --features sqlite"
);
if let Ok(version) = env::var("BWRS_VERSION") {
println!("cargo:rustc-env=BWRS_VERSION={}", version);
@@ -56,7 +58,7 @@ fn read_git_info() -> Result<(), std::io::Error> {
// Combined version
let version = if let Some(exact) = exact_tag {
exact
} else if &branch != "master" {
} else if &branch != "main" && &branch != "master" {
format!("{}-{} ({})", last_tag, rev_short, branch)
} else {
format!("{}-{}", last_tag, rev_short)

View File

@@ -1,7 +1,7 @@
# The cross-built images have the build arch (`amd64`) embedded in the image
# manifest, rather than the target arch. For example:
#
# $ docker inspect bitwardenrs/server:latest-armv7 | jq -r '.[]|.Architecture'
# $ docker inspect vaultwarden/server:latest-armv7 | jq -r '.[]|.Architecture'
# amd64
#
# Recent versions of Docker have started printing a warning when the image's

View File

@@ -1,10 +1,10 @@
# This file was generated using a Jinja2 template.
# Please make your changes in `Dockerfile.j2` and then `make` the individual Dockerfiles.
{% set build_stage_base_image = "rust:1.50" %}
{% set build_stage_base_image = "rust:1.51" %}
{% if "alpine" in target_file %}
{% if "amd64" in target_file %}
{% set build_stage_base_image = "clux/muslrust:nightly-2021-02-22" %}
{% set build_stage_base_image = "clux/muslrust:nightly-2021-04-14" %}
{% set runtime_stage_base_image = "alpine:3.13" %}
{% set package_arch_target = "x86_64-unknown-linux-musl" %}
{% elif "armv7" in target_file %}
@@ -44,26 +44,26 @@
# https://docs.docker.com/develop/develop-images/multistage-build/
# https://whitfin.io/speeding-up-rust-docker-builds/
####################### VAULT BUILD IMAGE #######################
{% set vault_version = "2.19.0" %}
{% set vault_image_digest = "sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4" %}
{% set vault_version = "2.19.0d" %}
{% set vault_image_digest = "sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233" %}
# The web-vault digest specifies a particular web-vault build on Docker Hub.
# Using the digest instead of the tag name provides better security,
# as the digest of an image is immutable, whereas a tag name can later
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v{{ vault_version }}
# $ docker image inspect --format "{{ '{{' }}.RepoDigests}}" bitwardenrs/web-vault:v{{ vault_version }}
# [bitwardenrs/web-vault@{{ vault_image_digest }}]
# $ docker pull vaultwarden/web-vault:v{{ vault_version }}
# $ docker image inspect --format "{{ '{{' }}.RepoDigests}}" vaultwarden/web-vault:v{{ vault_version }}
# [vaultwarden/web-vault@{{ vault_image_digest }}]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{ '{{' }}.RepoTags}}" bitwardenrs/web-vault@{{ vault_image_digest }}
# [bitwardenrs/web-vault:v{{ vault_version }}]
# $ docker image inspect --format "{{ '{{' }}.RepoTags}}" vaultwarden/web-vault@{{ vault_image_digest }}
# [vaultwarden/web-vault:v{{ vault_version }}]
#
FROM bitwardenrs/web-vault@{{ vault_image_digest }} as vault
FROM vaultwarden/web-vault@{{ vault_image_digest }} as vault
########################## BUILD IMAGE ##########################
FROM {{ build_stage_base_image }} as build
@@ -189,7 +189,7 @@ RUN touch src/main.rs
RUN cargo build --features ${DB} --release{{ package_arch_target_param }}
{% if "alpine" in target_file %}
{% if "armv7" in target_file %}
RUN musl-strip target/{{ package_arch_target }}/release/bitwarden_rs
RUN musl-strip target/{{ package_arch_target }}/release/vaultwarden
{% endif %}
{% endif %}
@@ -215,9 +215,6 @@ RUN apk add --no-cache \
openssl \
curl \
dumb-init \
{% if "sqlite" in features %}
sqlite \
{% endif %}
{% if "mysql" in features %}
mariadb-connector-c \
{% endif %}
@@ -232,7 +229,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
dumb-init \
sqlite3 \
libmariadb-dev-compat \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
@@ -250,12 +246,13 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
{% if package_arch_target is defined %}
COPY --from=build /app/target/{{ package_arch_target }}/release/bitwarden_rs .
COPY --from=build /app/target/{{ package_arch_target }}/release/vaultwarden .
{% else %}
COPY --from=build /app/target/release/bitwarden_rs .
COPY --from=build /app/target/release/vaultwarden .
{% endif %}
COPY docker/healthcheck.sh /healthcheck.sh
@@ -264,6 +261,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,21 +11,21 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM rust:1.50 as build
FROM rust:1.51 as build
# Debian-based builds support multidb
ARG DB=sqlite,mysql,postgresql
@@ -86,7 +86,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
dumb-init \
sqlite3 \
libmariadb-dev-compat \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
@@ -98,9 +97,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/release/bitwarden_rs .
COPY --from=build /app/target/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -108,6 +108,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,21 +11,21 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM clux/muslrust:nightly-2021-02-22 as build
FROM clux/muslrust:nightly-2021-04-14 as build
# Alpine-based AMD64 (musl) does not support mysql/mariadb during compile time.
ARG DB=sqlite,postgresql
@@ -82,7 +82,6 @@ RUN apk add --no-cache \
openssl \
curl \
dumb-init \
sqlite \
postgresql-libs \
ca-certificates
@@ -93,9 +92,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/x86_64-unknown-linux-musl/release/bitwarden_rs .
COPY --from=build /app/target/x86_64-unknown-linux-musl/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -103,6 +103,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,21 +11,21 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM rust:1.50 as build
FROM rust:1.51 as build
# Debian-based builds support multidb
ARG DB=sqlite,mysql,postgresql
@@ -129,7 +129,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
dumb-init \
sqlite3 \
libmariadb-dev-compat \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
@@ -144,9 +143,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/aarch64-unknown-linux-gnu/release/bitwarden_rs .
COPY --from=build /app/target/aarch64-unknown-linux-gnu/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -154,6 +154,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,21 +11,21 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM rust:1.50 as build
FROM rust:1.51 as build
# Debian-based builds support multidb
ARG DB=sqlite,mysql,postgresql
@@ -129,7 +129,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
dumb-init \
sqlite3 \
libmariadb-dev-compat \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
@@ -144,9 +143,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/arm-unknown-linux-gnueabi/release/bitwarden_rs .
COPY --from=build /app/target/arm-unknown-linux-gnueabi/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -154,6 +154,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,21 +11,21 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM rust:1.50 as build
FROM rust:1.51 as build
# Debian-based builds support multidb
ARG DB=sqlite,mysql,postgresql
@@ -129,7 +129,6 @@ RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
dumb-init \
sqlite3 \
libmariadb-dev-compat \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
@@ -144,9 +143,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/bitwarden_rs .
COPY --from=build /app/target/armv7-unknown-linux-gnueabihf/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -154,6 +154,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -11,18 +11,18 @@
# be changed to point to a malicious image.
#
# To verify the current digest for a given tag name:
# - From https://hub.docker.com/r/bitwardenrs/web-vault/tags,
# - From https://hub.docker.com/r/vaultwarden/web-vault/tags,
# click the tag name to view the digest of the image it currently points to.
# - From the command line:
# $ docker pull bitwardenrs/web-vault:v2.19.0
# $ docker image inspect --format "{{.RepoDigests}}" bitwardenrs/web-vault:v2.19.0
# [bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4]
# $ docker pull vaultwarden/web-vault:v2.19.0d
# $ docker image inspect --format "{{.RepoDigests}}" vaultwarden/web-vault:v2.19.0d
# [vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233]
#
# - Conversely, to get the tag name from the digest:
# $ docker image inspect --format "{{.RepoTags}}" bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4
# [bitwardenrs/web-vault:v2.19.0]
# $ docker image inspect --format "{{.RepoTags}}" vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233
# [vaultwarden/web-vault:v2.19.0d]
#
FROM bitwardenrs/web-vault@sha256:8747cfaa2c6d87d1749e119dd884697e8099389aa9aca30a4d73d4ff796fe0e4 as vault
FROM vaultwarden/web-vault@sha256:a7bd6bc4db33bd45f723c4b1ac90918b7f80204560683cfc8efd9efd03a9b233 as vault
########################## BUILD IMAGE ##########################
FROM messense/rust-musl-cross:armv7-musleabihf as build
@@ -67,7 +67,7 @@ RUN touch src/main.rs
# Builds again, this time it'll just be
# your actual source files being built
RUN cargo build --features ${DB} --release --target=armv7-unknown-linux-musleabihf
RUN musl-strip target/armv7-unknown-linux-musleabihf/release/bitwarden_rs
RUN musl-strip target/armv7-unknown-linux-musleabihf/release/vaultwarden
######################## RUNTIME IMAGE ########################
# Create a new stage with a minimal image
@@ -86,7 +86,6 @@ RUN apk add --no-cache \
openssl \
curl \
dumb-init \
sqlite \
ca-certificates
RUN mkdir /data
@@ -99,9 +98,10 @@ EXPOSE 3012
# Copies the files from the context (Rocket.toml file and web-vault)
# and the binary from the "build" stage to the current stage
WORKDIR /
COPY Rocket.toml .
COPY --from=vault /web-vault ./web-vault
COPY --from=build /app/target/armv7-unknown-linux-musleabihf/release/bitwarden_rs .
COPY --from=build /app/target/armv7-unknown-linux-musleabihf/release/vaultwarden .
COPY docker/healthcheck.sh /healthcheck.sh
COPY docker/start.sh /start.sh
@@ -109,6 +109,5 @@ COPY docker/start.sh /start.sh
HEALTHCHECK --interval=60s --timeout=10s CMD ["/healthcheck.sh"]
# Configures the startup!
WORKDIR /
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/start.sh"]

View File

@@ -1,10 +1,20 @@
#!/bin/sh
if [ -r /etc/bitwarden_rs.sh ]; then
if [ -r /etc/vaultwarden.sh ]; then
. /etc/vaultwarden.sh
elif [ -r /etc/bitwarden_rs.sh ]; then
echo "### You are using the old /etc/bitwarden_rs.sh script, please migrate to /etc/vaultwarden.sh ###"
. /etc/bitwarden_rs.sh
fi
if [ -d /etc/bitwarden_rs.d ]; then
if [ -d /etc/vaultwarden.d ]; then
for f in /etc/vaultwarden.d/*.sh; do
if [ -r $f ]; then
. $f
fi
done
elif [ -d /etc/bitwarden_rs.d ]; then
echo "### You are using the old /etc/bitwarden_rs.d script directory, please migrate to /etc/vaultwarden.d ###"
for f in /etc/bitwarden_rs.d/*.sh; do
if [ -r $f ]; then
. $f
@@ -12,4 +22,4 @@ if [ -d /etc/bitwarden_rs.d ]; then
done
fi
exec /bitwarden_rs "${@}"
exec /vaultwarden "${@}"

View File

@@ -10,7 +10,7 @@ Docker Hub hooks provide these predefined [environment variables](https://docs.d
* `DOCKER_TAG`: the Docker repository tag being built.
* `IMAGE_NAME`: the name and tag of the Docker repository being built. (This variable is a combination of `DOCKER_REPO:DOCKER_TAG`.)
The current multi-arch image build relies on the original bitwarden_rs Dockerfiles, which use cross-compilation for architectures other than `amd64`, and don't yet support all arch/distro combinations. However, cross-compilation is much faster than QEMU-based builds (e.g., using `docker buildx`). This situation may need to be revisited at some point.
The current multi-arch image build relies on the original vaultwarden Dockerfiles, which use cross-compilation for architectures other than `amd64`, and don't yet support all arch/distro combinations. However, cross-compilation is much faster than QEMU-based builds (e.g., using `docker buildx`). This situation may need to be revisited at some point.
## References

View File

@@ -22,7 +22,7 @@ fi
LABELS=(
# https://github.com/opencontainers/image-spec/blob/master/annotations.md
org.opencontainers.image.created="$(date --utc --iso-8601=seconds)"
org.opencontainers.image.documentation="https://github.com/dani-garcia/bitwarden_rs/wiki"
org.opencontainers.image.documentation="https://github.com/dani-garcia/vaultwarden/wiki"
org.opencontainers.image.licenses="GPL-3.0-only"
org.opencontainers.image.revision="${SOURCE_COMMIT}"
org.opencontainers.image.source="${SOURCE_REPOSITORY_URL}"

View File

@@ -103,7 +103,7 @@ docker buildx build \
# (https://github.com/moby/moby/issues/41017).
#
# Note that we use `arm32v6` instead of `armv6` to be consistent with the
# existing bitwarden_rs tags, which adhere to the naming conventions of the
# existing vaultwarden tags, which adhere to the naming conventions of the
# Docker per-architecture repos (e.g., https://hub.docker.com/u/arm32v6).
# Unfortunately, these per-arch repo names aren't always consistent with the
# corresponding platform (OS/arch/variant) IDs, particularly in the case of

View File

@@ -1 +1 @@
nightly-2021-02-22
nightly-2021-04-14

View File

@@ -1,2 +1,7 @@
version = "Two"
edition = "2018"
max_width = 120
newline_style = "Unix"
use_small_heuristics = "Off"
struct_lit_single_line = false
overflow_delimited_expr = true

View File

@@ -1,9 +1,8 @@
use once_cell::sync::Lazy;
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::{env, process::Command, time::Duration};
use std::{env, time::Duration};
use reqwest::{blocking::Client, header::USER_AGENT};
use rocket::{
http::{Cookie, Cookies, SameSite},
request::{self, FlashMessage, Form, FromRequest, Outcome, Request},
@@ -19,7 +18,7 @@ use crate::{
db::{backup_database, get_sql_server_version, models::*, DbConn, DbConnType},
error::{Error, MapResult},
mail,
util::{format_naive_datetime_local, get_display_size, is_running_in_docker},
util::{format_naive_datetime_local, get_display_size, get_reqwest_client, is_running_in_docker},
CONFIG,
};
@@ -64,12 +63,8 @@ static DB_TYPE: Lazy<&str> = Lazy::new(|| {
.unwrap_or("Unknown")
});
static CAN_BACKUP: Lazy<bool> = Lazy::new(|| {
DbConnType::from_url(&CONFIG.database_url())
.map(|t| t == DbConnType::sqlite)
.unwrap_or(false)
&& Command::new("sqlite3").arg("-version").status().is_ok()
});
static CAN_BACKUP: Lazy<bool> =
Lazy::new(|| DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::sqlite).unwrap_or(false));
#[get("/")]
fn admin_disabled() -> &'static str {
@@ -142,7 +137,12 @@ fn admin_url(referer: Referer) -> String {
fn admin_login(flash: Option<FlashMessage>) -> ApiResult<Html<String>> {
// If there is an error, show it
let msg = flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()));
let json = json!({"page_content": "admin/login", "version": VERSION, "error": msg, "urlpath": CONFIG.domain_path()});
let json = json!({
"page_content": "admin/login",
"version": VERSION,
"error": msg,
"urlpath": CONFIG.domain_path()
});
// Return the page
let text = CONFIG.render_template(BASE_TEMPLATE, &json)?;
@@ -166,10 +166,7 @@ fn post_admin_login(
// If the token is invalid, redirect to login page
if !_validate_token(&data.token) {
error!("Invalid admin token. IP: {}", ip.ip);
Err(Flash::error(
Redirect::to(admin_url(referer)),
"Invalid admin token, please try again.",
))
Err(Flash::error(Redirect::to(admin_url(referer)), "Invalid admin token, please try again."))
} else {
// If the token received is valid, generate JWT and save it as a cookie
let claims = generate_admin_claims();
@@ -329,7 +326,8 @@ fn get_users_json(_token: AdminToken, conn: DbConn) -> Json<Value> {
fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
let users = User::get_all(&conn);
let dt_fmt = "%Y-%m-%d %H:%M:%S %Z";
let users_json: Vec<Value> = users.iter()
let users_json: Vec<Value> = users
.iter()
.map(|u| {
let mut usr = u.to_json(&conn);
usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &conn));
@@ -339,7 +337,7 @@ fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
usr["created_at"] = json!(format_naive_datetime_local(&u.created_at, dt_fmt));
usr["last_active"] = match u.last_active(&conn) {
Some(dt) => json!(format_naive_datetime_local(&dt, dt_fmt)),
None => json!("Never")
None => json!("Never"),
};
usr
})
@@ -424,7 +422,6 @@ fn update_user_org_type(data: Json<UserOrgTypeData>, _token: AdminToken, conn: D
user_to_edit.save(&conn)
}
#[post("/users/update_revision")]
fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
User::update_all_revisions(&conn)
@@ -433,7 +430,8 @@ fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
#[get("/organizations/overview")]
fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
let organizations = Organization::get_all(&conn);
let organizations_json: Vec<Value> = organizations.iter()
let organizations_json: Vec<Value> = organizations
.iter()
.map(|o| {
let mut org = o.to_json();
org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &conn));
@@ -470,26 +468,15 @@ struct GitCommit {
}
fn get_github_api<T: DeserializeOwned>(url: &str) -> Result<T, Error> {
let github_api = Client::builder().build()?;
let github_api = get_reqwest_client();
Ok(github_api
.get(url)
.timeout(Duration::from_secs(10))
.header(USER_AGENT, "Bitwarden_RS")
.send()?
.error_for_status()?
.json::<T>()?)
Ok(github_api.get(url).timeout(Duration::from_secs(10)).send()?.error_for_status()?.json::<T>()?)
}
fn has_http_access() -> bool {
let http_access = Client::builder().build().unwrap();
let http_access = get_reqwest_client();
match http_access
.head("https://github.com/dani-garcia/bitwarden_rs")
.timeout(Duration::from_secs(10))
.header(USER_AGENT, "Bitwarden_RS")
.send()
{
match http_access.head("https://github.com/dani-garcia/vaultwarden").timeout(Duration::from_secs(10)).send() {
Ok(r) => r.status().is_success(),
_ => false,
}
@@ -502,9 +489,16 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
use std::net::ToSocketAddrs;
// Get current running versions
let vault_version_path = format!("{}/{}", CONFIG.web_vault_folder(), "version.json");
let vault_version_str = read_file_string(&vault_version_path)?;
let web_vault_version: WebVaultVersion = serde_json::from_str(&vault_version_str)?;
let web_vault_version: WebVaultVersion =
match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "bwrs-version.json")) {
Ok(s) => serde_json::from_str(&s)?,
_ => match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "version.json")) {
Ok(s) => serde_json::from_str(&s)?,
_ => WebVaultVersion {
version: String::from("Version file missing"),
},
},
};
// Execute some environment checks
let running_within_docker = is_running_in_docker();
@@ -524,11 +518,11 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
// TODO: Maybe we need to cache this using a LazyStatic or something. Github only allows 60 requests per hour, and we use 3 here already.
let (latest_release, latest_commit, latest_web_build) = if has_http_access {
(
match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/bitwarden_rs/releases/latest") {
match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest") {
Ok(r) => r.tag_name,
_ => "-".to_string(),
},
match get_github_api::<GitCommit>("https://api.github.com/repos/dani-garcia/bitwarden_rs/commits/master") {
match get_github_api::<GitCommit>("https://api.github.com/repos/dani-garcia/vaultwarden/commits/main") {
Ok(mut c) => {
c.sha.truncate(8);
c.sha
@@ -540,7 +534,9 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
if running_within_docker {
"-".to_string()
} else {
match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest") {
match get_github_api::<GitRelease>(
"https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest",
) {
Ok(r) => r.tag_name.trim_start_matches('v').to_string(),
_ => "-".to_string(),
}
@@ -552,14 +548,15 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
let ip_header_name = match &ip_header.0 {
Some(h) => h,
_ => ""
_ => "",
};
let diagnostics_json = json!({
"dns_resolved": dns_resolved,
"web_vault_version": web_vault_version.version,
"latest_release": latest_release,
"latest_commit": latest_commit,
"web_vault_enabled": &CONFIG.web_vault_enabled(),
"web_vault_version": web_vault_version.version,
"latest_web_build": latest_web_build,
"running_within_docker": running_within_docker,
"has_http_access": has_http_access,
@@ -571,6 +568,7 @@ fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResu
"db_type": *DB_TYPE,
"db_version": get_sql_server_version(&conn),
"admin_url": format!("{}/diagnostics", admin_url(Referer(None))),
"server_time_local": Local::now().format("%Y-%m-%d %H:%M:%S %Z").to_string(),
"server_time": Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(), // Run the date/time check as the last item to minimize the difference
});
@@ -596,11 +594,11 @@ fn delete_config(_token: AdminToken) -> EmptyResult {
}
#[post("/config/backup_db")]
fn backup_db(_token: AdminToken) -> EmptyResult {
fn backup_db(_token: AdminToken, conn: DbConn) -> EmptyResult {
if *CAN_BACKUP {
backup_database()
backup_database(&conn)
} else {
err!("Can't back up current DB (either it's not SQLite or the 'sqlite' binary is not present)");
err!("Can't back up current DB (Only SQLite supports this feature)");
}
}

View File

@@ -95,7 +95,7 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
}
None => {
// Order is important here; the invitation check must come first
// because the bitwarden_rs admin can invite anyone, regardless
// because the vaultwarden admin can invite anyone, regardless
// of other signup restrictions.
if Invitation::take(&data.Email, &conn) || CONFIG.is_signup_allowed(&data.Email) {
User::new(data.Email.clone())
@@ -320,15 +320,7 @@ fn post_rotatekey(data: JsonUpcase<KeyData>, headers: Headers, conn: DbConn, nt:
err!("The cipher is not owned by the user")
}
update_cipher_from_data(
&mut saved_cipher,
cipher_data,
&headers,
false,
&conn,
&nt,
UpdateType::CipherUpdate,
)?
update_cipher_from_data(&mut saved_cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?
}
// Update user data

View File

@@ -13,7 +13,7 @@ use crate::{
api::{self, EmptyResult, JsonResult, JsonUpcase, Notify, PasswordData, UpdateType},
auth::Headers,
crypto,
db::{models::*, DbConn},
db::{models::*, DbConn, DbPool},
CONFIG,
};
@@ -25,7 +25,7 @@ pub fn routes() -> Vec<Route> {
// whether the user is an owner/admin of the relevant org, and if so,
// allows the operation unconditionally.
//
// bitwarden_rs factors in the org owner/admin status as part of
// vaultwarden factors in the org owner/admin status as part of
// determining the write accessibility of a cipher, so most
// admin/non-admin implementations can be shared.
routes![
@@ -77,6 +77,15 @@ pub fn routes() -> Vec<Route> {
]
}
pub fn purge_trashed_ciphers(pool: DbPool) {
debug!("Purging trashed ciphers");
if let Ok(conn) = pool.get() {
Cipher::purge_trash(&conn);
} else {
error!("Failed to get DB connection while purging trashed ciphers")
}
}
#[derive(FromForm, Default)]
struct SyncData {
#[form(field = "excludeDomains")]
@@ -91,24 +100,18 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> Json<Value> {
let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn);
let collections_json: Vec<Value> = collections.iter()
.map(|c| c.to_json_details(&headers.user.uuid, &conn))
.collect();
let collections_json: Vec<Value> =
collections.iter().map(|c| c.to_json_details(&headers.user.uuid, &conn)).collect();
let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn);
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers
.iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
.collect();
let ciphers_json: Vec<Value> =
ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
let sends = Send::find_by_user(&headers.user.uuid, &conn);
let sends_json: Vec<Value> = sends
.iter()
.map(|s| s.to_json())
.collect();
let sends_json: Vec<Value> = sends.iter().map(|s| s.to_json()).collect();
let domains_json = if data.exclude_domains {
Value::Null
@@ -124,6 +127,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> Json<Value> {
"Ciphers": ciphers_json,
"Domains": domains_json,
"Sends": sends_json,
"unofficialServer": true,
"Object": "sync"
}))
}
@@ -132,10 +136,8 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> Json<Value> {
fn get_ciphers(headers: Headers, conn: DbConn) -> Json<Value> {
let ciphers = Cipher::find_by_user_visible(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers
.iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
.collect();
let ciphers_json: Vec<Value> =
ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
Json(json!({
"Data": ciphers_json,
@@ -236,7 +238,7 @@ fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn
// Check if there are one more more collections selected when this cipher is part of an organization.
// err if this is not the case before creating an empty cipher.
if data.Cipher.OrganizationId.is_some() && data.CollectionIds.is_empty() {
if data.Cipher.OrganizationId.is_some() && data.CollectionIds.is_empty() {
err!("You must select at least one collection.");
}
@@ -278,17 +280,12 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt
/// allowed to delete or share such ciphers to an org, however.
///
/// Ref: https://bitwarden.com/help/article/policies/#personal-ownership
fn enforce_personal_ownership_policy(
data: &CipherData,
headers: &Headers,
conn: &DbConn
) -> EmptyResult {
fn enforce_personal_ownership_policy(data: &CipherData, headers: &Headers, conn: &DbConn) -> EmptyResult {
if data.OrganizationId.is_none() {
let user_uuid = &headers.user.uuid;
let policy_type = OrgPolicyType::PersonalOwnership;
if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) {
err!("Due to an Enterprise Policy, you are restricted from \
saving items to your personal vault.")
err!("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.")
}
}
Ok(())
@@ -307,11 +304,12 @@ pub fn update_cipher_from_data(
// Check that the client isn't updating an existing cipher with stale data.
if let Some(dt) = data.LastKnownRevisionDate {
match NaiveDateTime::parse_from_str(&dt, "%+") { // ISO 8601 format
Err(err) =>
warn!("Error parsing LastKnownRevisionDate '{}': {}", dt, err),
Ok(dt) if cipher.updated_at.signed_duration_since(dt).num_seconds() > 1 =>
err!("The client copy of this cipher is out of date. Resync the client and try again."),
match NaiveDateTime::parse_from_str(&dt, "%+") {
// ISO 8601 format
Err(err) => warn!("Error parsing LastKnownRevisionDate '{}': {}", dt, err),
Ok(dt) if cipher.updated_at.signed_duration_since(dt).num_seconds() > 1 => {
err!("The client copy of this cipher is out of date. Resync the client and try again.")
}
Ok(_) => (),
}
}
@@ -384,12 +382,9 @@ pub fn update_cipher_from_data(
// But, we at least know we do not need to store and return this specific key.
fn _clean_cipher_data(mut json_data: Value) -> Value {
if json_data.is_array() {
json_data.as_array_mut()
.unwrap()
.iter_mut()
.for_each(|ref mut f| {
f.as_object_mut().unwrap().remove("Response");
});
json_data.as_array_mut().unwrap().iter_mut().for_each(|ref mut f| {
f.as_object_mut().unwrap().remove("Response");
});
};
json_data
}
@@ -411,13 +406,13 @@ pub fn update_cipher_from_data(
data["Uris"] = _clean_cipher_data(data["Uris"].clone());
}
data
},
}
None => err!("Data missing"),
};
cipher.name = data.Name;
cipher.notes = data.Notes;
cipher.fields = data.Fields.map(|f| _clean_cipher_data(f).to_string() );
cipher.fields = data.Fields.map(|f| _clean_cipher_data(f).to_string());
cipher.data = type_data.to_string();
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
@@ -592,11 +587,8 @@ fn post_collections_admin(
}
let posted_collections: HashSet<String> = data.CollectionIds.iter().cloned().collect();
let current_collections: HashSet<String> = cipher
.get_collections(&headers.user.uuid, &conn)
.iter()
.cloned()
.collect();
let current_collections: HashSet<String> =
cipher.get_collections(&headers.user.uuid, &conn).iter().cloned().collect();
for collection in posted_collections.symmetric_difference(&current_collections) {
match Collection::find_by_uuid(&collection, &conn) {
@@ -832,24 +824,25 @@ fn post_attachment(
let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
let path = base_path.join(&file_name);
let size = match field.data.save().memory_threshold(0).size_limit(size_limit).with_path(path.clone()) {
SaveResult::Full(SavedData::File(_, size)) => size as i32,
SaveResult::Full(other) => {
std::fs::remove_file(path).ok();
error = Some(format!("Attachment is not a file: {:?}", other));
return;
}
SaveResult::Partial(_, reason) => {
std::fs::remove_file(path).ok();
error = Some(format!("Attachment size limit exceeded with this file: {:?}", reason));
return;
}
SaveResult::Error(e) => {
std::fs::remove_file(path).ok();
error = Some(format!("Error: {:?}", e));
return;
}
};
let size =
match field.data.save().memory_threshold(0).size_limit(size_limit).with_path(path.clone()) {
SaveResult::Full(SavedData::File(_, size)) => size as i32,
SaveResult::Full(other) => {
std::fs::remove_file(path).ok();
error = Some(format!("Attachment is not a file: {:?}", other));
return;
}
SaveResult::Partial(_, reason) => {
std::fs::remove_file(path).ok();
error = Some(format!("Attachment size limit exceeded with this file: {:?}", reason));
return;
}
SaveResult::Error(e) => {
std::fs::remove_file(path).ok();
error = Some(format!("Error: {:?}", e));
return;
}
};
let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
attachment.akey = attachment_key.clone();
@@ -984,12 +977,22 @@ fn delete_cipher_selected_admin(data: JsonUpcase<Value>, headers: Headers, conn:
}
#[post("/ciphers/delete-admin", data = "<data>")]
fn delete_cipher_selected_post_admin(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
fn delete_cipher_selected_post_admin(
data: JsonUpcase<Value>,
headers: Headers,
conn: DbConn,
nt: Notify,
) -> EmptyResult {
delete_cipher_selected_post(data, headers, conn, nt)
}
#[put("/ciphers/delete-admin", data = "<data>")]
fn delete_cipher_selected_put_admin(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, nt: Notify) -> EmptyResult {
fn delete_cipher_selected_put_admin(
data: JsonUpcase<Value>,
headers: Headers,
conn: DbConn,
nt: Notify,
) -> EmptyResult {
delete_cipher_selected_put(data, headers, conn, nt)
}
@@ -1140,7 +1143,13 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, soft_del
Ok(())
}
fn _delete_multiple_ciphers(data: JsonUpcase<Value>, headers: Headers, conn: DbConn, soft_delete: bool, nt: Notify) -> EmptyResult {
fn _delete_multiple_ciphers(
data: JsonUpcase<Value>,
headers: Headers,
conn: DbConn,
soft_delete: bool,
nt: Notify,
) -> EmptyResult {
let data: Value = data.into_inner().data;
let uuids = match data.get("Ids") {
@@ -1192,7 +1201,7 @@ fn _restore_multiple_ciphers(data: JsonUpcase<Value>, headers: &Headers, conn: &
for uuid in uuids {
match _restore_cipher_by_uuid(uuid, headers, conn, nt) {
Ok(json) => ciphers.push(json.into_inner()),
err => return err
err => return err,
}
}

View File

@@ -8,15 +8,7 @@ use crate::{
};
pub fn routes() -> Vec<rocket::Route> {
routes![
get_folders,
get_folder,
post_folders,
post_folder,
put_folder,
delete_folder_post,
delete_folder,
]
routes![get_folders, get_folder, post_folders, post_folder, put_folder, delete_folder_post, delete_folder,]
}
#[get("/folders")]

View File

@@ -2,20 +2,15 @@ mod accounts;
mod ciphers;
mod folders;
mod organizations;
pub mod two_factor;
mod sends;
pub mod two_factor;
pub use sends::start_send_deletion_scheduler;
pub use ciphers::purge_trashed_ciphers;
pub use sends::purge_sends;
pub fn routes() -> Vec<Route> {
let mut mod_routes = routes![
clear_device_token,
put_device_token,
get_eq_domains,
post_eq_domains,
put_eq_domains,
hibp_breach,
];
let mut mod_routes =
routes![clear_device_token, put_device_token, get_eq_domains, post_eq_domains, put_eq_domains, hibp_breach,];
let mut routes = Vec::new();
routes.append(&mut accounts::routes());
@@ -32,9 +27,9 @@ pub fn routes() -> Vec<Route> {
//
// Move this somewhere else
//
use rocket::response::Response;
use rocket::Route;
use rocket_contrib::json::Json;
use rocket::response::Response;
use serde_json::Value;
use crate::{
@@ -42,6 +37,7 @@ use crate::{
auth::Headers,
db::DbConn,
error::Error,
util::get_reqwest_client,
};
#[put("/devices/identifier/<uuid>/clear-token")]
@@ -146,22 +142,15 @@ fn put_eq_domains(data: JsonUpcase<EquivDomainData>, headers: Headers, conn: DbC
#[get("/hibp/breach?<username>")]
fn hibp_breach(username: String) -> JsonResult {
let user_agent = "Bitwarden_RS";
let url = format!(
"https://haveibeenpwned.com/api/v3/breachedaccount/{}?truncateResponse=false&includeUnverified=false",
username
);
use reqwest::{blocking::Client, header::USER_AGENT};
if let Some(api_key) = crate::CONFIG.hibp_api_key() {
let hibp_client = Client::builder().build()?;
let hibp_client = get_reqwest_client();
let res = hibp_client
.get(&url)
.header(USER_AGENT, user_agent)
.header("hibp-api-key", api_key)
.send()?;
let res = hibp_client.get(&url).header("hibp-api-key", api_key).send()?;
// If we get a 404, return a 404, it means no breached accounts
if res.status() == 404 {

View File

@@ -5,7 +5,7 @@ use serde_json::Value;
use crate::{
api::{EmptyResult, JsonResult, JsonUpcase, JsonUpcaseVec, Notify, NumberOrString, PasswordData, UpdateType},
auth::{decode_invite, AdminHeaders, Headers, OwnerHeaders, ManagerHeaders, ManagerHeadersLoose},
auth::{decode_invite, AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
db::{models::*, DbConn},
mail, CONFIG,
};
@@ -333,7 +333,12 @@ fn post_organization_collection_delete_user(
}
#[delete("/organizations/<org_id>/collections/<col_id>")]
fn delete_organization_collection(org_id: String, col_id: String, _headers: ManagerHeaders, conn: DbConn) -> EmptyResult {
fn delete_organization_collection(
org_id: String,
col_id: String,
_headers: ManagerHeaders,
conn: DbConn,
) -> EmptyResult {
match Collection::find_by_uuid(&col_id, &conn) {
None => err!("Collection not found"),
Some(collection) => {
@@ -426,9 +431,7 @@ fn put_collection_users(
continue;
}
CollectionUser::save(&user.user_uuid, &coll_id,
d.ReadOnly, d.HidePasswords,
&conn)?;
CollectionUser::save(&user.user_uuid, &coll_id, d.ReadOnly, d.HidePasswords, &conn)?;
}
Ok(())
@@ -443,10 +446,8 @@ struct OrgIdData {
#[get("/ciphers/organization-details?<data..>")]
fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> Json<Value> {
let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
let ciphers_json: Vec<Value> = ciphers
.iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn))
.collect();
let ciphers_json: Vec<Value> =
ciphers.iter().map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)).collect();
Json(json!({
"Data": ciphers_json,
@@ -544,9 +545,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
None => err!("Collection not found in Organization"),
Some(collection) => {
CollectionUser::save(&user.uuid, &collection.uuid,
col.ReadOnly, col.HidePasswords,
&conn)?;
CollectionUser::save(&user.uuid, &collection.uuid, col.ReadOnly, col.HidePasswords, &conn)?;
}
}
}
@@ -801,9 +800,13 @@ fn edit_user(
match Collection::find_by_uuid_and_org(&col.Id, &org_id, &conn) {
None => err!("Collection not found in Organization"),
Some(collection) => {
CollectionUser::save(&user_to_edit.user_uuid, &collection.uuid,
col.ReadOnly, col.HidePasswords,
&conn)?;
CollectionUser::save(
&user_to_edit.user_uuid,
&collection.uuid,
col.ReadOnly,
col.HidePasswords,
&conn,
)?;
}
}
}
@@ -899,16 +902,8 @@ fn post_org_import(
.into_iter()
.map(|cipher_data| {
let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone());
update_cipher_from_data(
&mut cipher,
cipher_data,
&headers,
false,
&conn,
&nt,
UpdateType::CipherCreate,
)
.ok();
update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn, &nt, UpdateType::CipherCreate)
.ok();
cipher
})
.collect();
@@ -989,7 +984,13 @@ struct PolicyData {
}
#[put("/organizations/<org_id>/policies/<pol_type>", data = "<data>")]
fn put_policy(org_id: String, pol_type: i32, data: Json<PolicyData>, _headers: AdminHeaders, conn: DbConn) -> JsonResult {
fn put_policy(
org_id: String,
pol_type: i32,
data: Json<PolicyData>,
_headers: AdminHeaders,
conn: DbConn,
) -> JsonResult {
let data: PolicyData = data.into_inner();
let pol_type_enum = match OrgPolicyType::from_i32(pol_type) {
@@ -1127,8 +1128,7 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
// If user is not part of the organization, but it exists
} else if UserOrganization::find_by_email_and_org(&user_data.Email, &org_id, &conn).is_none() {
if let Some (user) = User::find_by_mail(&user_data.Email, &conn) {
if let Some(user) = User::find_by_mail(&user_data.Email, &conn) {
let user_org_status = if CONFIG.mail_enabled() {
UserOrgStatus::Invited as i32
} else {
@@ -1164,7 +1164,7 @@ fn import(org_id: String, data: JsonUpcase<OrgImportData>, headers: Headers, con
// If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true)
if data.OverwriteExisting {
for user_org in UserOrganization::find_by_org_and_type(&org_id, UserOrgType::User as i32, &conn) {
if let Some (user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).map(|u| u.email) {
if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &conn).map(|u| u.email) {
if !data.Users.iter().any(|u| u.Email == user_email) {
user_org.delete(&conn)?;
}

View File

@@ -9,39 +9,23 @@ use serde_json::Value;
use crate::{
api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, Notify, UpdateType},
auth::{Headers, Host},
db::{models::*, DbConn},
db::{models::*, DbConn, DbPool},
CONFIG,
};
const SEND_INACCESSIBLE_MSG: &str = "Send does not exist or is no longer available";
pub fn routes() -> Vec<rocket::Route> {
routes![
post_send,
post_send_file,
post_access,
post_access_file,
put_send,
delete_send,
put_remove_password
]
routes![post_send, post_send_file, post_access, post_access_file, put_send, delete_send, put_remove_password]
}
pub fn start_send_deletion_scheduler(pool: crate::db::DbPool) {
std::thread::spawn(move || {
loop {
if let Ok(conn) = pool.get() {
info!("Initiating send deletion");
for send in Send::find_all(&conn) {
if chrono::Utc::now().naive_utc() >= send.deletion_date {
send.delete(&conn).ok();
}
}
}
std::thread::sleep(std::time::Duration::from_secs(3600));
}
});
pub fn purge_sends(pool: DbPool) {
debug!("Purging sends");
if let Ok(conn) = pool.get() {
Send::purge(&conn);
} else {
error!("Failed to get DB connection while purging sends")
}
}
#[derive(Deserialize)]
@@ -179,13 +163,7 @@ fn post_send_file(data: Data, content_type: &ContentType, headers: Headers, conn
None => err!("No model entry present"),
};
let size = match data_entry
.data
.save()
.memory_threshold(0)
.size_limit(size_limit)
.with_path(&file_path)
{
let size = match data_entry.data.save().memory_threshold(0).size_limit(size_limit).with_path(&file_path) {
SaveResult::Full(SavedData::File(_, size)) => size as i32,
SaveResult::Full(other) => {
std::fs::remove_file(&file_path).ok();
@@ -206,10 +184,7 @@ fn post_send_file(data: Data, content_type: &ContentType, headers: Headers, conn
if let Some(o) = data_value.as_object_mut() {
o.insert(String::from("Id"), Value::String(file_id));
o.insert(String::from("Size"), Value::Number(size.into()));
o.insert(
String::from("SizeName"),
Value::String(crate::util::get_display_size(size)),
);
o.insert(String::from("SizeName"), Value::String(crate::util::get_display_size(size)));
}
send.data = serde_json::to_string(&data_value)?;

View File

@@ -17,11 +17,7 @@ use crate::{
pub use crate::config::CONFIG;
pub fn routes() -> Vec<Route> {
routes![
generate_authenticator,
activate_authenticator,
activate_authenticator_put,
]
routes![generate_authenticator, activate_authenticator, activate_authenticator_put,]
}
#[post("/two-factor/get-authenticator", data = "<data>")]
@@ -141,7 +137,7 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, ip: &Cl
// The amount of steps back and forward in time
// Also check if we need to disable time drifted TOTP codes.
// If that is the case, we set the steps to 0 so only the current TOTP is valid.
let steps: i64 = if CONFIG.authenticator_disable_time_drift() { 0 } else { 1 };
let steps = !CONFIG.authenticator_disable_time_drift() as i64;
for step in -steps..=steps {
let time_step = current_timestamp / 30i64 + step;
@@ -163,22 +159,11 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, ip: &Cl
twofactor.save(&conn)?;
return Ok(());
} else if generated == totp_code && time_step <= twofactor.last_used as i64 {
warn!(
"This or a TOTP code within {} steps back and forward has already been used!",
steps
);
err!(format!(
"Invalid TOTP code! Server time: {} IP: {}",
current_time.format("%F %T UTC"),
ip.ip
));
warn!("This or a TOTP code within {} steps back and forward has already been used!", steps);
err!(format!("Invalid TOTP code! Server time: {} IP: {}", current_time.format("%F %T UTC"), ip.ip));
}
}
// Else no valide code received, deny access
err!(format!(
"Invalid TOTP code! Server time: {} IP: {}",
current_time.format("%F %T UTC"),
ip.ip
));
err!(format!("Invalid TOTP code! Server time: {} IP: {}", current_time.format("%F %T UTC"), ip.ip));
}

View File

@@ -12,6 +12,7 @@ use crate::{
DbConn,
},
error::MapResult,
util::get_reqwest_client,
CONFIG,
};
@@ -59,7 +60,11 @@ impl DuoData {
ik.replace_range(digits.., replaced);
sk.replace_range(digits.., replaced);
Self { host, ik, sk }
Self {
host,
ik,
sk,
}
}
}
@@ -185,9 +190,7 @@ fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbC
}
fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> EmptyResult {
const AGENT: &str = "bitwarden_rs:Duo/1.0 (Rust)";
use reqwest::{blocking::Client, header::*, Method};
use reqwest::{header, Method};
use std::str::FromStr;
// https://duo.com/docs/authapi#api-details
@@ -199,11 +202,13 @@ fn duo_api_request(method: &str, path: &str, params: &str, data: &DuoData) -> Em
let m = Method::from_str(method).unwrap_or_default();
Client::new()
let client = get_reqwest_client();
client
.request(m, &url)
.basic_auth(username, Some(password))
.header(USER_AGENT, AGENT)
.header(DATE, date)
.header(header::USER_AGENT, "vaultwarden:Duo/1.0 (Rust)")
.header(header::DATE, date)
.send()?
.error_for_status()?;

View File

@@ -125,11 +125,7 @@ fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, conn: DbConn) -
let twofactor_data = EmailTokenData::new(data.Email, generated_token);
// Uses EmailVerificationChallenge as type to show that it's not verified yet.
let twofactor = TwoFactor::new(
user.uuid,
TwoFactorType::EmailVerificationChallenge,
twofactor_data.to_json(),
);
let twofactor = TwoFactor::new(user.uuid, TwoFactorType::EmailVerificationChallenge, twofactor_data.to_json());
twofactor.save(&conn)?;
mail::send_token(&twofactor_data.email, &twofactor_data.last_token.map_res("Token is empty")?)?;
@@ -186,7 +182,8 @@ fn email(data: JsonUpcase<EmailData>, headers: Headers, conn: DbConn) -> JsonRes
/// Validate the email code when used as TwoFactor token mechanism
pub fn validate_email_code_str(user_uuid: &str, token: &str, data: &str, conn: &DbConn) -> EmptyResult {
let mut email_data = EmailTokenData::from_json(&data)?;
let mut twofactor = TwoFactor::find_by_user_and_type(&user_uuid, TwoFactorType::Email as i32, &conn).map_res("Two factor not found")?;
let mut twofactor = TwoFactor::find_by_user_and_type(&user_uuid, TwoFactorType::Email as i32, &conn)
.map_res("Two factor not found")?;
let issued_token = match &email_data.last_token {
Some(t) => t,
_ => err!("No token available"),

View File

@@ -20,13 +20,7 @@ pub mod u2f;
pub mod yubikey;
pub fn routes() -> Vec<Route> {
let mut routes = routes![
get_twofactor,
get_recover,
recover,
disable_twofactor,
disable_twofactor_put,
];
let mut routes = routes![get_twofactor, get_recover, recover, disable_twofactor, disable_twofactor_put,];
routes.append(&mut authenticator::routes());
routes.append(&mut duo::routes());

View File

@@ -28,13 +28,7 @@ static APP_ID: Lazy<String> = Lazy::new(|| format!("{}/app-id.json", &CONFIG.dom
static U2F: Lazy<U2f> = Lazy::new(|| U2f::new(APP_ID.clone()));
pub fn routes() -> Vec<Route> {
routes![
generate_u2f,
generate_u2f_challenge,
activate_u2f,
activate_u2f_put,
delete_u2f,
]
routes![generate_u2f, generate_u2f_challenge, activate_u2f, activate_u2f_put, delete_u2f,]
}
#[post("/two-factor/get-u2f", data = "<data>")]
@@ -161,10 +155,7 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
let response: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
let error_code = response
.error_code
.clone()
.map_or("0".into(), NumberOrString::into_string);
let error_code = response.error_code.clone().map_or("0".into(), NumberOrString::into_string);
if error_code != "0" {
err!("Error registering U2F token")
@@ -300,20 +291,13 @@ fn _old_parse_registrations(registations: &str) -> Vec<Registration> {
let regs: Vec<Value> = serde_json::from_str(registations).expect("Can't parse Registration data");
regs.into_iter()
.map(|r| serde_json::from_value(r).unwrap())
.map(|Helper(r)| r)
.collect()
regs.into_iter().map(|r| serde_json::from_value(r).unwrap()).map(|Helper(r)| r).collect()
}
pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult<U2fSignRequest> {
let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn);
let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn)?
.1
.into_iter()
.map(|r| r.reg)
.collect();
let registrations: Vec<_> = get_u2f_registrations(user_uuid, conn)?.1.into_iter().map(|r| r.reg).collect();
if registrations.is_empty() {
err!("No U2F devices registered")

View File

@@ -12,14 +12,16 @@ use regex::Regex;
use reqwest::{blocking::Client, blocking::Response, header, Url};
use rocket::{http::ContentType, http::Cookie, response::Content, Route};
use crate::{error::Error, util::Cached, CONFIG};
use crate::{
error::Error,
util::{get_reqwest_client_builder, Cached},
CONFIG,
};
pub fn routes() -> Vec<Route> {
routes![icon]
}
const ALLOWED_CHARS: &str = "_-.";
static CLIENT: Lazy<Client> = Lazy::new(|| {
// Generate the default headers
let mut default_headers = header::HeaderMap::new();
@@ -27,31 +29,47 @@ static CLIENT: Lazy<Client> = Lazy::new(|| {
default_headers.insert(header::ACCEPT_LANGUAGE, header::HeaderValue::from_static("en-US,en;q=0.8"));
default_headers.insert(header::CACHE_CONTROL, header::HeaderValue::from_static("no-cache"));
default_headers.insert(header::PRAGMA, header::HeaderValue::from_static("no-cache"));
default_headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html,application/xhtml+xml,application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8"));
default_headers.insert(
header::ACCEPT,
header::HeaderValue::from_static(
"text/html,application/xhtml+xml,application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8",
),
);
// Reuse the client between requests
Client::builder()
get_reqwest_client_builder()
.timeout(Duration::from_secs(CONFIG.icon_download_timeout()))
.default_headers(default_headers)
.build()
.unwrap()
.expect("Failed to build icon client")
});
// Build Regex only once since this takes a lot of time.
static ICON_REL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)icon$|apple.*icon").unwrap());
static ICON_REL_BLACKLIST: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)mask-icon").unwrap());
static ICON_SIZE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?x)(\d+)\D*(\d+)").unwrap());
// Special HashMap which holds the user defined Regex to speedup matching the regex.
static ICON_BLACKLIST_REGEX: Lazy<RwLock<HashMap<String, Regex>>> = Lazy::new(|| RwLock::new(HashMap::new()));
#[get("/<domain>/icon.png")]
fn icon(domain: String) -> Option<Cached<Content<Vec<u8>>>> {
fn icon(domain: String) -> Cached<Content<Vec<u8>>> {
const FALLBACK_ICON: &[u8] = include_bytes!("../static/images/fallback-icon.png");
if !is_valid_domain(&domain) {
warn!("Invalid domain: {}", domain);
return None;
return Cached::ttl(
Content(ContentType::new("image", "png"), FALLBACK_ICON.to_vec()),
CONFIG.icon_cache_negttl(),
);
}
get_icon(&domain).map(|icon| Cached::ttl(Content(ContentType::new("image", "x-icon"), icon), CONFIG.icon_cache_ttl()))
match get_icon(&domain) {
Some((icon, icon_type)) => {
Cached::ttl(Content(ContentType::new("image", icon_type), icon), CONFIG.icon_cache_ttl())
}
_ => Cached::ttl(Content(ContentType::new("image", "png"), FALLBACK_ICON.to_vec()), CONFIG.icon_cache_negttl()),
}
}
/// Returns if the domain provided is valid or not.
@@ -59,6 +77,8 @@ fn icon(domain: String) -> Option<Cached<Content<Vec<u8>>>> {
/// This does some manual checks and makes use of Url to do some basic checking.
/// domains can't be larger then 63 characters (not counting multiple subdomains) according to the RFC's, but we limit the total size to 255.
fn is_valid_domain(domain: &str) -> bool {
const ALLOWED_CHARS: &str = "_-.";
// If parsing the domain fails using Url, it will not work with reqwest.
if let Err(parse_error) = Url::parse(format!("https://{}", domain).as_str()) {
debug!("Domain parse error: '{}' - {:?}", domain, parse_error);
@@ -69,7 +89,10 @@ fn is_valid_domain(domain: &str) -> bool {
|| domain.starts_with('-')
|| domain.ends_with('-')
{
debug!("Domain validation error: '{}' is either empty, contains '..', starts with an '.', starts or ends with a '-'", domain);
debug!(
"Domain validation error: '{}' is either empty, contains '..', starts with an '.', starts or ends with a '-'",
domain
);
return false;
} else if domain.len() > 255 {
debug!("Domain validation error: '{}' exceeds 255 characters", domain);
@@ -238,7 +261,7 @@ fn is_domain_blacklisted(domain: &str) -> bool {
is_blacklisted
}
fn get_icon(domain: &str) -> Option<Vec<u8>> {
fn get_icon(domain: &str) -> Option<(Vec<u8>, String)> {
let path = format!("{}/{}.png", CONFIG.icon_cache_folder(), domain);
// Check for expiration of negatively cached copy
@@ -247,7 +270,11 @@ fn get_icon(domain: &str) -> Option<Vec<u8>> {
}
if let Some(icon) = get_cached_icon(&path) {
return Some(icon);
let icon_type = match get_icon_type(&icon) {
Some(x) => x,
_ => "x-icon",
};
return Some((icon, icon_type.to_string()));
}
if CONFIG.disable_icon_download() {
@@ -256,9 +283,9 @@ fn get_icon(domain: &str) -> Option<Vec<u8>> {
// Get the icon, or None in case of error
match download_icon(&domain) {
Ok(icon) => {
Ok((icon, icon_type)) => {
save_icon(&path, &icon);
Some(icon)
Some((icon, icon_type.unwrap_or("x-icon").to_string()))
}
Err(e) => {
error!("Error downloading icon: {:?}", e);
@@ -319,7 +346,6 @@ fn icon_is_expired(path: &str) -> bool {
expired.unwrap_or(true)
}
#[derive(Debug)]
struct Icon {
priority: u8,
href: String,
@@ -327,12 +353,20 @@ struct Icon {
impl Icon {
const fn new(priority: u8, href: String) -> Self {
Self { href, priority }
Self {
href,
priority,
}
}
}
fn get_favicons_node(node: &std::rc::Rc<markup5ever_rcdom::Node>, icons: &mut Vec<Icon>, url: &Url) {
if let markup5ever_rcdom::NodeData::Element { name, attrs, .. } = &node.data {
if let markup5ever_rcdom::NodeData::Element {
name,
attrs,
..
} = &node.data
{
if name.local.as_ref() == "link" {
let mut has_rel = false;
let mut href = None;
@@ -343,7 +377,8 @@ fn get_favicons_node(node: &std::rc::Rc<markup5ever_rcdom::Node>, icons: &mut Ve
let attr_name = attr.name.local.as_ref();
let attr_value = attr.value.as_ref();
if attr_name == "rel" && ICON_REL_REGEX.is_match(attr_value) {
if attr_name == "rel" && ICON_REL_REGEX.is_match(attr_value) && !ICON_REL_BLACKLIST.is_match(attr_value)
{
has_rel = true;
} else if attr_name == "href" {
href = Some(attr_value);
@@ -486,10 +521,10 @@ fn get_icon_url(domain: &str) -> Result<IconUrlResult, Error> {
iconlist.sort_by_key(|x| x.priority);
// There always is an icon in the list, so no need to check if it exists, and just return the first one
Ok(IconUrlResult{
Ok(IconUrlResult {
iconlist,
cookies: cookie_str,
referer
referer,
})
}
@@ -510,9 +545,7 @@ fn get_page_with_cookies(url: &str, cookie_str: &str, referer: &str) -> Result<R
client = client.header("Referer", referer)
}
client.send()?
.error_for_status()
.map_err(Into::into)
client.send()?.error_for_status().map_err(Into::into)
}
/// Returns a Integer with the priority of the type of the icon which to prefer.
@@ -594,7 +627,7 @@ fn parse_sizes(sizes: Option<&str>) -> (u16, u16) {
(width, height)
}
fn download_icon(domain: &str) -> Result<Vec<u8>, Error> {
fn download_icon(domain: &str) -> Result<(Vec<u8>, Option<&str>), Error> {
if is_domain_blacklisted(domain) {
err!("Domain is blacklisted", domain)
}
@@ -602,6 +635,7 @@ fn download_icon(domain: &str) -> Result<Vec<u8>, Error> {
let icon_result = get_icon_url(&domain)?;
let mut buffer = Vec::new();
let mut icon_type: Option<&str> = None;
use data_url::DataUrl;
@@ -613,29 +647,43 @@ fn download_icon(domain: &str) -> Result<Vec<u8>, Error> {
Ok((body, _fragment)) => {
// Also check if the size is atleast 67 bytes, which seems to be the smallest png i could create
if body.len() >= 67 {
// Check if the icon type is allowed, else try an icon from the list.
icon_type = get_icon_type(&body);
if icon_type.is_none() {
debug!("Icon from {} data:image uri, is not a valid image type", domain);
continue;
}
info!("Extracted icon from data:image uri for {}", domain);
buffer = body;
break;
}
}
_ => warn!("data uri is invalid"),
_ => warn!("Extracted icon from data:image uri is invalid"),
};
} else {
match get_page_with_cookies(&icon.href, &icon_result.cookies, &icon_result.referer) {
Ok(mut res) => {
info!("Downloaded icon from {}", icon.href);
res.copy_to(&mut buffer)?;
// Check if the icon type is allowed, else try an icon from the list.
icon_type = get_icon_type(&buffer);
if icon_type.is_none() {
buffer.clear();
debug!("Icon from {}, is not a valid image type", icon.href);
continue;
}
info!("Downloaded icon from {}", icon.href);
break;
},
}
_ => warn!("Download failed for {}", icon.href),
};
}
}
if buffer.is_empty() {
err!("Empty response")
err!("Empty response downloading icon")
}
Ok(buffer)
Ok((buffer, icon_type))
}
fn save_icon(path: &str, icon: &[u8]) {
@@ -647,7 +695,18 @@ fn save_icon(path: &str, icon: &[u8]) {
create_dir_all(&CONFIG.icon_cache_folder()).expect("Error creating icon cache");
}
Err(e) => {
info!("Icon save error: {:?}", e);
warn!("Icon save error: {:?}", e);
}
}
}
fn get_icon_type(bytes: &[u8]) -> Option<&'static str> {
match bytes {
[137, 80, 78, 71, ..] => Some("png"),
[0, 0, 1, 0, ..] => Some("x-icon"),
[82, 73, 70, 70, ..] => Some("webp"),
[255, 216, 255, ..] => Some("jpeg"),
[66, 77, ..] => Some("bmp"),
_ => None,
}
}

View File

@@ -72,7 +72,8 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult {
"Kdf": user.client_kdf_type,
"KdfIterations": user.client_kdf_iter,
"ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing
"scope": "api offline_access"
"scope": "api offline_access",
"unofficialServer": true,
})))
}
@@ -87,34 +88,28 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
let username = data.username.as_ref().unwrap();
let user = match User::find_by_mail(username, &conn) {
Some(user) => user,
None => err!(
"Username or password is incorrect. Try again",
format!("IP: {}. Username: {}.", ip.ip, username)
),
None => err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username)),
};
// Check password
let password = data.password.as_ref().unwrap();
if !user.check_valid_password(password) {
err!(
"Username or password is incorrect. Try again",
format!("IP: {}. Username: {}.", ip.ip, username)
)
err!("Username or password is incorrect. Try again", format!("IP: {}. Username: {}.", ip.ip, username))
}
// Check if the user is disabled
if !user.enabled {
err!(
"This user has been disabled",
format!("IP: {}. Username: {}.", ip.ip, username)
)
err!("This user has been disabled", format!("IP: {}. Username: {}.", ip.ip, username))
}
let now = Local::now();
if user.verified_at.is_none() && CONFIG.mail_enabled() && CONFIG.signups_verify() {
let now = now.naive_utc();
if user.last_verifying_at.is_none() || now.signed_duration_since(user.last_verifying_at.unwrap()).num_seconds() > CONFIG.signups_verify_resend_time() as i64 {
if user.last_verifying_at.is_none()
|| now.signed_duration_since(user.last_verifying_at.unwrap()).num_seconds()
> CONFIG.signups_verify_resend_time() as i64
{
let resend_limit = CONFIG.signups_verify_resend_limit() as i32;
if resend_limit == 0 || user.login_verify_count < resend_limit {
// We want to send another email verification if we require signups to verify
@@ -134,10 +129,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
}
// We still want the login to fail until they actually verified the email address
err!(
"Please verify your email before trying again.",
format!("IP: {}. Username: {}.", ip.ip, username)
)
err!("Please verify your email before trying again.", format!("IP: {}. Username: {}.", ip.ip, username))
}
let (mut device, new_device) = get_device(&data, &conn, &user);
@@ -172,7 +164,8 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult
"Kdf": user.client_kdf_type,
"KdfIterations": user.client_kdf_iter,
"ResetMasterPassword": false,// TODO: Same as above
"scope": "api offline_access"
"scope": "api offline_access",
"unofficialServer": true,
});
if let Some(token) = twofactor_token {
@@ -234,9 +227,7 @@ fn twofactor_auth(
None => err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn)?, "2FA token not provided"),
};
let selected_twofactor = twofactors
.into_iter()
.find(|tf| tf.atype == selected_id && tf.enabled);
let selected_twofactor = twofactors.into_iter().find(|tf| tf.atype == selected_id && tf.enabled);
use crate::api::core::two_factor as _tf;
use crate::crypto::ct_eq;
@@ -245,18 +236,26 @@ fn twofactor_auth(
let mut remember = data.two_factor_remember.unwrap_or(0);
match TwoFactorType::from_i32(selected_id) {
Some(TwoFactorType::Authenticator) => _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn)?,
Some(TwoFactorType::Authenticator) => {
_tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn)?
}
Some(TwoFactorType::U2f) => _tf::u2f::validate_u2f_login(user_uuid, twofactor_code, conn)?,
Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?)?,
Some(TwoFactorType::Duo) => _tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn)?,
Some(TwoFactorType::Email) => _tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn)?,
Some(TwoFactorType::Duo) => {
_tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn)?
}
Some(TwoFactorType::Email) => {
_tf::email::validate_email_code_str(user_uuid, twofactor_code, &selected_data?, conn)?
}
Some(TwoFactorType::Remember) => {
match device.twofactor_remember {
Some(ref code) if !CONFIG.disable_2fa_remember() && ct_eq(code, twofactor_code) => {
remember = 1; // Make sure we also return the token here, otherwise it will only remember the first time
}
_ => err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn)?, "2FA Remember token not provided"),
_ => {
err_json!(_json_err_twofactor(&twofactor_ids, user_uuid, conn)?, "2FA Remember token not provided")
}
}
}
_ => err!("Invalid two factor provider"),

View File

@@ -10,8 +10,9 @@ use serde_json::Value;
pub use crate::api::{
admin::routes as admin_routes,
core::purge_sends,
core::purge_trashed_ciphers,
core::routes as core_routes,
core::start_send_deletion_scheduler,
icons::routes as icons_routes,
identity::routes as identity_routes,
notifications::routes as notifications_routes,
@@ -54,9 +55,9 @@ impl NumberOrString {
use std::num::ParseIntError as PIE;
match self {
NumberOrString::Number(n) => Ok(n),
NumberOrString::String(s) => s
.parse()
.map_err(|e: PIE| crate::Error::new("Can't convert to number", e.to_string())),
NumberOrString::String(s) => {
s.parse().map_err(|e: PIE| crate::Error::new("Can't convert to number", e.to_string()))
}
}
}
}

View File

@@ -4,12 +4,7 @@ use rocket::Route;
use rocket_contrib::json::Json;
use serde_json::Value as JsonValue;
use crate::{
api::EmptyResult,
auth::Headers,
db::DbConn,
Error, CONFIG,
};
use crate::{api::EmptyResult, auth::Headers, db::DbConn, Error, CONFIG};
pub fn routes() -> Vec<Route> {
routes![negotiate, websockets_err]
@@ -19,12 +14,16 @@ static SHOW_WEBSOCKETS_MSG: AtomicBool = AtomicBool::new(true);
#[get("/hub")]
fn websockets_err() -> EmptyResult {
if CONFIG.websocket_enabled() && SHOW_WEBSOCKETS_MSG.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
err!("
if CONFIG.websocket_enabled()
&& SHOW_WEBSOCKETS_MSG.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed).is_ok()
{
err!(
"
###########################################################
'/notifications/hub' should be proxied to the websocket server or notifications won't work.
Go to the Wiki for more info, or disable WebSockets setting WEBSOCKET_ENABLED=false.
###########################################################################################\n")
###########################################################################################\n"
)
} else {
Err(Error::empty())
}
@@ -204,9 +203,7 @@ impl Handler for WsHandler {
let handler_insert = self.out.clone();
let handler_update = self.out.clone();
self.users
.map
.upsert(user_uuid, || vec![handler_insert], |ref mut v| v.push(handler_update));
self.users.map.upsert(user_uuid, || vec![handler_insert], |ref mut v| v.push(handler_update));
// Schedule a ping to keep the connection alive
self.out.timeout(PING_MS, PING)
@@ -216,7 +213,11 @@ impl Handler for WsHandler {
if let Message::Text(text) = msg.clone() {
let json = &text[..text.len() - 1]; // Remove last char
if let Ok(InitialMessage { protocol, version }) = from_str::<InitialMessage>(json) {
if let Ok(InitialMessage {
protocol,
version,
}) = from_str::<InitialMessage>(json)
{
if &protocol == "messagepack" && version == 1 {
return self.out.send(&INITIAL_RESPONSE[..]); // Respond to initial message
}
@@ -295,10 +296,7 @@ impl WebSocketUsers {
// NOTE: The last modified date needs to be updated before calling these methods
pub fn send_user_update(&self, ut: UpdateType, user: &User) {
let data = create_update(
vec![
("UserId".into(), user.uuid.clone().into()),
("Date".into(), serialize_date(user.updated_at)),
],
vec![("UserId".into(), user.uuid.clone().into()), ("Date".into(), serialize_date(user.updated_at))],
ut,
);

View File

@@ -83,11 +83,15 @@ fn static_files(filename: String) -> Result<Content<&'static [u8]>, Error> {
"hibp.png" => Ok(Content(ContentType::PNG, include_bytes!("../static/images/hibp.png"))),
"bootstrap.css" => Ok(Content(ContentType::CSS, include_bytes!("../static/scripts/bootstrap.css"))),
"bootstrap-native.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native.js"))),
"bootstrap-native.js" => {
Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native.js")))
}
"identicon.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
"datatables.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))),
"datatables.css" => Ok(Content(ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))),
"jquery-3.5.1.slim.js" => Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.5.1.slim.js"))),
"jquery-3.5.1.slim.js" => {
Ok(Content(ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.5.1.slim.js")))
}
_ => err!(format!("Static file not found: {}", filename)),
}
}

View File

@@ -223,10 +223,9 @@ use crate::db::{
};
pub struct Host {
pub host: String
pub host: String,
}
impl<'a, 'r> FromRequest<'a, 'r> for Host {
type Error = &'static str;
@@ -261,7 +260,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Host {
format!("{}://{}", protocol, host)
};
Outcome::Success(Host { host })
Outcome::Success(Host {
host,
})
}
}
@@ -317,10 +318,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers {
};
if user.security_stamp != claims.sstamp {
if let Some(stamp_exception) = user
.stamp_exception
.as_deref()
.and_then(|s| serde_json::from_str::<UserStampException>(s).ok())
if let Some(stamp_exception) =
user.stamp_exception.as_deref().and_then(|s| serde_json::from_str::<UserStampException>(s).ok())
{
let current_route = match request.route().and_then(|r| r.name) {
Some(name) => name,
@@ -338,7 +337,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for Headers {
}
}
Outcome::Success(Headers { host, device, user })
Outcome::Success(Headers {
host,
device,
user,
})
}
}
@@ -506,7 +509,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for ManagerHeaders {
};
if !headers.org_user.has_full_access() {
match CollectionUser::find_by_collection_and_user(&col_id, &headers.org_user.user_uuid, &conn) {
match CollectionUser::find_by_collection_and_user(
&col_id,
&headers.org_user.user_uuid,
&conn,
) {
Some(_) => (),
None => err_handler!("The current user isn't a manager for this collection"),
}
@@ -636,10 +643,10 @@ impl<'a, 'r> FromRequest<'a, 'r> for ClientIp {
None
};
let ip = ip
.or_else(|| req.remote().map(|r| r.ip()))
.unwrap_or_else(|| "0.0.0.0".parse().unwrap());
let ip = ip.or_else(|| req.remote().map(|r| r.ip())).unwrap_or_else(|| "0.0.0.0".parse().unwrap());
Outcome::Success(ClientIp { ip })
Outcome::Success(ClientIp {
ip,
})
}
}

View File

@@ -316,6 +316,17 @@ make_config! {
/// Websocket port
websocket_port: u16, false, def, 3012;
},
jobs {
/// Job scheduler poll interval |> How often the job scheduler thread checks for jobs to run.
/// Set to 0 to globally disable scheduled jobs.
job_poll_interval_ms: u64, false, def, 30_000;
/// Send purge schedule |> Cron schedule of the job that checks for Sends past their deletion date.
/// Defaults to hourly. Set blank to disable this job.
send_purge_schedule: String, false, def, "0 5 * * * *".to_string();
/// Trash purge schedule |> Cron schedule of the job that checks for trashed items to delete permanently.
/// Defaults to daily. Set blank to disable this job.
trash_purge_schedule: String, false, def, "0 5 0 * * *".to_string();
},
/// General settings
settings {
@@ -339,11 +350,16 @@ make_config! {
/// Per-organization attachment limit (KB) |> Limit in kilobytes for an organization attachments, once the limit is exceeded it won't be possible to upload more
org_attachment_limit: i64, true, option;
/// Trash auto-delete days |> Number of days to wait before auto-deleting a trashed item.
/// If unset, trashed items are not auto-deleted. This setting applies globally, so make
/// sure to inform all users of any changes to this setting.
trash_auto_delete_days: i64, true, option;
/// Disable icon downloads |> Set to true to disable icon downloading, this would still serve icons from
/// $ICON_CACHE_FOLDER, but it won't produce any external network request. Needs to set $ICON_CACHE_TTL to 0,
/// otherwise it will delete them and they won't be downloaded again.
disable_icon_download: bool, true, def, false;
/// Allow new signups |> Controls whether new users can register. Users can be invited by the bitwarden_rs admin even if this is disabled
/// Allow new signups |> Controls whether new users can register. Users can be invited by the vaultwarden admin even if this is disabled
signups_allowed: bool, true, def, true;
/// Require email verification on signups. This will prevent logins from succeeding until the address has been verified
signups_verify: bool, true, def, false;
@@ -369,7 +385,7 @@ make_config! {
admin_token: Pass, true, option;
/// Invitation organization name |> Name shown in the invitation emails that don't come from a specific organization
invitation_org_name: String, true, def, "Bitwarden_RS".to_string();
invitation_org_name: String, true, def, "Vaultwarden".to_string();
},
/// Advanced settings
@@ -418,7 +434,7 @@ make_config! {
/// Log level
log_level: String, false, def, "Info".to_string();
/// Enable DB WAL |> Turning this off might lead to worse performance, but might help if using bitwarden_rs on some exotic filesystems,
/// Enable DB WAL |> Turning this off might lead to worse performance, but might help if using vaultwarden on some exotic filesystems,
/// that do not support WAL. Please make sure you read project wiki on the topic before changing this setting.
enable_db_wal: bool, false, def, true;
@@ -473,7 +489,7 @@ make_config! {
/// From Address
smtp_from: String, true, def, String::new();
/// From Name
smtp_from_name: String, true, def, "Bitwarden_RS".to_string();
smtp_from_name: String, true, def, "Vaultwarden".to_string();
/// Username
smtp_username: String, true, option;
/// Password
@@ -511,10 +527,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
let limit = 256;
if cfg.database_max_conns < 1 || cfg.database_max_conns > limit {
err!(format!(
"`DATABASE_MAX_CONNS` contains an invalid value. Ensure it is between 1 and {}.",
limit,
));
err!(format!("`DATABASE_MAX_CONNS` contains an invalid value. Ensure it is between 1 and {}.", limit,));
}
let dom = cfg.domain.to_lowercase();
@@ -855,9 +868,7 @@ fn case_helper<'reg, 'rc>(
rc: &mut RenderContext<'reg, 'rc>,
out: &mut dyn Output,
) -> HelperResult {
let param = h
.param(0)
.ok_or_else(|| RenderError::new("Param not found for helper \"case\""))?;
let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"case\""))?;
let value = param.value().clone();
if h.params().iter().skip(1).any(|x| x.value() == &value) {
@@ -874,21 +885,15 @@ fn js_escape_helper<'reg, 'rc>(
_rc: &mut RenderContext<'reg, 'rc>,
out: &mut dyn Output,
) -> HelperResult {
let param = h
.param(0)
.ok_or_else(|| RenderError::new("Param not found for helper \"js_escape\""))?;
let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"js_escape\""))?;
let no_quote = h
.param(1)
.is_some();
let no_quote = h.param(1).is_some();
let value = param
.value()
.as_str()
.ok_or_else(|| RenderError::new("Param for helper \"js_escape\" is not a String"))?;
let value =
param.value().as_str().ok_or_else(|| RenderError::new("Param for helper \"js_escape\" is not a String"))?;
let mut escaped_value = value.replace('\\', "").replace('\'', "\\x22").replace('\"', "\\x27");
if ! no_quote {
if !no_quote {
escaped_value = format!("&quot;{}&quot;", escaped_value);
}

View File

@@ -47,9 +47,7 @@ pub fn get_random_64() -> Vec<u8> {
pub fn get_random(mut array: Vec<u8>) -> Vec<u8> {
use ring::rand::{SecureRandom, SystemRandom};
SystemRandom::new()
.fill(&mut array)
.expect("Error generating random values");
SystemRandom::new().fill(&mut array).expect("Error generating random values");
array
}

View File

@@ -1,6 +1,3 @@
use std::process::Command;
use chrono::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use rocket::{
http::Status,
@@ -25,7 +22,6 @@ pub mod __mysql_schema;
#[path = "schemas/postgresql/schema.rs"]
pub mod __postgresql_schema;
// This is used to generate the main DbConn and DbPool enums, which contain one variant for each database supported
macro_rules! generate_connections {
( $( $name:ident: $ty:ty ),+ ) => {
@@ -110,7 +106,6 @@ impl DbConnType {
}
}
#[macro_export]
macro_rules! db_run {
// Same for all dbs
@@ -144,6 +139,7 @@ macro_rules! db_run {
// Different code for each db
( @raw $conn:ident: $( $($db:ident),+ $body:block )+ ) => {
#[allow(unused)] use diesel::prelude::*;
#[allow(unused_variables)]
match $conn {
$($(
#[cfg($db)]
@@ -155,7 +151,6 @@ macro_rules! db_run {
};
}
pub trait FromDb {
type Output;
#[allow(clippy::wrong_self_convention)]
@@ -221,51 +216,34 @@ macro_rules! db_object {
// Reexport the models, needs to be after the macros are defined so it can access them
pub mod models;
/// Creates a back-up of the database using sqlite3
pub fn backup_database() -> Result<(), Error> {
use std::path::Path;
let db_url = CONFIG.database_url();
let db_path = Path::new(&db_url).parent().unwrap();
let now: DateTime<Utc> = Utc::now();
let file_date = now.format("%Y%m%d").to_string();
let backup_command: String = format!("{}{}{}", ".backup 'db_", file_date, ".sqlite3'");
Command::new("sqlite3")
.current_dir(db_path)
.args(&["db.sqlite3", &backup_command])
.output()
.expect("Can't open database, sqlite3 is not available, make sure it's installed and available on the PATH");
Ok(())
}
use diesel::sql_types::Text;
#[derive(QueryableByName,Debug)]
struct SqlVersion {
#[sql_type = "Text"]
version: String,
/// Creates a back-up of the sqlite database
/// MySQL/MariaDB and PostgreSQL are not supported.
pub fn backup_database(conn: &DbConn) -> Result<(), Error> {
db_run! {@raw conn:
postgresql, mysql {
err!("PostgreSQL and MySQL/MariaDB do not support this backup feature");
}
sqlite {
use std::path::Path;
let db_url = CONFIG.database_url();
let db_path = Path::new(&db_url).parent().unwrap().to_string_lossy();
let file_date = chrono::Utc::now().format("%Y%m%d_%H%M%S").to_string();
diesel::sql_query(format!("VACUUM INTO '{}/db_{}.sqlite3'", db_path, file_date)).execute(conn)?;
Ok(())
}
}
}
/// Get the SQL Server version
pub fn get_sql_server_version(conn: &DbConn) -> String {
db_run! {@raw conn:
postgresql, mysql {
match diesel::sql_query("SELECT version() AS version;").get_result::<SqlVersion>(conn).ok() {
Some(v) => {
v.version
},
_ => "Unknown".to_string()
}
no_arg_sql_function!(version, diesel::sql_types::Text);
diesel::select(version).get_result::<String>(conn).unwrap_or_else(|_| "Unknown".to_string())
}
sqlite {
match diesel::sql_query("SELECT sqlite_version() AS version;").get_result::<SqlVersion>(conn).ok() {
Some(v) => {
v.version
},
_ => "Unknown".to_string()
}
no_arg_sql_function!(sqlite_version, diesel::sql_types::Text);
diesel::select(sqlite_version).get_result::<String>(conn).unwrap_or_else(|_| "Unknown".to_string())
}
}
}
@@ -308,8 +286,7 @@ mod sqlite_migrations {
use diesel::{Connection, RunQueryDsl};
// Make sure the database is up to date (create if it doesn't exist, or run the migrations)
let connection =
diesel::sqlite::SqliteConnection::establish(&crate::CONFIG.database_url())?;
let connection = diesel::sqlite::SqliteConnection::establish(&crate::CONFIG.database_url())?;
// Disable Foreign Key Checks during migration
// Scoped to a connection.
@@ -319,9 +296,7 @@ mod sqlite_migrations {
// Turn on WAL in SQLite
if crate::CONFIG.enable_db_wal() {
diesel::sql_query("PRAGMA journal_mode=wal")
.execute(&connection)
.expect("Failed to turn on WAL");
diesel::sql_query("PRAGMA journal_mode=wal").execute(&connection).expect("Failed to turn on WAL");
}
embedded_migrations::run_with_output(&connection, &mut std::io::stdout())?;
@@ -337,8 +312,7 @@ mod mysql_migrations {
pub fn run_migrations() -> Result<(), super::Error> {
use diesel::{Connection, RunQueryDsl};
// Make sure the database is up to date (create if it doesn't exist, or run the migrations)
let connection =
diesel::mysql::MysqlConnection::establish(&crate::CONFIG.database_url())?;
let connection = diesel::mysql::MysqlConnection::establish(&crate::CONFIG.database_url())?;
// Disable Foreign Key Checks during migration
// Scoped to a connection/session.
@@ -359,8 +333,7 @@ mod postgresql_migrations {
pub fn run_migrations() -> Result<(), super::Error> {
use diesel::{Connection, RunQueryDsl};
// Make sure the database is up to date (create if it doesn't exist, or run the migrations)
let connection =
diesel::pg::PgConnection::establish(&crate::CONFIG.database_url())?;
let connection = diesel::pg::PgConnection::establish(&crate::CONFIG.database_url())?;
// Disable Foreign Key Checks during migration
// FIXME: Per https://www.postgresql.org/docs/12/sql-set-constraints.html,

View File

@@ -59,7 +59,6 @@ use crate::error::MapResult;
/// Database methods
impl Attachment {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
db_run! { conn:
sqlite, mysql {

View File

@@ -1,15 +1,10 @@
use chrono::{NaiveDateTime, Utc};
use chrono::{Duration, NaiveDateTime, Utc};
use serde_json::Value;
use crate::CONFIG;
use super::{
Attachment,
CollectionCipher,
Favorite,
FolderCipher,
Organization,
User,
UserOrgStatus,
UserOrgType,
Attachment, CollectionCipher, Favorite, FolderCipher, Organization, User, UserOrgStatus, UserOrgType,
UserOrganization,
};
@@ -91,16 +86,16 @@ impl Cipher {
};
let fields_json = self.fields.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
let password_history_json = self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
let password_history_json =
self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
let (read_only, hide_passwords) =
match self.get_access_restrictions(&user_uuid, conn) {
Some((ro, hp)) => (ro, hp),
None => {
error!("Cipher ownership assertion failure");
(true, true)
},
};
let (read_only, hide_passwords) = match self.get_access_restrictions(&user_uuid, conn) {
Some((ro, hp)) => (ro, hp),
None => {
error!("Cipher ownership assertion failure");
(true, true)
}
};
// Get the type_data or a default to an empty json object '{}'.
// If not passing an empty object, mobile clients will crash.
@@ -130,7 +125,7 @@ impl Cipher {
// There are three types of cipher response models in upstream
// Bitwarden: "cipherMini", "cipher", and "cipherDetails" (in order
// of increasing level of detail). bitwarden_rs currently only
// of increasing level of detail). vaultwarden currently only
// supports the "cipherDetails" type, though it seems like the
// Bitwarden clients will ignore extra fields.
//
@@ -195,12 +190,10 @@ impl Cipher {
None => {
// Belongs to Organization, need to update affected users
if let Some(ref org_uuid) = self.organization_uuid {
UserOrganization::find_by_cipher_and_org(&self.uuid, &org_uuid, conn)
.iter()
.for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
user_uuids.push(user_org.user_uuid.clone())
});
UserOrganization::find_by_cipher_and_org(&self.uuid, &org_uuid, conn).iter().for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
user_uuids.push(user_org.user_uuid.clone())
});
}
}
};
@@ -271,6 +264,17 @@ impl Cipher {
Ok(())
}
/// Purge all ciphers that are old enough to be auto-deleted.
pub fn purge_trash(conn: &DbConn) {
if let Some(auto_delete_days) = CONFIG.trash_auto_delete_days() {
let now = Utc::now().naive_utc();
let dt = now - Duration::days(auto_delete_days);
for cipher in Self::find_deleted_before(&dt, conn) {
cipher.delete(&conn).ok();
}
}
}
pub fn move_to_folder(&self, folder_uuid: Option<String>, user_uuid: &str, conn: &DbConn) -> EmptyResult {
User::update_uuid_revision(user_uuid, conn);
@@ -511,6 +515,15 @@ impl Cipher {
}}
}
/// Find all ciphers that were deleted before the specified datetime.
pub fn find_deleted_before(dt: &NaiveDateTime, conn: &DbConn) -> Vec<Self> {
db_run! {conn: {
ciphers::table
.filter(ciphers::deleted_at.lt(dt))
.load::<CipherDb>(conn).expect("Error loading ciphers").from_db()
}}
}
pub fn get_collections(&self, user_id: &str, conn: &DbConn) -> Vec<String> {
db_run! {conn: {
ciphers_collections::table

View File

@@ -1,6 +1,6 @@
use serde_json::Value;
use super::{Organization, UserOrgStatus, UserOrgType, UserOrganization, User, Cipher};
use super::{Cipher, Organization, User, UserOrgStatus, UserOrgType, UserOrganization};
db_object! {
#[derive(Identifiable, Queryable, Insertable, Associations, AsChangeset)]
@@ -127,11 +127,9 @@ impl Collection {
}
pub fn update_users_revision(&self, conn: &DbConn) {
UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn)
.iter()
.for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
});
UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).iter().for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
});
}
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {
@@ -170,10 +168,7 @@ impl Collection {
}
pub fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &DbConn) -> Vec<Self> {
Self::find_by_user_uuid(user_uuid, conn)
.into_iter()
.filter(|c| c.org_uuid == org_uuid)
.collect()
Self::find_by_user_uuid(user_uuid, conn).into_iter().filter(|c| c.org_uuid == org_uuid).collect()
}
pub fn find_by_organization(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
@@ -284,7 +279,13 @@ impl CollectionUser {
}}
}
pub fn save(user_uuid: &str, collection_uuid: &str, read_only: bool, hide_passwords: bool, conn: &DbConn) -> EmptyResult {
pub fn save(
user_uuid: &str,
collection_uuid: &str,
read_only: bool,
hide_passwords: bool,
conn: &DbConn,
) -> EmptyResult {
User::update_uuid_revision(&user_uuid, conn);
db_run! { conn:
@@ -374,11 +375,9 @@ impl CollectionUser {
}
pub fn delete_all_by_collection(collection_uuid: &str, conn: &DbConn) -> EmptyResult {
CollectionUser::find_by_collection(&collection_uuid, conn)
.iter()
.for_each(|collection| {
User::update_uuid_revision(&collection.user_uuid, conn);
});
CollectionUser::find_by_collection(&collection_uuid, conn).iter().for_each(|collection| {
User::update_uuid_revision(&collection.user_uuid, conn);
});
db_run! { conn: {
diesel::delete(users_collections::table.filter(users_collections::collection_uuid.eq(collection_uuid)))

View File

@@ -20,7 +20,7 @@ use crate::error::MapResult;
impl Favorite {
// Returns whether the specified cipher is a favorite of the specified user.
pub fn is_favorite(cipher_uuid: &str, user_uuid: &str, conn: &DbConn) -> bool {
db_run!{ conn: {
db_run! { conn: {
let query = favorites::table
.filter(favorites::cipher_uuid.eq(cipher_uuid))
.filter(favorites::user_uuid.eq(user_uuid))
@@ -36,19 +36,19 @@ impl Favorite {
match (old, new) {
(false, true) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
diesel::insert_into(favorites::table)
.values((
favorites::user_uuid.eq(user_uuid),
favorites::cipher_uuid.eq(cipher_uuid),
))
.execute(conn)
.map_res("Error adding favorite")
}}
db_run! { conn: {
diesel::insert_into(favorites::table)
.values((
favorites::user_uuid.eq(user_uuid),
favorites::cipher_uuid.eq(cipher_uuid),
))
.execute(conn)
.map_res("Error adding favorite")
}}
}
(true, false) => {
User::update_uuid_revision(user_uuid, &conn);
db_run!{ conn: {
db_run! { conn: {
diesel::delete(
favorites::table
.filter(favorites::user_uuid.eq(user_uuid))
@@ -59,7 +59,7 @@ impl Favorite {
}}
}
// Otherwise, the favorite status is already what it should be.
_ => Ok(())
_ => Ok(()),
}
}

View File

@@ -109,7 +109,6 @@ impl Folder {
User::update_uuid_revision(&self.user_uuid, conn);
FolderCipher::delete_all_by_folder(&self.uuid, &conn)?;
db_run! { conn: {
diesel::delete(folders::table.filter(folders::uuid.eq(&self.uuid)))
.execute(conn)

View File

@@ -6,9 +6,9 @@ mod favorite;
mod folder;
mod org_policy;
mod organization;
mod send;
mod two_factor;
mod user;
mod send;
pub use self::attachment::Attachment;
pub use self::cipher::Cipher;
@@ -18,6 +18,6 @@ pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher};
pub use self::org_policy::{OrgPolicy, OrgPolicyType};
pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::user::{Invitation, User, UserStampException};
pub use self::send::{Send, SendType};

View File

@@ -4,7 +4,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
use super::{Organization, UserOrganization, UserOrgStatus, UserOrgType};
use super::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
db_object! {
#[derive(Identifiable, Queryable, Insertable, Associations, AsChangeset)]
@@ -20,8 +20,7 @@ db_object! {
}
}
#[derive(Copy, Clone)]
#[derive(num_derive::FromPrimitive)]
#[derive(Copy, Clone, num_derive::FromPrimitive)]
pub enum OrgPolicyType {
TwoFactorAuthentication = 0,
MasterPassword = 1,
@@ -175,7 +174,8 @@ impl OrgPolicy {
/// and the user is not an owner or admin of that org. This is only useful for checking
/// applicability of policy types that have these particular semantics.
pub fn is_applicable_to_user(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> bool {
for policy in OrgPolicy::find_by_user(user_uuid, conn) { // Returns confirmed users only.
// Returns confirmed users only.
for policy in OrgPolicy::find_by_user(user_uuid, conn) {
if policy.enabled && policy.has_type(policy_type) {
let org_uuid = &policy.org_uuid;
if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {

View File

@@ -1,8 +1,8 @@
use num_traits::FromPrimitive;
use serde_json::Value;
use std::cmp::Ordering;
use num_traits::FromPrimitive;
use super::{CollectionUser, User, OrgPolicy};
use super::{CollectionUser, OrgPolicy, User};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@@ -35,8 +35,7 @@ pub enum UserOrgStatus {
Confirmed = 2,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(num_derive::FromPrimitive)]
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
pub enum UserOrgType {
Owner = 0,
Admin = 1,
@@ -190,11 +189,9 @@ use crate::error::MapResult;
/// Database methods
impl Organization {
pub fn save(&self, conn: &DbConn) -> EmptyResult {
UserOrganization::find_by_org(&self.uuid, conn)
.iter()
.for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
});
UserOrganization::find_by_org(&self.uuid, conn).iter().for_each(|user_org| {
User::update_uuid_revision(&user_org.user_uuid, conn);
});
db_run! { conn:
sqlite, mysql {
@@ -236,7 +233,6 @@ impl Organization {
UserOrganization::delete_all_by_organization(&self.uuid, &conn)?;
OrgPolicy::delete_all_by_organization(&self.uuid, &conn)?;
db_run! { conn: {
diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid)))
.execute(conn)
@@ -347,11 +343,13 @@ impl UserOrganization {
let collections = CollectionUser::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn);
collections
.iter()
.map(|c| json!({
"Id": c.collection_uuid,
"ReadOnly": c.read_only,
"HidePasswords": c.hide_passwords,
}))
.map(|c| {
json!({
"Id": c.collection_uuid,
"ReadOnly": c.read_only,
"HidePasswords": c.hide_passwords,
})
})
.collect()
};
@@ -446,8 +444,7 @@ impl UserOrganization {
}
pub fn has_full_access(&self) -> bool {
(self.access_all || self.atype >= UserOrgType::Admin) &&
self.has_status(UserOrgStatus::Confirmed)
(self.access_all || self.atype >= UserOrgType::Admin) && self.has_status(UserOrgStatus::Confirmed)
}
pub fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> {

View File

@@ -205,6 +205,13 @@ impl Send {
}}
}
/// Purge all sends that are past their deletion date.
pub fn purge(conn: &DbConn) {
for send in Self::find_by_past_deletion_date(&conn) {
send.delete(&conn).ok();
}
}
pub fn update_users_revision(&self, conn: &DbConn) {
match &self.user_uuid {
Some(user_uuid) => {
@@ -223,12 +230,6 @@ impl Send {
Ok(())
}
pub fn find_all(conn: &DbConn) -> Vec<Self> {
db_run! {conn: {
sends::table.load::<SendDb>(conn).expect("Error loading sends").from_db()
}}
}
pub fn find_by_access_id(access_id: &str, conn: &DbConn) -> Option<Self> {
use data_encoding::BASE64URL_NOPAD;
use uuid::Uuid;
@@ -271,4 +272,13 @@ impl Send {
.load::<SendDb>(conn).expect("Error loading sends").from_db()
}}
}
pub fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> {
let now = Utc::now().naive_utc();
db_run! {conn: {
sends::table
.filter(sends::deletion_date.lt(now))
.load::<SendDb>(conn).expect("Error loading sends").from_db()
}}
}
}

View File

@@ -63,8 +63,8 @@ enum UserStatus {
#[derive(Serialize, Deserialize)]
pub struct UserStampException {
pub route: String,
pub security_stamp: String
pub route: String,
pub security_stamp: String,
}
/// Local methods
@@ -162,7 +162,7 @@ impl User {
pub fn set_stamp_exception(&mut self, route_exception: &str) {
let stamp_exception = UserStampException {
route: route_exception.to_string(),
security_stamp: self.security_stamp.to_string()
security_stamp: self.security_stamp.to_string(),
};
self.stamp_exception = Some(serde_json::to_string(&stamp_exception).unwrap_or_default());
}
@@ -341,14 +341,16 @@ impl User {
pub fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> {
match Device::find_latest_active_by_user(&self.uuid, conn) {
Some(device) => Some(device.updated_at),
None => None
None => None,
}
}
}
impl Invitation {
pub const fn new(email: String) -> Self {
Self { email }
Self {
email,
}
}
pub fn save(&self, conn: &DbConn) -> EmptyResult {

View File

@@ -33,10 +33,10 @@ macro_rules! make_error {
};
}
use diesel::r2d2::PoolError as R2d2Err;
use diesel::result::Error as DieselErr;
use diesel::ConnectionError as DieselConErr;
use diesel_migrations::RunMigrationsError as DieselMigErr;
use diesel::r2d2::PoolError as R2d2Err;
use handlebars::RenderError as HbErr;
use jsonwebtoken::errors::Error as JwtErr;
use regex::Error as RegexErr;
@@ -191,18 +191,14 @@ use rocket::response::{self, Responder, Response};
impl<'r> Responder<'r> for Error {
fn respond_to(self, _: &Request) -> response::Result<'r> {
match self.error {
ErrorKind::EmptyError(_) => {} // Don't print the error in this situation
ErrorKind::EmptyError(_) => {} // Don't print the error in this situation
ErrorKind::SimpleError(_) => {} // Don't print the error in this situation
_ => error!(target: "error", "{:#?}", self),
};
let code = Status::from_code(self.error_code).unwrap_or(Status::BadRequest);
Response::build()
.status(code)
.header(ContentType::JSON)
.sized_body(Cursor::new(format!("{}", self)))
.ok()
Response::build().status(code).header(ContentType::JSON).sized_body(Cursor::new(format!("{}", self))).ok()
}
}

View File

@@ -1,4 +1,4 @@
use std::{str::FromStr};
use std::str::FromStr;
use chrono::{DateTime, Local};
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
@@ -62,11 +62,13 @@ fn mailer() -> SmtpTransport {
let mut selected_mechanisms = vec![];
for wanted_mechanism in mechanism.split(',') {
for m in &allowed_mechanisms {
if m.to_string().to_lowercase() == wanted_mechanism.trim_matches(|c| c == '"' || c == '\'' || c == ' ').to_lowercase() {
if m.to_string().to_lowercase()
== wanted_mechanism.trim_matches(|c| c == '"' || c == '\'' || c == ' ').to_lowercase()
{
selected_mechanisms.push(*m);
}
}
};
}
if !selected_mechanisms.is_empty() {
smtp_client.authentication(selected_mechanisms)
@@ -316,31 +318,23 @@ fn send_email(address: &str, subject: &str, body_html: String, body_text: String
let smtp_from = &CONFIG.smtp_from();
let email = Message::builder()
.message_id(Some(format!("<{}@{}>", crate::util::get_uuid(), smtp_from.split('@').collect::<Vec<&str>>()[1] )))
.message_id(Some(format!("<{}@{}>", crate::util::get_uuid(), smtp_from.split('@').collect::<Vec<&str>>()[1])))
.to(Mailbox::new(None, Address::from_str(&address)?))
.from(Mailbox::new(
Some(CONFIG.smtp_from_name()),
Address::from_str(smtp_from)?,
))
.from(Mailbox::new(Some(CONFIG.smtp_from_name()), Address::from_str(smtp_from)?))
.subject(subject)
.multipart(
MultiPart::alternative()
.singlepart(text)
.singlepart(html)
)?;
.multipart(MultiPart::alternative().singlepart(text).singlepart(html))?;
match mailer().send(&email) {
Ok(_) => Ok(()),
// Match some common errors and make them more user friendly
Err(e) => {
if e.is_client() {
err!(format!("SMTP Client error: {}", e));
} else if e.is_transient() {
err!(format!("SMTP 4xx error: {:?}", e));
} else if e.is_permanent() {
err!(format!("SMTP 5xx error: {:?}", e));
} else if e.is_timeout() {
} else if e.is_timeout() {
err!(format!("SMTP timeout error: {:?}", e));
} else {
Err(e.into())

View File

@@ -16,6 +16,7 @@ extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
use job_scheduler::{Job, JobScheduler};
use std::{
fs::create_dir_all,
panic,
@@ -23,6 +24,7 @@ use std::{
process::{exit, Command},
str::FromStr,
thread,
time::Duration,
};
#[macro_use]
@@ -56,14 +58,16 @@ fn main() {
create_icon_cache_folder();
launch_rocket(extra_debug);
let pool = create_db_pool();
schedule_jobs(pool.clone());
launch_rocket(pool, extra_debug); // Blocks until program termination.
}
const HELP: &str = "\
A Bitwarden API server written in Rust
Alternative implementation of the Bitwarden server API written in Rust
USAGE:
bitwarden_rs
vaultwarden
FLAGS:
-h, --help Prints help information
@@ -75,18 +79,18 @@ fn parse_args() {
let mut pargs = pico_args::Arguments::from_env();
if pargs.contains(["-h", "--help"]) {
println!("bitwarden_rs {}", option_env!("BWRS_VERSION").unwrap_or(NO_VERSION));
println!("vaultwarden {}", option_env!("BWRS_VERSION").unwrap_or(NO_VERSION));
print!("{}", HELP);
exit(0);
} else if pargs.contains(["-v", "--version"]) {
println!("bitwarden_rs {}", option_env!("BWRS_VERSION").unwrap_or(NO_VERSION));
println!("vaultwarden {}", option_env!("BWRS_VERSION").unwrap_or(NO_VERSION));
exit(0);
}
}
fn launch_info() {
println!("/--------------------------------------------------------------------\\");
println!("| Starting Bitwarden_RS |");
println!("| Starting Vaultwarden |");
if let Some(version) = option_env!("BWRS_VERSION") {
println!("|{:^68}|", format!("Version {}", version));
@@ -98,7 +102,7 @@ fn launch_info() {
println!("| Send usage/configuration questions or feature requests to: |");
println!("| https://bitwardenrs.discourse.group/ |");
println!("| Report suspected bugs/issues in the software itself at: |");
println!("| https://github.com/dani-garcia/bitwarden_rs/issues/new |");
println!("| https://github.com/dani-garcia/vaultwarden/issues/new |");
println!("\\--------------------------------------------------------------------/\n");
}
@@ -123,7 +127,9 @@ fn init_logging(level: log::LevelFilter) -> Result<(), fern::InitError> {
// Enable smtp debug logging only specifically for smtp when need.
// This can contain sensitive information we do not want in the default debug/trace logging.
if CONFIG.smtp_debug() {
println!("[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!");
println!(
"[WARNING] SMTP Debugging is enabled (SMTP_DEBUG=true). Sensitive information could be disclosed via logs!"
);
println!("[WARNING] Only enable SMTP_DEBUG during troubleshooting!\n");
logger = logger.level_for("lettre::transport::smtp", log::LevelFilter::Debug)
} else {
@@ -201,7 +207,7 @@ fn chain_syslog(logger: fern::Dispatch) -> fern::Dispatch {
let syslog_fmt = syslog::Formatter3164 {
facility: syslog::Facility::LOG_USER,
hostname: None,
process: "bitwarden_rs".into(),
process: "vaultwarden".into(),
pid: 0,
};
@@ -294,24 +300,27 @@ fn check_web_vault() {
let index_path = Path::new(&CONFIG.web_vault_folder()).join("index.html");
if !index_path.exists() {
error!("Web vault is not found at '{}'. To install it, please follow the steps in: ", CONFIG.web_vault_folder());
error!("https://github.com/dani-garcia/bitwarden_rs/wiki/Building-binary#install-the-web-vault");
error!(
"Web vault is not found at '{}'. To install it, please follow the steps in: ",
CONFIG.web_vault_folder()
);
error!("https://github.com/dani-garcia/vaultwarden/wiki/Building-binary#install-the-web-vault");
error!("You can also set the environment variable 'WEB_VAULT_ENABLED=false' to disable it");
exit(1);
}
}
fn launch_rocket(extra_debug: bool) {
let pool = match util::retry_db(db::DbPool::from_config, CONFIG.db_connection_retries()) {
fn create_db_pool() -> db::DbPool {
match util::retry_db(db::DbPool::from_config, CONFIG.db_connection_retries()) {
Ok(p) => p,
Err(e) => {
error!("Error creating database pool: {:?}", e);
exit(1);
}
};
api::start_send_deletion_scheduler(pool.clone());
}
}
fn launch_rocket(pool: db::DbPool, extra_debug: bool) {
let basepath = &CONFIG.domain_path();
// If adding more paths here, consider also adding them to
@@ -334,3 +343,40 @@ fn launch_rocket(extra_debug: bool) {
// The launch will restore the original logging level
error!("Launch error {:#?}", result);
}
fn schedule_jobs(pool: db::DbPool) {
if CONFIG.job_poll_interval_ms() == 0 {
info!("Job scheduler disabled.");
return;
}
thread::Builder::new()
.name("job-scheduler".to_string())
.spawn(move || {
let mut sched = JobScheduler::new();
// Purge sends that are past their deletion date.
if !CONFIG.send_purge_schedule().is_empty() {
sched.add(Job::new(CONFIG.send_purge_schedule().parse().unwrap(), || {
api::purge_sends(pool.clone());
}));
}
// Purge trashed items that are old enough to be auto-deleted.
if !CONFIG.trash_purge_schedule().is_empty() {
sched.add(Job::new(CONFIG.trash_purge_schedule().parse().unwrap(), || {
api::purge_trashed_ciphers(pool.clone());
}));
}
// Periodically check for jobs to run. We probably won't need any
// jobs that run more often than once a minute, so a default poll
// interval of 30 seconds should be sufficient. Users who want to
// schedule jobs to run more frequently for some reason can reduce
// the poll interval accordingly.
loop {
sched.tick();
thread::sleep(Duration::from_millis(CONFIG.job_poll_interval_ms()));
}
})
.expect("Error spawning job scheduler thread");
}

View File

@@ -772,7 +772,8 @@
"stackoverflow.com",
"serverfault.com",
"mathoverflow.net",
"askubuntu.com"
"askubuntu.com",
"stackapps.com"
],
"Excluded": false
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="robots" content="noindex,nofollow" />
<link rel="icon" type="image/png" href="{{urlpath}}/bwrs_static/shield-white.png">
<title>Bitwarden_rs Admin Panel</title>
<title>Vaultwarden Admin Panel</title>
<link rel="stylesheet" href="{{urlpath}}/bwrs_static/bootstrap.css" />
<style>
body {
@@ -83,7 +83,7 @@
<body class="bg-light">
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4 shadow fixed-top">
<div class="container-xl">
<a class="navbar-brand" href="{{urlpath}}/admin"><img class="pr-1" src="{{urlpath}}/bwrs_static/shield-white.png">Bitwarden_rs Admin</a>
<a class="navbar-brand" href="{{urlpath}}/admin"><img class="pr-1" src="{{urlpath}}/bwrs_static/shield-white.png">Vaultwarden Admin</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse"
aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>

View File

@@ -20,6 +20,7 @@
<dd class="col-sm-7">
<span id="server-latest">{{diagnostics.latest_release}}<span id="server-latest-commit" class="d-none">-{{diagnostics.latest_commit}}</span></span>
</dd>
{{#if diagnostics.web_vault_enabled}}
<dt class="col-sm-5">Web Installed
<span class="badge badge-success d-none" id="web-success" title="Latest version is installed.">Ok</span>
<span class="badge badge-warning d-none" id="web-warning" title="There seems to be an update available.">Update</span>
@@ -35,6 +36,13 @@
<span id="web-latest">{{diagnostics.latest_web_build}}</span>
</dd>
{{/unless}}
{{/if}}
{{#unless diagnostics.web_vault_enabled}}
<dt class="col-sm-5">Web Installed</dt>
<dd class="col-sm-7">
<span id="web-installed">Web Vault is disabled</span>
</dd>
{{/unless}}
<dt class="col-sm-5">Database</dt>
<dd class="col-sm-7">
<span><b>{{diagnostics.db_type}}:</b> {{diagnostics.db_version}}</span>
@@ -118,7 +126,10 @@
<dd class="col-sm-7">
<span id="dns-resolved">{{diagnostics.dns_resolved}}</span>
</dd>
<dt class="col-sm-5">Date & Time (Local)</dt>
<dd class="col-sm-7">
<span><b>Server:</b> {{diagnostics.server_time_local}}</span>
</dd>
<dt class="col-sm-5">Date & Time (UTC)
<span class="badge badge-success d-none" id="time-success" title="Time offsets seem to be correct.">Ok</span>
<span class="badge badge-danger d-none" id="time-warning" title="Time offsets are too mouch at drift.">Error</span>
@@ -148,8 +159,8 @@
<dl class="row">
<dd class="col-sm-12">
If you need support please check the following links first before you create a new issue:
<a href="https://bitwardenrs.discourse.group/" target="_blank" rel="noreferrer">Bitwarden_RS Forum</a>
| <a href="https://github.com/dani-garcia/bitwarden_rs/discussions" target="_blank" rel="noreferrer">Github Discussions</a>
<a href="https://bitwardenrs.discourse.group/" target="_blank" rel="noreferrer">Vaultwarden Forum</a>
| <a href="https://github.com/dani-garcia/vaultwarden/discussions" target="_blank" rel="noreferrer">Github Discussions</a>
</dd>
</dl>
<dl class="row">
@@ -211,7 +222,7 @@
}
// ================================
// Version check for both bitwarden_rs and web-vault
// Version check for both vaultwarden and web-vault
let serverInstalled = document.getElementById('server-installed').innerText;
let serverLatest = document.getElementById('server-latest').innerText;
let serverLatestCommit = document.getElementById('server-latest-commit').innerText.replace('-', '');
@@ -294,7 +305,7 @@
async function generateSupportString() {
supportString = "### Your environment (Generated via diagnostics page)\n";
supportString += "* Bitwarden_rs version: v{{ version }}\n";
supportString += "* Vaultwarden version: v{{ version }}\n";
supportString += "* Web-vault version: v{{ diagnostics.web_vault_version }}\n";
supportString += "* Running within Docker: {{ diagnostics.running_within_docker }}\n";
supportString += "* Uses a reverse proxy: {{ diagnostics.ip_header_exists }}\n";

View File

@@ -116,7 +116,11 @@
data-target="#g_database">Backup Database</button></div>
<div id="g_database" class="card-body collapse" data-parent="#config-form">
<div class="small mb-3">
NOTE: A local installation of sqlite3 is required for this section to work.
WARNING: This function only creates a backup copy of the SQLite database.
This does not include any configuration or file attachment data that may
also be needed to fully restore a vaultwarden instance. For details on
how to perform complete backups, refer to the wiki page on
<a href="https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault">backups</a>.
</div>
<button type="button" class="btn btn-primary" onclick="backupDatabase();">Backup Database</button>
</div>

View File

@@ -5,4 +5,4 @@ To finalize changing your email address enter the following code in web vault: {
If you did not try to change an email address, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Your Email Change
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -113,7 +113,7 @@ Your Email Change
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -7,4 +7,4 @@ Delete Your Account: {{url}}/#/verify-recover-delete?userId={{user_id}}&token={{
If you did not request this email to delete your account, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Delete Your Account
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -121,7 +121,7 @@ Delete Your Account
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -1,7 +1,7 @@
Invitation to {{{org_name}}} accepted
<!---------------->
Your invitation for *{{email}}* to join *{{org_name}}* was accepted.
Please log in via {{url}} to the bitwarden_rs server and confirm them from the organization management page.
Please log in via {{url}} to the vaultwarden server and confirm them from the organization management page.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Invitation to {{{org_name}}} accepted
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -101,7 +101,7 @@ Invitation to {{{org_name}}} accepted
</tr>
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
Please <a href="{{url}}/">log in</a> to the bitwarden_rs server and confirm them from the organization management page.
Please <a href="{{url}}/">log in</a> to the vaultwarden server and confirm them from the organization management page.
</td>
</tr>
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
@@ -118,7 +118,7 @@ Invitation to {{{org_name}}} accepted
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -4,4 +4,4 @@ Your invitation to join *{{org_name}}* was confirmed.
It will now appear under the Organizations the next time you log in to the web vault at {{url}}.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Invitation to {{{org_name}}} confirmed
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -114,7 +114,7 @@ Invitation to {{{org_name}}} confirmed
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -9,4 +9,4 @@ Your account was just logged into from a new device.
You can deauthorize all devices that have access to your account from the web vault ( {{url}} ) under Settings > My Account > Deauthorize Sessions.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ New Device Logged In From {{{device}}}
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -128,7 +128,7 @@ New Device Logged In From {{{device}}}
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -7,4 +7,4 @@ If you cannot remember your master password, there is no way to recover your dat
If you did not request your master password hint you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Sorry, you have no password hint...
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -118,7 +118,7 @@ Sorry, you have no password hint...
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -10,4 +10,4 @@ If you cannot remember your master password, there is no way to recover your dat
If you did not request your master password hint you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Your master password hint
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -124,7 +124,7 @@ Your master password hint
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -9,4 +9,4 @@ Click here to join: {{url}}/#/accept-organization/?organizationId={{org_id}}&org
If you do not wish to join this organization, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Join {{{org_name}}}
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -121,7 +121,7 @@ Join {{{org_name}}}
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -1,8 +1,8 @@
Bitwarden_rs SMTP Test
Vaultwarden SMTP Test
<!---------------->
This is a test email to verify the SMTP configuration for {{url}}.
When you can read this email it is probably configured correctly.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -1,10 +1,10 @@
Bitwarden_rs SMTP Test
Vaultwarden SMTP Test
<!---------------->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -113,7 +113,7 @@ Bitwarden_rs SMTP Test
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -5,4 +5,4 @@ Your two-step verification code is: {{token}}
Use this code to complete logging in with Bitwarden.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Your Two-step Login Verification Code
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -113,7 +113,7 @@ Your Two-step Login Verification Code
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -7,4 +7,4 @@ Verify Email Address Now: {{url}}/#/verify-email/?userId={{user_id}}&token={{tok
If you did not request to verify your account, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Verify Your Email
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -121,7 +121,7 @@ Verify Your Email
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -5,4 +5,4 @@ Thank you for creating an account at {{url}}. You may now log in with your new a
If you did not request to create an account, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Welcome
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -113,7 +113,7 @@ Welcome
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -7,4 +7,4 @@ Verify Email Address Now: {{url}}/#/verify-email/?userId={{user_id}}&token={{tok
If you did not request to create an account, you can safely ignore this email.
===
Github: https://github.com/dani-garcia/bitwarden_rs
Github: https://github.com/dani-garcia/vaultwarden

View File

@@ -4,7 +4,7 @@ Welcome
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Bitwarden_rs</title>
<title>Vaultwarden</title>
</head>
<body style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; height: 100%; line-height: 25px; width: 100% !important;" bgcolor="#f6f6f6">
<style type="text/css">
@@ -121,7 +121,7 @@ Welcome
<td class="aligncenter social-icons" align="center" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 15px 0 0 0;" valign="top">
<table cellpadding="0" cellspacing="0" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0 auto;">
<tr style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0;">
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/bitwarden_rs" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
<td style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; padding: 0 10px;" valign="top"><a href="https://github.com/dani-garcia/vaultwarden" target="_blank" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 20px; margin: 0; text-decoration: underline;"><img src="{{url}}/bwrs_static/mail-github.png" alt="GitHub" width="30" height="30" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; border: none; box-sizing: border-box; color: #333; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; max-width: 100%;" /></a></td>
</tr>
</table>
</td>

View File

@@ -28,7 +28,10 @@ impl Fairing for AppHeaders {
res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
res.set_raw_header("X-Content-Type-Options", "nosniff");
res.set_raw_header("X-XSS-Protection", "1; mode=block");
let csp = format!("frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb moz-extension://* {};", CONFIG.allowed_iframe_ancestors());
let csp = format!(
"frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb moz-extension://* {};",
CONFIG.allowed_iframe_ancestors()
);
res.set_raw_header("Content-Security-Policy", csp);
// Disable cache unless otherwise specified
@@ -124,14 +127,8 @@ impl<'r, R: Responder<'r>> Responder<'r> for Cached<R> {
// Log all the routes from the main paths list, and the attachments endpoint
// Effectively ignores, any static file route, and the alive endpoint
const LOGGED_ROUTES: [&str; 6] = [
"/api",
"/admin",
"/identity",
"/icons",
"/notifications/hub/negotiate",
"/attachments",
];
const LOGGED_ROUTES: [&str; 6] =
["/api", "/admin", "/identity", "/icons", "/notifications/hub/negotiate", "/attachments"];
// Boolean is extra debug, when true, we ignore the whitelist above and also print the mounts
pub struct BetterLogging(pub bool);
@@ -158,7 +155,11 @@ impl Fairing for BetterLogging {
}
let config = rocket.config();
let scheme = if config.tls_enabled() { "https" } else { "http" };
let scheme = if config.tls_enabled() {
"https"
} else {
"http"
};
let addr = format!("{}://{}:{}", &scheme, &config.address, &config.port);
info!(target: "start", "Rocket has launched from {}", addr);
}
@@ -293,8 +294,7 @@ where
use std::env;
pub fn get_env_str_value(key: &str) -> Option<String>
{
pub fn get_env_str_value(key: &str) -> Option<String> {
let key_file = format!("{}_FILE", key);
let value_from_env = env::var(key);
let value_file = env::var(&key_file);
@@ -304,9 +304,9 @@ pub fn get_env_str_value(key: &str) -> Option<String>
(Ok(v_env), Err(_)) => Some(v_env),
(Err(_), Ok(v_file)) => match fs::read_to_string(v_file) {
Ok(content) => Some(content.trim().to_string()),
Err(e) => panic!("Failed to load {}: {:?}", key, e)
Err(e) => panic!("Failed to load {}: {:?}", key, e),
},
_ => None
_ => None,
}
}
@@ -478,7 +478,6 @@ pub fn retry<F, T, E>(func: F, max_tries: u32) -> Result<T, E>
where
F: Fn() -> Result<T, E>,
{
use std::{thread::sleep, time::Duration};
let mut tries = 0;
loop {
@@ -497,12 +496,13 @@ where
}
}
use std::{thread::sleep, time::Duration};
pub fn retry_db<F, T, E>(func: F, max_tries: u32) -> Result<T, E>
where
F: Fn() -> Result<T, E>,
E: std::error::Error,
{
use std::{thread::sleep, time::Duration};
let mut tries = 0;
loop {
@@ -522,3 +522,18 @@ where
}
}
}
use reqwest::{
blocking::{Client, ClientBuilder},
header,
};
pub fn get_reqwest_client() -> Client {
get_reqwest_client_builder().build().expect("Failed to build client")
}
pub fn get_reqwest_client_builder() -> ClientBuilder {
let mut headers = header::HeaderMap::new();
headers.insert(header::USER_AGENT, header::HeaderValue::from_static("Vaultwarden"));
Client::builder().default_headers(headers).timeout(Duration::from_secs(10))
}